Giter Site home page Giter Site logo

jaime-olivares / zipstorer Goto Github PK

View Code? Open in Web Editor NEW
178.0 15.0 65.0 189 KB

A Pure C# Class to Store Files in Zip

License: MIT License

C# 66.29% Visual Basic .NET 33.71%
zip-storage compression nuget netstandard netframework cross-platform zip async-await deflate csharp

zipstorer's Introduction

ZipStorer

A Pure C# class for storing files in Zip format

NuGet github Build Status

ZipStorer is a minimalistic cross-platform .net class to create Zip files and store/retrieve files to/from it, by using the Deflate algorithm. No other compression methods supported.

Advantages and usage

ZipStorer has the following advantages:

  • It is a short and monolithic C# class that can be embedded as source code in any project (1 source file)
  • No Interop calls, increments portability to Mono and other non-Windows platforms
  • Can also be implemented with Mono, .NET Compact Framework and .Net Standard 2.0+
  • Async methods for storing and extracting files (only for .Net Framework 4.5+ and .Net Standard 2.0+)
  • NEW: Support for Zip64 (file sizes > 4GB)
  • UTF8 Encoding support and ePUB compatibility
  • Available as a nuget package

Using the code

The ZipStorer class is the unique one needed to create the zip file. It contains a nested structure (ZipFileEntry) for collecting each directory entry. The class has been declared inside the System.IO namespace.

There is no default constructor. There are two ways to construct a new ZipStorer instance, depending on specific needs: use either the Create() or the Open() static method. To create a new Zip file, use the Create() method like this:

ZipStorer zip = ZipStorer.Create(filename, comment);  // file-oriented version
ZipStorer zip = ZipStorer.Create(stream, comment);  // stream-oriented version

It is required to specify the full path for the new zip file, or pass a valid stream, and optionally add a comment. For opening an existing zip file for appending, the Open() method is required, like the following:

ZipStorer zip = ZipStorer.Open(filename, fileaccess);  // file-oriented version
ZipStorer zip = ZipStorer.Open(stream, fileaccess);  // stream-oriented version

Where fileaccess should be of type System.IO.FileAccess enumeration type. Also, as now ZipStorer is derived from IDisposable interface, the using keyword can be used to ensure proper disposing of the storage resource:

using (ZipStorer zip = ZipStorer.Create(filename, comment))
{
    // some operations with zip object
    //
}   // automatic close operation here

For adding files into an opened zip storage, there are two available methods:

public void AddFile(ZipStorer.Compress _method, string _pathname, string _filenameInZip, string _comment);
public void AddStream(ZipStorer.Compress _method, string _filenameInZip, Stream _source, DateTime _modTime, string _comment);

The first method allows adding an existing file to the storage. The first argument receives the compression method; it can be Store or Deflate enum values. The second argument admits the physical path name, the third one allows to change the path or file name to be stored in the Zip, and the last argument inserts a comment in the storage. Notice that the folder path in the _pathname argument is not saved in the Zip file. Use the _filenameInZip argument instead to specify the folder path and filename. It can be expressed with both slashes or backslashes.

The second method allows adding data from any kind of stream object derived from the System.IO.Stream class. Internally, the first method opens a FileStream and calls the second method.

Finally, it is required to close the storage with the Close() method. This will save the central directory information too. Alternatively, the Dispose() method can be used.

Extracting stored files

For extracting a file, the zip directory shall be read first, by using the ReadCentralDir() method, and then the ExtractFile() method, like in the following minimal sample code:

// Open an existing zip file for reading
ZipStorer zip = ZipStorer.Open(@"c:\data\sample.zip", FileAccess.Read);

// Read the central directory collection
List<ZipStorer.ZipFileEntry> dir = zip.ReadCentralDir();

// Look for the desired file
foreach (ZipStorer.ZipFileEntry entry in dir)
{
    if (Path.GetFileName(entry.FilenameInZip) == "sample.jpg")
    {
        // File found, extract it
        zip.ExtractFile(entry, @"c:\data\sample.jpg");
        break;
    }
}
zip.Close();

Removal of entries

Removal of entries in a zip file is a resource-consuming task. The simplest way is to copy all non-removed files into a new zip storage. The RemoveEntries() static method will do this exactly and will construct the ZipStorer object again. For the sake of efficiency, RemoveEntries() will accept many entry references in a single call, as in the following example:

List<ZipStorer.ZipFileEntry> removeList = new List<ZipStorer.ZipFileEntry>();

foreach (object sel in listBox4.SelectedItems)
{
    removeList.Add((ZipStorer.ZipFileEntry)sel);
}

