January 2, 2006

SharpZipLib: Adding Files To A ZIP File

I had several visitors to my site in December looking for information on how to add files to a ZIP file, including information on how to add in path information.

Here is the code I use to write a file to a ZIP file:

    Private ZipToWrite As ZipOutputStream
Private Crc As New Crc32

Private Sub CreateZip(ByVal fname As String)
ZipToWrite = New ZipOutputStream(File.Create(fname))
ZipToWrite.SetLevel(9) ' 9 = maximum compression
End Sub

Private Sub CloseZip()
ZipToWrite.Finish()
ZipToWrite.Close()
End Sub

Private Sub WriteFileToZip(ByVal fname As String, ByVal basepath As String)
Dim strmFile As FileStream = File.OpenRead(fname)
Dim abyBuffer(strmFile.Length - 1) As Byte

strmFile.Read(abyBuffer, 0, abyBuffer.Length)
Dim objZipEntry As ZipEntry
If basepath.Length <> 0 Then
objZipEntry = New ZipEntry(fname.Replace(basepath, String.Empty).ToLower)
Else
objZipEntry = New ZipEntry(fname.ToLower)
End If

objZipEntry.DateTime = DateTime.Now
objZipEntry.Size = strmFile.Length
strmFile.Close()
Crc.Reset()
Crc.Update(abyBuffer)
objZipEntry.Crc = Crc.Value
ZipToWrite.PutNextEntry(objZipEntry)
ZipToWrite.Write(abyBuffer, 0, abyBuffer.Length)
End Sub

The purpose of the basepath parameter is so that you can ZIP up a folder and have the folder name magically go away.

Mind you, some ZIP utilities, when unzipping multiple folders, expect a directory entry for that folder...and if they don't find the directory entry, they'll crash out or say that your ZIP file is corrupt. In that case, before you add the first file for a subdirectory, add in a 0-byte file with this sub...
    Private Sub WriteZeroByteFileToZip(ByVal fname As String, ByVal basepath As String)
Dim abyBuffer(0) As Byte

Dim objZipEntry As ZipEntry
If basepath.Length <> 0 Then
objZipEntry = New ZipEntry(fname.Replace(basepath, String.Empty).ToLower & "\")
Else
objZipEntry = New ZipEntry(fname.ToLower & "\")
End If

objZipEntry.DateTime = DateTime.Now
objZipEntry.Size = 0
Crc.Reset()
Crc.Update(0)
objZipEntry.Crc = Crc.Value
ZipToWrite.PutNextEntry(objZipEntry)
End Sub

I hope that helps.

6 comments:

Milo said...

This sounds good, but do you also have sample code on how to add a file to an existing zip file?

BryanThrasher said...

Thanks for the posting. Here's a C# translation:

private ZipOutputStream ZipToWrite;
private Crc32 Crc = new Crc32();


private void CreateZip(string ZipFilename)
{
ZipToWrite = new ZipOutputStream(File.Create(ZipFilename));
ZipToWrite.SetLevel(9); // 9 = maximum compression
}

private void CloseZip()
{
ZipToWrite.Finish();
ZipToWrite.Close();
}

private void WriteFileToZip(string Filename, string BasePath)
{
FileStream strmFile = File.OpenRead(Filename);
byte[] abyBuffer = new byte[strmFile.Length - 1];

strmFile.Read(abyBuffer, 0, abyBuffer.Length);
ZipEntry objZipEntry;
if (BasePath.Length > 0)
objZipEntry = new ZipEntry(Filename.Replace(BasePath, String.Empty).ToLower);
else
objZipEntry = new ZipEntry(Filename.ToLower);

objZipEntry.DateTime = DateTime.Now;
objZipEntry.Size = strmFile.Length;
strmFile.Close();
Crc.Reset();
Crc.Update(abyBuffer);
objZipEntry.Crc = Crc.Value;
ZipToWrite.PutNextEntry(objZipEntry);
ZipToWrite.Write(abyBuffer, 0, abyBuffer.Length);
}

I found an implementation of Crc32 here: http://staceyw.spaces.live.com/blog/cns!F4A38E96E598161E!402.entry

DennisK said...

Thanks! You helped us fix an issue with the windows zip utility because we weren't adding the empty folder entries to the zip file.

Alex said...
This comment has been removed by a blog administrator.
Alex said...
This comment has been removed by a blog administrator.
Mark said...

I overloaded your method to split the filename and path from the fullname. It calls your other method:

private void WriteFileToZip(string FilePathAndName)
{
string BasePath = System.IO.Path.GetDirectoryName(FilePathAndName).ToString();
string FileName = System.IO.Path.GetFileName(FilePathAndName).ToString();

WriteFileToZip(Filename, BasePath);
}