support now read from and write into archive with streams
authorab
Fri, 30 May 2008 10:59:59 +0200
changeset 1995 93f2f08bceb3
parent 1994 76336287ae17
child 1996 2f3bbb07a2f9
support now read from and write into archive with streams
ZipArchive.st
--- a/ZipArchive.st	Wed May 28 18:20:00 2008 +0200
+++ b/ZipArchive.st	Fri May 30 10:59:59 2008 +0200
@@ -1247,6 +1247,53 @@
 "
 !
 
+examples2
+"
+    add to new zip archive a entry which is located in memory using selector
+        addFile:'crcTest_resume_compressed.txt' withContents:
+    and real file contents from disk identified by a readStream using selector
+        addFile:rdStreamFile fromStream:
+                                                        [exBegin]
+    |zipwr testDirectory testFileWr rdStreamFile rdFileStream |
+
+    testDirectory := 'C:\Dokumente und Einstellungen\stefan\Eigene Dateien\tmp\'.
+    testFileWr := 'streamtest_uncompressed.zip'.
+    rdStreamFile := 'projects.zip'.
+
+    rdFileStream := ('C:\Dokumente und Einstellungen\stefan\Eigene Dateien\tmp\', rdStreamFile) asFilename readStream.
+    zipwr := ZipArchive newFileNamed:(testDirectory, testFileWr).
+    zipwr addFile:'crcTest_resume_compressed.txt' withContents: 'resume'.
+    zipwr addFile:rdStreamFile fromStream: rdFileStream.
+
+    zipwr closeFile.
+                                                        [exEnd]
+
+    read from zip archive a entry which into memory using selector
+        extract:'crcTest_resume_compressed.txt'
+    and store real file contents from disk using a readStream on archive contents entry
+        readStreamFor: rdStreamFile 
+                                                        [exBegin]
+    |ziprd testDirectory testFileRd wrStreamFile wrFileStream archiveRdStream data1
+     buffer streamAtEnd nextBlockSize |
+
+    testDirectory := 'C:\Dokumente und Einstellungen\stefan\Eigene Dateien\tmp\'.
+    testFileRd := 'streamtest_uncompressed.zip'.
+    wrStreamFile := 'test_projects.zip'.
+
+    ziprd := ZipArchive oldFileNamed:(testDirectory, testFileRd).
+    data1 := ziprd extract:'crcTest_resume_compressed.txt'.
+
+    wrFileStream := ('C:\Dokumente und Einstellungen\stefan\Eigene Dateien\tmp\', wrStreamFile) asFilename writeStream.
+    (ziprd extract:'projects.zip' intoStream: wrFileStream) ifFalse: [
+        self halt.
+    ].
+
+    ziprd closeFile.
+                                                        [exEnd]
+
+"
+!
+
 fileFormatDescription
 
 "/File:    APPNOTE.TXT - .ZIP File Format Specification
@@ -2902,6 +2949,12 @@
     ^ 46
 
     "Created: / 29.3.1998 / 19:11:20 / cg"
+!
+
+streamBufferSize
+    ^ 65536     "/ 1024 * 64
+
+    "Created: / 29.3.1998 / 19:11:20 / cg"
 ! !
 
 !ZipArchive class methodsFor:'debugging'!
@@ -3610,7 +3663,7 @@
 !ZipArchive methodsFor:'reading'!
 
 extract:fileName
-    "extract a filename entry as a byteArray;
+    "extract an entry identified by fileName as a byteArray;
      nil on errors"
 
     |zmemb rawContents data|
@@ -3640,6 +3693,64 @@
     ^ data.
 !
 
+extract:fileName intoStream: aWriteStream
+    "extract a entry indentified by filename into write stream
+      return false on error
+    "
+
+    |zmemb rawContents data buffer rdSize nextBlockSize streamBufferSize|
+
+    (file isNil or: [mode ~~ #read]) ifTrue: [
+        self error: 'Archiv not open for reading ...'.
+        ^ false
+    ].    
+
+    zmemb := self findMember:fileName.
+    zmemb isNil ifTrue:[^ false].
+
+    (zmemb fileStart + startOfArchive) > endOfArchive ifTrue: [
+        ZipFileFormatErrorSignal raiseRequestErrorString:' - zipEntry start is out of the archive bounds'.
+        ^ false
+    ].
+
+    (zmemb fileStart + startOfArchive + (zmemb compressedSize)) > endOfArchive ifTrue: [
+        ZipFileFormatErrorSignal raiseRequestErrorString:' - zipEntry end is out of the archive bounds'.
+        ^ false
+    ].
+
+    file position0Based:(zmemb fileStart + startOfArchive).
+
+    zmemb compressionMethod == 8 ifTrue: [
+        self shouldImplement.
+        rawContents := file nextBytes:(zmemb compressedSize).
+        data := self
+                    decode:rawContents
+                    method:(zmemb compressionMethod)
+                    size:(zmemb uncompressedSize).
+        ^ false
+    ].
+
+    rdSize := zmemb compressedSize.
+    streamBufferSize := self class streamBufferSize.    
+    buffer := ByteArray new: streamBufferSize.
+    [rdSize > 0] whileTrue: [
+        rdSize > (self class streamBufferSize) ifTrue: [
+            nextBlockSize := streamBufferSize.
+        ] ifFalse: [
+            (nextBlockSize := rdSize) > 0 ifTrue: [
+                buffer := ByteArray new: nextBlockSize.
+            ].
+        ].
+        nextBlockSize > 0 ifTrue: [
+            file nextBytes:nextBlockSize into:buffer startingAt:1.
+            aWriteStream nextPutBytes:nextBlockSize from:buffer startingAt:1.
+        ].
+        rdSize := rdSize - nextBlockSize.
+    ].
+
+    ^ true.
+!
+
 extractArchive:fileName
     "extract a filename entry as a byteArray;
      nil on errors"
@@ -3667,31 +3778,6 @@
                  endOfArchive:(zmemb fileStart + startOfArchive + (zmemb compressedSize)).
 !
 
-readStreamFor:nameOfFileInArchive
-    "open a stream on archive contents identified by nameOfFileInArchive"
-
-    |zmemb rs zs|
-
-    (file isNil or: [mode ~~ #read]) ifTrue: [
-        ^ self error: 'Archiv not open for reading ...'.
-    ].    
-
-    zmemb := self findMember:nameOfFileInArchive.
-    zmemb isNil ifTrue:[^ nil].
-
-    zmemb compressionMethod == 0 ifTrue: [
-        rs := ZipUncompressedArchiveStream readonlyFileNamed: archiveName asFilename.
-        rs position0Based:zmemb fileStart.
-        rs readLimit:(zmemb fileStart + zmemb uncompressedSize).
-        ^ rs
-    ].
-
-    zs := ZipCompressedArchiveStream readOpenOn:rs 
-                                       position:zmemb fileStart 
-                                      readLimit:zmemb compressedSize.
-    ^ zs
-!
-
 restoreContentsFromZipDirectory:zipDirectoryName intoDirectory:intoDirectory
     "restore the contents of a file or directory (recursive) from zip archive -> zipDirectoryName 
      into directory -> intoDirectory"
@@ -3701,6 +3787,35 @@
     ^ true
 ! !
 
+!ZipArchive methodsFor:'reading - stream'!
+
+readStreamFor:nameOfFileInArchive
+    "open a stream on archive contents identified by nameOfFileInArchive"
+    self shouldImplement.
+"/
+"/    |zmemb rs zs|
+"/
+"/    (file isNil or: [mode ~~ #read]) ifTrue: [
+"/        ^ self error: 'Archiv not open for reading ...'.
+"/    ].    
+"/
+"/    zmemb := self findMember:nameOfFileInArchive.
+"/    zmemb isNil ifTrue:[^ nil].
+"/
+"/    zmemb compressionMethod == 0 ifTrue: [
+"/        rs := ZipUncompressedArchiveStream readonlyFileNamed: archiveName asFilename.
+"/        rs position0Based:zmemb fileStart.
+"/        rs readLimit:(zmemb fileStart + zmemb uncompressedSize).
+"/        ^ rs
+"/    ].
+"/
+"/    "/ did not work right now proberly
+"/    zs := ZipCompressedArchiveStream readOpenOn:rs 
+"/                                       position:zmemb fileStart 
+"/                                      readLimit:zmemb compressedSize.
+"/    ^ zs
+! !
+
 !ZipArchive methodsFor:'writing'!
 
 addContentsFromFileOrDirectory:realFileOrDirectoryName toZipDirectory:zipDirectoryName
@@ -3724,6 +3839,107 @@
     ^ self addFile: aDirectoryName withContents: nil compressMethod: 0 asDirectory: true.
 !
 
+addFile: aFileName fromStream: aStream
+    ^ self addFile: aFileName fromStream: aStream compressMethod: 0
+!
+
+addFile: aFileName fromStream: aStream compressMethod: theCompressMethod
+    |zipEntry curTime curDate crc32Pos crc32 unCompressedDataSize
+      compressedDataSize buffer rdSize nextBlockSize streamBufferSize|
+
+    (file isNil or: [mode ~~ #write]) ifTrue: [
+        ^ self error: 'Archiv not open for writing ...'.
+    ].
+    zipEntry := ZipMember new default.
+
+    firstEntry isNil ifTrue: [
+        firstEntry := zipEntry.
+    ] ifFalse: [
+        lastEntry next: zipEntry.
+    ].
+
+    lastEntry := zipEntry.
+
+    zipEntry fileName: aFileName.
+    zipEntry fileNameLength: aFileName size.
+    zipEntry uncompressedSize: 0.
+
+    zipEntry compressionMethod: theCompressMethod.
+    zipEntry internalFileAttributes: 1.
+    zipEntry externalFileAttributes: 32.
+
+    curTime := Time now.
+    curDate := Date today.
+    "/ data and time in msdos format
+    zipEntry lastModFileTime: (((curTime seconds // 2) bitOr: (curTime minutes rightShift: -5)) bitOr: (curTime hours rightShift: -11)).
+    zipEntry lastModFileDate: (((curDate day) bitOr: (curDate month rightShift: -5)) bitOr: (((curDate year) - 1980) rightShift: -9)).
+
+    "/ ensure that the file position is at the end
+    file setToEnd.
+
+    zipEntry relativeLocalHeaderOffset:(file position).
+    file nextPutLong: 16r04034b50  MSB:false.
+    file nextPutShort:zipEntry versionNeedToExtract MSB:false.
+    file nextPutShort:zipEntry generalPurposBitFlag MSB:false.
+    file nextPutShort:zipEntry compressionMethod MSB:false.
+    file nextPutShort:zipEntry lastModFileTime MSB:false.
+    file nextPutShort:zipEntry lastModFileDate MSB:false.
+    crc32Pos := file position.
+    file nextPutLong:zipEntry crc32 MSB:false.
+    file nextPutLong:zipEntry compressedSize MSB:false.
+    file nextPutLong:zipEntry uncompressedSize MSB:false.
+    file nextPutShort:zipEntry fileNameLength MSB:false.
+    file nextPutShort:zipEntry extraFieldLength MSB:false.
+    file nextPutAll:zipEntry fileName.
+    zipEntry extraField notNil ifTrue: [
+        file nextPutAll:zipEntry extraField.
+    ].
+
+    (theCompressMethod == 8) ifTrue: [
+        self shouldImplement.
+"/        |tmpCompressedData tmpCompressedDataSize|
+"/        tmpCompressedData := ByteArray new:(data size + 16). "/ if the compression is less then the additional overhead we need more space in buffer
+"/        tmpCompressedDataSize := ZipStream compress:data into:tmpCompressedData.
+"/
+"/        zipEntry compressedSize: (tmpCompressedDataSize - 6). "/6 = the zlib specific data 2 bytes in front and 4 bytes behind the real data
+"/        theCompressedData := tmpCompressedData copyFrom: 3. "/ 2 bytes before the real data
+    ] ifFalse: [
+        crc32 := 0.
+        streamBufferSize := self class streamBufferSize.    
+        buffer := ByteArray new: streamBufferSize.
+        rdSize := aStream size.
+        unCompressedDataSize := rdSize.
+        [rdSize > 0] whileTrue: [
+            rdSize > (self class streamBufferSize) ifTrue: [
+                nextBlockSize := streamBufferSize.
+            ] ifFalse: [
+                (nextBlockSize := rdSize) > 0 ifTrue:[
+                    buffer := ByteArray new: nextBlockSize.
+                ].
+            ].
+            nextBlockSize > 0 ifTrue: [
+                aStream nextBytes:nextBlockSize into:buffer startingAt:1.
+                file nextPutBytes:nextBlockSize from:buffer startingAt:1.
+                crc32 := (ZipStream crc32BytesIn: buffer crc: crc32).
+            ].
+            rdSize := rdSize - nextBlockSize.
+        ].
+        compressedDataSize := unCompressedDataSize.
+    ].
+
+    zipEntry crc32: crc32.
+    zipEntry uncompressedSize: unCompressedDataSize.
+    zipEntry compressedSize: compressedDataSize.
+
+    file position0Based:crc32Pos.
+
+    file nextPutLong:zipEntry crc32 MSB:false.
+    file nextPutLong:zipEntry compressedSize MSB:false.
+    file nextPutLong:zipEntry uncompressedSize MSB:false.
+
+    file setToEnd.
+!
+
 addFile: aFileName withContents: data
     ^ self addFile: aFileName withContents: data compressMethod: 8 asDirectory: false.
 !
@@ -3804,6 +4020,65 @@
     ].
 ! !
 
+!ZipArchive methodsFor:'writing - stream'!
+
+writeStreamFor:nameOfFileInArchive
+    "create new entry in central directory"
+    self shouldImplement.
+"/    |zipEntry curTime curDate|
+"/
+"/    (file isNil or: [mode ~~ #write]) ifTrue: [
+"/        ^ self error: 'Archiv not open for writing ...'.
+"/    ].
+"/    zipEntry := ZipMember new default.
+"/
+"/    firstEntry isNil ifTrue: [
+"/        firstEntry := zipEntry.
+"/    ] ifFalse: [
+"/        lastEntry next: zipEntry.
+"/    ].
+"/
+"/    lastEntry := zipEntry.
+"/
+"/    zipEntry fileName: nameOfFileInArchive.
+"/    zipEntry fileNameLength: nameOfFileInArchive size.
+"/    zipEntry uncompressedSize: 0.
+"/
+"/    zipEntry compressionMethod: 0.
+"/    zipEntry internalFileAttributes: 1.
+"/    zipEntry externalFileAttributes: 32.
+"/
+"/    curTime := Time now.
+"/    curDate := Date today.
+"/    "/ data and time in msdos format
+"/    zipEntry lastModFileTime: (((curTime seconds // 2) bitOr: (curTime minutes rightShift: -5)) bitOr: (curTime hours rightShift: -11)).
+"/    zipEntry lastModFileDate: (((curDate day) bitOr: (curDate month rightShift: -5)) bitOr: (((curDate year) - 1980) rightShift: -9)).
+"/
+"/    zipEntry compressedSize: zipEntry uncompressedSize.
+"/
+"/    "/ ensure that the file position is at the end
+"/    file setToEnd.
+"/
+"/    zipEntry relativeLocalHeaderOffset:(file position).
+"/    file nextPutLong: 16r04034b50  MSB:false.
+"/    file nextPutShort:zipEntry versionNeedToExtract MSB:false.
+"/    file nextPutShort:zipEntry generalPurposBitFlag MSB:false.
+"/    file nextPutShort:zipEntry compressionMethod MSB:false.
+"/    file nextPutShort:zipEntry lastModFileTime MSB:false.
+"/    file nextPutShort:zipEntry lastModFileDate MSB:false.
+"/    file nextPutLong:zipEntry crc32 MSB:false.
+"/    file nextPutLong:zipEntry compressedSize MSB:false.
+"/    file nextPutLong:zipEntry uncompressedSize MSB:false.
+"/    file nextPutShort:zipEntry fileNameLength MSB:false.
+"/    file nextPutShort:zipEntry extraFieldLength MSB:false.
+"/    file nextPutAll:zipEntry fileName.
+"/    zipEntry extraField notNil ifTrue: [
+"/        file nextPutAll:zipEntry extraField.
+"/    ].
+"/
+"/    ^ file
+! !
+
 !ZipArchive::ZipCentralDirectory methodsFor:'accessing'!
 
 centralDirectorySize
@@ -4265,7 +4540,7 @@
 !ZipArchive class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic2/ZipArchive.st,v 1.62 2008-05-28 16:20:00 ab Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic2/ZipArchive.st,v 1.63 2008-05-30 08:59:59 ab Exp $'
 ! !
 
 ZipArchive initialize!