ZipStorer.RemoveEntries(ref zip, removeList);

File and stream usage

The current release of ZipStorer supports both files and streams for creating and opening a zip storage. Several methods are overloaded for this dual support. The advantage of file-oriented methods is simplicity, since those methods will open or create files internally. On the other hand, stream-oriented methods are more flexible by allowing to manage zip storages in streams different than files. File-oriented methods will invoke internally to equivalent stream-oriented methods. Notice that not all streams will apply, because the library requires the streams to be randomly accessed (CanSeek = true). The RemoveEntries method will work only if the zip storage is a file.

    // File-oriented methods:
    public static ZipStorer Create(string _filename, string _comment);
    public static ZipStorer Open(string _filename, FileAccess _access);
    public ZipFileEntry AddFile(Compression _method, string _pathname, string _filenameInZip, string _comment);
    public bool ExtractFile(ZipFileEntry _zfe, string _filename);
    public static bool RemoveEntries(ref ZipStorer _zip, List<zipfileentry> _zfes);  // No stream-oriented equivalent

    // Stream-oriented methods:
    public static ZipStorer Create(Stream _stream, string _comment, bool _leaveOpen);
    public static ZipStorer Open(Stream _stream, FileAccess _access, bool _leaveOpen);
    public ZipFileEntry AddStream(Compression _method, string _filenameInZip, Stream _source, DateTime _modTime, string _comment);
    public bool ExtractFile(ZipFileEntry _zfe, Stream _stream);

    // Async methods (not available for .Net Framework 2.0):
    public ZipFileEntry AddStreamAsync(Compression _method, string _filenameInZip, Stream _source, DateTime _modTime, string _comment)
    public async Task<bool> ExtractFileAsync(ZipFileEntry _zfe, Stream _stream);

The _leaveOpen argument will prevent the stream to be closed after completing the generation of the zip package.

Filename encoding

Traditionally, the ZIP format supported DOS encoding system (a.k.a. IBM Code Page 437) for filenames in header records, which is a serious limitation for using non-occidental and even some occidental characters. Since 2007, the ZIP format specification was improved to support Unicode's UTF-8 encoding system.

ZipStorer class detects UTF-8 encoding by reading the proper flag in each file's header information. For enforcing filenames to be encoded with UTF-8 system, set the EncodeUTF8 member of ZipStorer class to true. All new filenames added will be encoded with UTF8. Notice this doesn't affect stored file contents at all. Also be aware that Windows Explorer's embedded Zip format facility does not recognize well the UTF-8 encoding system, as WinZip or WinRAR do.

.Net Standard support

Now ZipStorer supports .Net Standard 2.0+ and hence a broad range of platforms.

If developing with Visual Studio Code, the csproj file must reference the nuget package:

  <ItemGroup>
    <PackageReference Include="ZipStorer" Version="*" />
  </ItemGroup>

zipstorer's People

Contributors

adisadi avatar aigin avatar benchen71 avatar cloudflying avatar gitii avatar jaime-olivares avatar kenkendk avatar lasat avatar minuswall avatar toxaris-nl avatar valentinbreiz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zipstorer's Issues

The library does not notice a file is not in Zip format

Hey,
I noticed the library does not check the file stamp to determine if the filew passed in is a Zip file. I passed in a .Rar archive by mistake and it started parsing away and crashing eventually. I created a simple fix that checks for the ZIP filestamp (0x50,0x4b,0x03, 0x04) before parsing to avoid that. I hope I am notr missing anything ?

Proper Date/Time

Can you add the proper date/time in the extended header instead of the brain dead dos format date/time?

Not Working Support for Zip64 (file sizes > 4GB)

Your helper file is very much useful, I have used in my 3-5 projects but when I used in recent project where zipping was of more than 6gb folder, zip file was generating successfully but unzipping was making issue in checksum error.

After going through your code and comments I found that support for larger sizes file you have changed the WriteEndRecord parameter size but not changed in close function to send the value for the same so I have changed some little two things in two lines of your code.

Please change the data type in ### close() function at line### 334, 335 from uint to long as when there is bigger sizes files the stream position is at greater than the uint max size.

