99import java .nio .file .Path ;
1010import java .nio .file .Paths ;
1111import java .util .stream .Stream ;
12- import java .util .zip .ZipEntry ;
13- import java .util .zip .ZipInputStream ;
14- import java .util .zip .ZipOutputStream ;
12+
13+ import org .apache .commons .compress .archivers .ArchiveEntry ;
14+ import org .apache .commons .compress .archivers .zip .ZipArchiveEntry ;
15+ import org .apache .commons .compress .archivers .zip .ZipArchiveInputStream ;
16+ import org .apache .commons .compress .archivers .zip .ZipArchiveOutputStream ;
17+ import org .apache .commons .compress .archivers .zip .Zip64Mode ;
18+
19+ import java .util .zip .CRC32 ;
20+ import java .util .zip .ZipEntry ; // for STORED constant
1521
1622
1723/** A Store implementation that buffers reads and writes and flushes them to an underlying Store as a zip file.
@@ -20,11 +26,19 @@ public class BufferedZipStore implements Store, Store.ListableStore {
2026
2127 private final StoreHandle underlyingStore ;
2228 private final Store .ListableStore bufferStore ;
29+ private final String archiveComment ;
2330
2431 private void writeBuffer () throws IOException {
2532 // create zip file bytes from buffer store and write to underlying store
2633 ByteArrayOutputStream baos = new ByteArrayOutputStream ();
27- try (ZipOutputStream zos = new ZipOutputStream (baos )) {
34+ try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream (baos )) {
35+ // always use zip64
36+ zos .setUseZip64 (Zip64Mode .Always );
37+ // set archive comment if provided
38+ if (archiveComment != null ) {
39+ zos .setComment (archiveComment );
40+ }
41+
2842 // iterate all entries provided by bufferStore.list()
2943 bufferStore .list ().forEach (keys -> {
3044 try {
@@ -39,17 +53,30 @@ private void writeBuffer() throws IOException{
3953 if (!entryName .endsWith ("/" )) {
4054 entryName = entryName + "/" ;
4155 }
42- zos .putNextEntry (new ZipEntry (entryName ));
43- zos .closeEntry ();
56+ ZipArchiveEntry dirEntry = new ZipArchiveEntry (entryName );
57+ dirEntry .setMethod (ZipEntry .STORED );
58+ dirEntry .setSize (0 );
59+ dirEntry .setCrc (0 );
60+ zos .putArchiveEntry (dirEntry );
61+ zos .closeArchiveEntry ();
4462 } else {
4563 // read bytes from ByteBuffer without modifying original
4664 ByteBuffer dup = bb .duplicate ();
4765 int len = dup .remaining ();
4866 byte [] bytes = new byte [len ];
4967 dup .get (bytes );
50- zos .putNextEntry (new ZipEntry (entryName ));
68+
69+ // compute CRC and set size for STORED (no compression)
70+ CRC32 crc = new CRC32 ();
71+ crc .update (bytes , 0 , bytes .length );
72+ ZipArchiveEntry fileEntry = new ZipArchiveEntry (entryName );
73+ fileEntry .setMethod (ZipEntry .STORED );
74+ fileEntry .setSize (bytes .length );
75+ fileEntry .setCrc (crc .getValue ());
76+
77+ zos .putArchiveEntry (fileEntry );
5178 zos .write (bytes );
52- zos .closeEntry ();
79+ zos .closeArchiveEntry ();
5380 }
5481 } catch (IOException e ) {
5582 // wrap checked exception so it can be rethrown from stream for handling below
@@ -76,11 +103,12 @@ private void loadBuffer() throws IOException{
76103 if (buffer == null ) {
77104 return ;
78105 }
79- try (ZipInputStream zis = new ZipInputStream (new ByteBufferBackedInputStream (buffer ))) {
80- ZipEntry entry ;
81- while ((entry = zis .getNextEntry ()) != null ) {
106+ try (ZipArchiveInputStream zis = new ZipArchiveInputStream (new ByteBufferBackedInputStream (buffer ))) {
107+ // this.archiveComment = zis.getComment();
108+ ArchiveEntry aentry ;
109+ while ((aentry = zis .getNextEntry ()) != null ) {
110+ ZipArchiveEntry entry = (ZipArchiveEntry ) aentry ;
82111 if (entry .isDirectory ()) {
83- zis .closeEntry ();
84112 continue ;
85113 }
86114 ByteArrayOutputStream baos = new ByteArrayOutputStream ();
@@ -89,49 +117,63 @@ private void loadBuffer() throws IOException{
89117 while ((read = zis .read (tmp )) != -1 ) {
90118 baos .write (tmp , 0 , read );
91119 }
92-
93120 byte [] bytes = baos .toByteArray ();
94- System .out .println ("Loading entry: " + entry .getName () + " (" + bytes .length + " bytes)" );
95-
96121 bufferStore .set (new String []{entry .getName ()}, ByteBuffer .wrap (bytes ));
97-
98- zis .closeEntry ();
99122 }
100123 }
101124
102125 }
103126
104- public BufferedZipStore (@ Nonnull StoreHandle underlyingStore , @ Nonnull Store .ListableStore bufferStore ) {
127+ public BufferedZipStore (@ Nonnull StoreHandle underlyingStore , @ Nonnull Store .ListableStore bufferStore , @ Nullable String archiveComment ) {
105128 this .underlyingStore = underlyingStore ;
106129 this .bufferStore = bufferStore ;
130+ this .archiveComment = archiveComment ;
107131 try {
108132 loadBuffer ();
109133 } catch (IOException e ) {
110134 throw new RuntimeException ("Failed to load buffer from underlying store" , e );
111135 }
112136 }
113137
138+ public BufferedZipStore (@ Nonnull StoreHandle underlyingStore , @ Nonnull Store .ListableStore bufferStore ) {
139+ this (underlyingStore , bufferStore , null );
140+ }
141+
142+ public BufferedZipStore (@ Nonnull StoreHandle underlyingStore , String archiveComment ) {
143+ this (underlyingStore , new MemoryStore (), archiveComment );
144+ }
145+
114146 public BufferedZipStore (@ Nonnull StoreHandle underlyingStore ) {
115- this (underlyingStore , new MemoryStore ());
147+ this (underlyingStore , (String ) null );
148+ }
149+
150+ public BufferedZipStore (@ Nonnull Path underlyingStore , String archiveComment ) {
151+ this (new FilesystemStore (underlyingStore .getParent ()).resolve (underlyingStore .getFileName ().toString ()), archiveComment );
116152 }
117153
118154 public BufferedZipStore (@ Nonnull Path underlyingStore ) {
119- this (new FilesystemStore ( underlyingStore . getParent ()). resolve ( underlyingStore . getFileName (). toString ()) );
120- System . out . println ( "Created BufferedZipStore with underlying path: " + this . underlyingStore . toString ());
155+ this (underlyingStore , null );
156+ }
121157
158+ public BufferedZipStore (@ Nonnull String underlyingStorePath , String archiveComment ) {
159+ this (Paths .get (underlyingStorePath ), archiveComment );
122160 }
123161
124162 public BufferedZipStore (@ Nonnull String underlyingStorePath ) {
125- this (Paths . get ( underlyingStorePath ) );
163+ this (underlyingStorePath , null );
126164 }
127165
128166 /**
129- * Flushes the buffer to the underlying store as a zip file.
167+ * Flushes the buffer and archiveComment to the underlying store as a zip file.
130168 */
131169 public void flush () throws IOException {
132170 writeBuffer ();
133171 }
134172
173+ public String getArchiveComment () {
174+ return archiveComment ;
175+ }
176+
135177 @ Override
136178 public Stream <String []> list (String [] keys ) {
137179 return bufferStore .list (keys );
0 commit comments