Please look the same and do changes for making this more awesome successful code.

    /// <summary>
    /// Updates central directory (if pertinent) and close the Zip storage
    /// </summary>
    /// <remarks>This is a required step, unless automatic dispose is used</remarks>
    public void Close()
    {
        if (this.Access != FileAccess.Read)
         {
            long centralOffset = this.ZipFileStream.Position;
            long centralSize = 0;

            if (this.CentralDirImage != null)
                this.ZipFileStream.Write(CentralDirImage, 0, CentralDirImage.Length);

            for (int i = 0; i < Files.Count; i++)
            {
                long pos = this.ZipFileStream.Position;
                this.WriteCentralDirRecord(Files[i]);
                centralSize += (uint)(this.ZipFileStream.Position - pos);
            }

            if (this.CentralDirImage != null)
                this.WriteEndRecord(centralSize + (uint)CentralDirImage.Length, centralOffset);
            else
                this.WriteEndRecord(centralSize, centralOffset);
        }

        if (this.ZipFileStream != null && !this.leaveOpen)
        {
            this.ZipFileStream.Flush();
            this.ZipFileStream.Dispose();
            this.ZipFileStream = null;
        }
    }

After changing code then to file extraction giving error of checksum please look in this issue.

Suggestion for the extraction routine in the ZipStorer class demo

The extract routine in the demo class does not work properly with the internal directory structure of the zip file. I tried extracting a zip file containing multiple shape files. The shape file were organized by folders, each folder containing shape files with the same name. When I ran the demo extraction I ended up with only one set of the shape files in a single folder (the other sets were overwritten by newer files of the same name). Basically, my suggestion is that the following line

path = Path.Combine(TextTargetFolder.Text, Path.GetFileName(entry.FilenameInZip));

is changed to

path = Path.Combine(TextTargetFolder.Text, entry.FilenameInZip);

to preserve the internal zip directory structure.

InvalidDataException when trying to unzip archive created by php ZipArchive

Hi! I've tried to unzip the attached archive cert_21.zip with the latest version available at the moment. It fails with exception System.IO.InvalidDataException, but this archive is successfully being unzipped on my MacOS using default Archive Utility (not checked with other tools). My coworker told me that he created this archive with php ZipArchive.

With debugger I see that the problem occurs on these lines:

// check if comment field is the very last data in file
if (commentPosition + commentSize != this.ZipFileStream.Length)
    return false;

1952 + 0 != 2208

If I disable these lines, the archive is being unzipped (didn't check if it's done correct). I execute code in Xamarin.iOS project.

Please, take a look and check if this is a bug.

p.s. also zipinfo utility doesn't complain about this archive.

zipinfo -v cert_21.zip
Archive:  cert_21.zip
There is no zipfile comment.

End-of-central-directory record:
-------------------------------

  Zip archive file size:                      2208 (00000000000008A0h)
  Actual end-cent-dir record offset:          1930 (000000000000078Ah)
  Expected end-cent-dir record offset:        1930 (000000000000078Ah)
  (based on the length of the central directory and its expected offset)

  This zipfile constitutes the sole disk of a single-part archive; its
  central directory contains 6 entries.
  The central directory is 480 (00000000000001E0h) bytes long,
  and its (expected) offset in bytes from the beginning of the zipfile
  is 1450 (00000000000005AAh).


Central directory entry #1:
---------------------------

  header.key

  offset of local header from start of archive:   0
                                                  (0000000000000000h) bytes
  file system or operating system of origin:      Unix
  version of encoding software:                   3.0
  minimum file system compatibility required:     MS-DOS, OS/2 or NT FAT
  minimum software version required to extract:   2.0
  compression method:                             deflated
  compression sub-type (deflation):               normal
  file security status:                           not encrypted
  extended local header:                          no
  file last modified on (DOS date/time):          2021 Jun 9 12:47:38
  file last modified on (UT extra field modtime): 2021 Jun 9 12:47:38 local
  file last modified on (UT extra field modtime): 2021 Jun 9 09:47:38 UTC
  32-bit CRC value (hex):                         1daf4c3e
  compressed size:                                719 bytes
  uncompressed size:                              898 bytes
  length of filename:                             10 characters
  length of extra field:                          24 bytes
  length of file comment:                         0 characters
  disk number on which file begins:               disk 1
  apparent file type:                             binary
  Unix file attributes (100644 octal):            -rw-r--r--
  MS-DOS file attributes (00 hex):                none

  The central-directory extra field contains:
  - A subfield with ID 0x5455 (universal time) and 5 data bytes.
    The local extra field has UTC/GMT modification/access times.
  - A subfield with ID 0x7875 (Unix UID/GID (any size)) and 11 data bytes:
    01 04 d0 07 00 00 04 17 27 00 00.

  There is no file comment.

Central directory entry #2:
---------------------------

  masks.key

  offset of local header from start of archive:   787
                                                  (0000000000000313h) bytes
  file system or operating system of origin:      Unix
  version of encoding software:                   3.0
  minimum file system compatibility required:     MS-DOS, OS/2 or NT FAT
  minimum software version required to extract:   1.0
  compression method:                             none (stored)
  file security status:                           not encrypted
  extended local header:                          no
  file last modified on (DOS date/time):          2021 Jun 9 12:47:38
  file last modified on (UT extra field modtime): 2021 Jun 9 12:47:38 local
  file last modified on (UT extra field modtime): 2021 Jun 9 09:47:38 UTC
  32-bit CRC value (hex):                         89e6e4e8
  compressed size:                                88 bytes
  uncompressed size:                              88 bytes
  length of filename:                             9 characters
  length of extra field:                          24 bytes
  length of file comment:                         0 characters
  disk number on which file begins:               disk 1
  apparent file type:                             binary
  Unix file attributes (100644 octal):            -rw-r--r--
  MS-DOS file attributes (00 hex):                none

  The central-directory extra field contains:
  - A subfield with ID 0x5455 (universal time) and 5 data bytes.
    The local extra field has UTC/GMT modification/access times.
  - A subfield with ID 0x7875 (Unix UID/GID (any size)) and 11 data bytes:
    01 04 d0 07 00 00 04 17 27 00 00.

  There is no file comment.

Central directory entry #3:
---------------------------

  masks2.key

  offset of local header from start of archive:   942
                                                  (00000000000003AEh) bytes
  file system or operating system of origin:      Unix
  version of encoding software:                   3.0
  minimum file system compatibility required:     MS-DOS, OS/2 or NT FAT
  minimum software version required to extract:   1.0
  compression method:                             none (stored)
  file security status:                           not encrypted
  extended local header:                          no
  file last modified on (DOS date/time):          2021 Jun 9 12:47:38
  file last modified on (UT extra field modtime): 2021 Jun 9 12:47:38 local
  file last modified on (UT extra field modtime): 2021 Jun 9 09:47:38 UTC
  32-bit CRC value (hex):                         911b3453
  compressed size:                                88 bytes
  uncompressed size:                              88 bytes
  length of filename:                             10 characters
  length of extra field:                          24 bytes
  length of file comment:                         0 characters
  disk number on which file begins:               disk 1
  apparent file type:                             binary
  Unix file attributes (100644 octal):            -rw-r--r--
  MS-DOS file attributes (00 hex):                none

  The central-directory extra field contains:
  - A subfield with ID 0x5455 (universal time) and 5 data bytes.
    The local extra field has UTC/GMT modification/access times.
  - A subfield with ID 0x7875 (Unix UID/GID (any size)) and 11 data bytes:
    01 04 d0 07 00 00 04 17 27 00 00.

  There is no file comment.

Central directory entry #4:
---------------------------

  name.key

  offset of local header from start of archive:   1098
                                                  (000000000000044Ah) bytes
  file system or operating system of origin:      Unix
  version of encoding software:                   3.0
  minimum file system compatibility required:     MS-DOS, OS/2 or NT FAT
  minimum software version required to extract:   1.0
  compression method:                             none (stored)
  file security status:                           not encrypted
  extended local header:                          no
  file last modified on (DOS date/time):          2021 Jun 9 12:47:38
  file last modified on (UT extra field modtime): 2021 Jun 9 12:47:38 local
  file last modified on (UT extra field modtime): 2021 Jun 9 09:47:38 UTC
  32-bit CRC value (hex):                         aa04b7fa
  compressed size:                                11 bytes
  uncompressed size:                              11 bytes
  length of filename:                             8 characters
  length of extra field:                          24 bytes
  length of file comment:                         0 characters
  disk number on which file begins:               disk 1
  apparent file type:                             binary
  Unix file attributes (100644 octal):            -rw-r--r--
  MS-DOS file attributes (00 hex):                none

  The central-directory extra field contains:
  - A subfield with ID 0x5455 (universal time) and 5 data bytes.
    The local extra field has UTC/GMT modification/access times.
  - A subfield with ID 0x7875 (Unix UID/GID (any size)) and 11 data bytes:
    01 04 d0 07 00 00 04 17 27 00 00.

  There is no file comment.

Central directory entry #5:
---------------------------

  primary.key

  offset of local header from start of archive:   1175
                                                  (0000000000000497h) bytes
  file system or operating system of origin:      Unix
  version of encoding software:                   3.0
  minimum file system compatibility required:     MS-DOS, OS/2 or NT FAT
  minimum software version required to extract:   1.0
  compression method:                             none (stored)
  file security status:                           not encrypted
  extended local header:                          no
  file last modified on (DOS date/time):          2021 Jun 9 12:47:38
  file last modified on (UT extra field modtime): 2021 Jun 9 12:47:38 local
  file last modified on (UT extra field modtime): 2021 Jun 9 09:47:38 UTC
  32-bit CRC value (hex):                         ffe813bd
  compressed size:                                68 bytes
  uncompressed size:                              68 bytes
  length of filename:                             11 characters
  length of extra field:                          24 bytes
  length of file comment:                         0 characters
  disk number on which file begins:               disk 1
  apparent file type:                             binary
  Unix file attributes (100644 octal):            -rw-r--r--
  MS-DOS file attributes (00 hex):                none

  The central-directory extra field contains:
  - A subfield with ID 0x5455 (universal time) and 5 data bytes.
    The local extra field has UTC/GMT modification/access times.
  - A subfield with ID 0x7875 (Unix UID/GID (any size)) and 11 data bytes:
    01 04 d0 07 00 00 04 17 27 00 00.

  There is no file comment.

Central directory entry #6:
---------------------------

  primary2.key

  offset of local header from start of archive:   1312
                                                  (0000000000000520h) bytes
  file system or operating system of origin:      Unix
  version of encoding software:                   3.0
  minimum file system compatibility required:     MS-DOS, OS/2 or NT FAT
  minimum software version required to extract:   1.0
  compression method:                             none (stored)
  file security status:                           not encrypted
  extended local header:                          no
  file last modified on (DOS date/time):          2021 Jun 9 12:47:38
  file last modified on (UT extra field modtime): 2021 Jun 9 12:47:38 local
  file last modified on (UT extra field modtime): 2021 Jun 9 09:47:38 UTC
  32-bit CRC value (hex):                         e53582c5
  compressed size:                                68 bytes
  uncompressed size:                              68 bytes
  length of filename:                             12 characters
  length of extra field:                          24 bytes
  length of file comment:                         0 characters
  disk number on which file begins:               disk 1
  apparent file type:                             binary
  Unix file attributes (100644 octal):            -rw-r--r--
  MS-DOS file attributes (00 hex):                none

  The central-directory extra field contains:
  - A subfield with ID 0x5455 (universal time) and 5 data bytes.
    The local extra field has UTC/GMT modification/access times.
  - A subfield with ID 0x7875 (Unix UID/GID (any size)) and 11 data bytes:
    01 04 d0 07 00 00 04 17 27 00 00.

  There is no file comment.

Should support over 4.2 Giga adding files

Old version Zipstorer did not support big files if added file size was over uint range :). I have not tested this new version but I had feeling, when I looked source code, that there is same problem. You should check this out.

hello , i want to zip to memorystream

hello , i want save to MemoryStream , export the file by external code . but found that the export of the file can not be properly extracted.

        public byte[] Compress(byte[] bytes)
        {
			Stream stream = new MemoryStream();
			
			ZipStorer zip = ZipStorer.Create(stream, string.Empty);
			
			Stream zipData = new MemoryStream(bytes);
			zip.AddStream(ZipStorer.Compression.Deflate ,"default", zipData, DateTime.Now, string.Empty);

			bytes = zip.Close(); // i change this code, make it can return byte[]
			return bytes;
        }

DateTimeToDosTime exception

I'm using zipstorer.vb in an application to open and save EPUB files. I've discovered that the saving process is crashing in the DateTimeToDosTime function when files in the EPUB have "weird" dates. (Note that they open fine.)

The error that is given is "Arithmetic operation resulted in an overflow". If I check the value of the variable _dt, I get the following values:
_dt = #12/31/2107 11:00:00 PM#
_dt.Second = 0
_dt.Minute = 0
_dt.Hour = 23
_dt.Day = 31
_dt.Month = 12
_dt.Year = 2107

When I look at the EPUB file in 7-zip, the file that zipstorer is currently trying to save to the stream has a date modified 1980-01-01 07:00 (even though the created date is 2011-06-02 05:35).

Any idea what's causing the overflow?

Not possible to delete

Hi,

First i have to say nice work. I have a problem when i extract all files i would like to remove zip file afterwords, but it wont let me and zip is being lock by this class. Im using zip.dispose and also zip.close, but still it is locked. Any help pls ?

DosTimeToDateTime

I'm not good programmer, so I can't describe the issue will .. this exception showing with some files, not with all, and fire from DosTimeToDateTime Function
System.ArgumentOutOfRangeException: 'Hour, Minute, and Second parameters describe an un-representable DateTime.'

Feature suggestions

Hi all, I am planning to give this old library a new life. Please suggest features that you may not found in the existing Microsoft APIs. Take into account that this is a small library without dependencies (with exception of the Deflate class), so I won't be inclined to add very complex functionalities. Thanks.

Async-Support

Hi,

I added some basic support for async-operation.
Its in my fork. Feel free to pull it in here.
I'll test it a bit during development.

Greetings

Writing a stream - help needed

Hi!
First of all, thank you very much for your effort writing this software!

I need it, because the regular "ZipArchive" does not
tile_example
support storing without compression. It has a flag "CompressionLevel.NoCompression", that still produces compressed files. This is really weird and is discussed here also: https://stackoverflow.com/questions/38704792/set-ziparchiveentry-compressionlevel-to-compressionlevel-nocompression-not-work?rq=1

I need storing for a file format, which we "invented".

Anyway, I have successfully implemented your class. Storing of a text as a stream works (I did it like in your example), but adding Image (Bitmap) objects from memory through stream produces 0 byte entries in the ZIP.

I paste lines of code here, maybe you could help?

So, what I do: I create the ZIP archive, then I add a MemoryStream to it, in which I have stored the "tileData", which basically is an Image Object, which I read before from a Tiff file into this object.
In the attached image, you see the ZIP archive, which I opened. The entries of the images are there, but with 0 bytes.

zip = System.IO.Compression.ZipStorer.Create(sziFilename2, "SZI (c) Smart In Media"); MemoryStream tileFile = new MemoryStream(); tileData.Save(tileFile, ImageFormat.Jpeg); zip.AddStream(ZipStorer.Compression.Store, "test.jpeg", tileFile, DateTime.Now, ""); tileFile.Close(); zip.Close();

CA1028: Enum storage should be Int32

The FxCop static analysers are reporting CA1028 against the following line in ZipStorer.cs:

public enum Compression : ushort

These state that

Even though you can change this underlying type (from System.Int32), it is not necessary or recommended for most scenarios. No significant performance gain is achieved by using a data type that is smaller than Int32. If you cannot use the default data type, you should use one of the Common Language System (CLS)-compliant integral types, Byte, Int16, Int32, or Int64 to make sure that all values of the enumeration can be represented in CLS-compliant programming languages.

So, please consider making one of the following changes:

  • If the fact that it is a ushort specifically is relied upon and required, then change its underlying type to System.UInt16, otherwise
  • alter the code such that its underlying type is the default System.Int32.

Thanks.

Remove last zip entry corrupts archive, sometimes.

Excellent tool BTW. Comments in zip entries is a unique capability. A user of my software reports that his zip file gets corrupted when removing the last entry (Win 7, NET 4.0). I have not reproduced this in my testing, so I am puzzled - maybe a timing or access issue? I am not using streams. An ideas? Here is my code, which is nearly the same as in example:

// Function to delete one zip file entry, by matching a filename. All will be uppercase.

    private void RemoveOne(string aname)
    {
        List<ZipStorer.ZipFileEntry> removeList = new List<ZipStorer.ZipFileEntry>();
        // Look for the entry that matches the given name
        foreach (ZipStorer.ZipFileEntry entry in zipdir)
        {
            if (Path.GetFileName(entry.FilenameInZip) == aname)
            {
                removeList.Add(entry);
                break;
            }
        }
        // double check
        if (removeList.Count > 0)
        {
            ZipStorer.RemoveEntries(ref zip, removeList); // list is just one
            // the close and reopen is done within the removeentries method, but somehow not quite right?
        }
        // should be done. I think I need this.
        zipdir = zip.ReadCentralDir(); // needed? what happen when last entry is removed?
    }

Invalid Zip64 extended information record format

Hi!
Thanks for awesome tool to work with zip! Unfortunately i ran into a problem when reading the zip64 file.

According with zip format specification fields in zip64 extended information record MUST only appear if the corresponding Local or Central directory record field is set to 0xFFFF or 0xFFFFFFFF

This issue may be fixed by change // ZIP64 Information block in ReadExtraInfo function to this:

if (extraId == 0x0001) // ZIP64 Information
{
    int extraOffset = 4;
    if (_zfe.FileSize == 0xFFFFFFFF)
    {
        _zfe.FileSize = BitConverter.ToInt64(buffer, pos + extraOffset);
        extraOffset += 8;
    }
    if (_zfe.CompressedSize == 0xFFFFFFFF)
    {
        _zfe.CompressedSize = BitConverter.ToInt64(buffer, pos + extraOffset);
        extraOffset += 8;
    }
    if (_zfe.HeaderOffset == 0xFFFFFFFF)
    {
        _zfe.HeaderOffset = BitConverter.ToInt64(buffer, pos + extraOffset);
    }
}

And calculate zfe.FileOffset after invocation ReadExtraInfo.

But CreateExtraInfo should be rewrited to.

not able to remove one entry

hi and thanks for the very useful utility. I am using it to manipulate .idml files which are compressed folders for adobe indesign files. Extracting a file from the directory works just fine.

My goal is to replace a file in the directory. To do this I figures I should first remove the entry and then add a new one.

I am facing a problem when trying to remove an entry. After executing RemoveEntries(), the entry is still there. I can check because after running, the number of entries didn't change. However, RemoveEntries() is returning true every time.

            ZipStorer zip = ZipStorer.Open(idmlPath, FileAccess.Write);
            
            List<ZipStorer.ZipFileEntry> removeList = new List<ZipStorer.ZipFileEntry>();
            // Look for the desired file to replace
            foreach (ZipStorer.ZipFileEntry entry in zip.ReadCentralDir())
            {
                if (Path.GetFileName(entry.FilenameInZip) == Path.GetFileName(archivePath))
                {
                    removeList.Add(entry);
                }
            }
            if (removeList.Count > 0)
            {
                bool result = ZipStorer.RemoveEntries(ref zip, removeList);
            }

            zip.Close();

not sure if this is a bug or I am doing something wrong. Assuming that the ZipStorer object should be opened with write access.

Issue with DosTimeToDateTime routine when the original modify time is not set

I found an issue with the DosTimeToDateTime routine. I have a KMZ file from which I wanted to extract the underlying KML file. However, the KML file does not have any attributes/metadata specified. This causes the class to crash when calling the DosTimeToDateTime routine with 0 as the argument. I modified the routine as following:

private DateTime DosTimeToDateTime(uint _dt)
{
    int year = (int)(_dt >> 25) + 1980;
    int month = (int)(_dt >> 21) & 15;
    int day = (int)(_dt >> 16) & 31;
    int hour = (int)(_dt >> 11) & 31;
    int minute = (int)(_dt >> 5) & 63;
    int second = (int)(_dt & 31) * 2;

    return new DateTime(year, month == 0 ? 1 : month, day == 0 ? 1 : day, hour, minute, second);
}

Maybe returning DateTime.Now would be better. Either way, in case the modify date/time stamp is missing, the original code crashes.

Large corrupt zip archive takes indefinitely long time to open

Hello,
I'm using ZipStorer with sometimes large zip archives. It could happen that a download of a large zip archive (above 1GB) is interrupted and a corrupt zip archive without central direcotry structure is caused. When opening such a file it takes an indefinitely long time (definitely longer than 10 minutes for 2GB) for the ZipStorer to search the whole zip for a "End of central directory record" (cause it did not exist).

Is there the possibility to get an earlier feedback that the zip archive is corrupted. A possible solution could be to only search in the last MB of the zip for this record? The only variable size in this record is a "comment" value.

It would be valuable to get a quicker feedback of a broken zip.

`UpdateCrcAndSizes` sets stream position on CanSeek = false streams

Firstly, terrific job with ZipStorer!

I'm just using zip storer a stream that does not support seeking. It looks like UpdateCrcAndSizes sets the position property of a stream, even when CanSeek = false.

According to https://msdn.microsoft.com/en-us/library/system.io.stream.canseek(v=vs.110).aspx "If a class derived from Stream does not support seeking, calls to Length, SetLength, Position, and Seek throw a NotSupportedException."

Sadly, checking the code I'm not sure how to work around it, short of wrapping it in say a buffered stream. Any thoughts?

Reading an archive content and then adding no files to it causes archive corruption

Thank you very much for developing ZipStorer. I have used it in several apps and until now I haven't encountered a problem.

In my last project I needed check a content of archive before add new files to avoid duplicate file names in that archive. I find out if I read an archive content and then I add no files into it to avoid duplicates causes an archive corruption.

See my test code. After first run a new archive is created. After second run an existing archive is opened and after check for duplicate file names in archive no files are added. And after that the whole archive is corrupted! And I opened only an existing archive, then I read its content, I wrote down no files and in the end I close it.

Do you have an idea where the problem might be?

const string archiveFilePath = @"C:\Temp\test.zip";
var archiveFileExists = File.Exists(archiveFilePath);
using (var zipArchive = archiveFileExists
  ? ZipStorer.Open(archiveFilePath, FileAccess.ReadWrite)
  : ZipStorer.Create(archiveFilePath, "TestArchive"))
{
  var zipFileEntries = archiveFileExists
    ? zipArchive.ReadCentralDir()
    : new List<ZipStorer.ZipFileEntry>();
  for (int i = 0; i < 10; i++)
  {
    if (zipFileEntries.Any(e => e.FilenameInZip == $"TestFile_{i + 1:00}.txt")) continue;
    using (var memoryStream = new MemoryStream())
    using (var streamWriter = new StreamWriter(memoryStream))
    {
      streamWriter.WriteLine("TEST!");
      streamWriter.Flush();
      memoryStream.Seek(0, SeekOrigin.Begin);
      zipArchive.AddStream(ZipStorer.Compression.Deflate, $"TestFile_{i + 1:00}.txt", memoryStream, 
      DateTime.Now, string.Empty);
    }
  }
}

Random AccessDenied when renaming the folder in which the zip contents have been put into

Hello while using the class ZipStorer as a standalone, versions 3.4.0 (August 4, 2017) and also the current version in the repository, I run into a race condition.

In my application I do the following:

  1. open a zip file in a memory stream
  2. create the folder A in which the zip contents will be put
  3. open the memory stream with zipstorer and put the zip contents in A
  4. rename the folder A in B

The problem is that sometimes the step 4 fails with System.IO.IOExceptionAccess to the path 'A' is denied. Further information

  • by applying delayed retries the problem happens way less often
  • the problem happened less frequently with a zip with less files/smaller (I didn't isolate which was the case)

Here follows a proof of concept and the zip file with some content in it that I used to reproduce the issue.

public void AccessDeniedPoc()
{
	// preparation
	var cycles = 300;
	var releaseZipFpath = "C:\\fixtures\\tozip.zip";
	var playgroundFolderFpath = "C:\\fixtures\\playground";
	var extractFolderPath = Path.Combine(playgroundFolderFpath, "extarget");
	var moveTargetFolderPath = Path.Combine(playgroundFolderFpath, "mvtarget");
	
	
	for (int i = 0; i < cycles; i++)
	{
		if (Directory.Exists(extractFolderPath)) Directory.Delete(extractFolderPath, true);
		if (Directory.Exists(moveTargetFolderPath)) Directory.Delete(moveTargetFolderPath, true);
		Directory.CreateDirectory(extractFolderPath);
		MemoryStream memoryStream = new MemoryStream();
		using (FileStream fileStream = new FileStream(releaseZipFpath, FileMode.Open, FileAccess.Read))
		{
			fileStream.CopyTo(memoryStream);
		}
		using (var zip = ZipStorer.Open(memoryStream, FileAccess.Read))
		{
			List<ZipStorer.ZipFileEntry> dir = zip.ReadCentralDir();
			foreach (ZipStorer.ZipFileEntry entry in dir)
			{
				string currPath = Path.Combine(extractFolderPath, entry.FilenameInZip);

				if (currPath.EndsWith("/"))
				{
					Directory.CreateDirectory(currPath);
				}
				else
				{
					zip.ExtractFile(entry, currPath);
				}
			}
		}
		
		// moving folder
		try
		{
			Directory.Move(extractFolderPath, moveTargetFolderPath);
		}
		catch (Exception e)
		{
			Console.WriteLine("exception on " + i);
			throw;
		}
	}
}

Could you please take a look at it and help me go through it? Am I doing something wrong or is there a problem in ZipStorer? Thank you

tozip.zip

Stream from outside should not be closed

I'm trying to use this class in a small WCF project where I don't want to add another 3rd party lib.
I want to use the stream mode for packaging and then send the zip stream to another machine. Unfortunately the internal zipfilestream is closed and disposed within the Close() method. If I don't close the zip, then the EndRecord is not written. But when I close the zip then the underlying stream is closed.
My suggestion is to set a flag when a filename is provided for Open() or Create(). When this is set, then ZipFileStream.Dispose(); and ZipFileStream = null; in function Close() should be omitted.
I have a working solution here and can provide a pull request later.

Do not support Unity on Android

Unable to find MonoPosixHelper
DllNotFoundException: MonoPosixHelper
at System.IO.Compression.DeflateStream:CreateZStream
at System.IO.Compression.DeflateStream..ctor
at System.IO.Compression.DeflateStream..ctor
at System.IO.Compression.DeflateStream:.ctor
at System.IO.Compression.ZipStorer.ExtractFile (ZipFileEntry _zfe, System.String _filename)

So better to use SharpZipLib

Error encoding 437 .net core 3.1

Raise this error
No data is available for encoding 437. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.

as workaround i comment the lines
private static Encoding DefaultEncoding = Encoding.UTF8/* Encoding.GetEncoding(437)*/;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.