Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate size of zip file with compression level 0

Tags:

c#

zip

I am writing functionality for our web server which should download several files from other servers, and return them as a zip archive without compression.

How can I determine the final size of the ZIP archive if I know the sizes of all downloaded files?

This is the code which I am working on for the moment. The commented line caused corruption of the ZIP archive.

public void Download()
{
    var urls = Request.Headers["URLS"].Split(';');
    Task<WebResponse>[] responseTasks = urls
        .Select(it =>
        {
            var request = WebRequest.Create(it);
            return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse(null, null), request.EndGetResponse);
        })
        .ToArray();

    Task.WaitAll(responseTasks);

    var webResponses = responseTasks.Where(it => it.Exception == null).Select(it => it.Result);

    var totalSize = webResponses.Sum(it => it.ContentLength + 32);

    Response.ContentType = "application/zip";
    Response.CacheControl = "Private";
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    // Response.AddHeader("Content-Length", totalSize.ToString(CultureInfo.InvariantCulture));

    var sortedResponses = webResponses.OrderBy(it => it.ContentLength);

    var buffer = new byte[32 * 1024];

    using (var zipOutput = new ZipOutputStream(Response.OutputStream))
    {
        zipOutput.SetLevel(0);

        foreach (var response in sortedResponses)
        {
            var dataStream = response.GetResponseStream();

            var ze = new ZipEntry(Guid.NewGuid().ToString() + ".jpg");
            zipOutput.PutNextEntry(ze);

            int read;
            while ((read = dataStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                zipOutput.Write(buffer, 0, read);
                Response.Flush();
            }

            if (!Response.IsClientConnected)
            {
                break;
            }
        }

        zipOutput.Finish();
    }

    Response.Flush();
    Response.End();
}
like image 909
v00d00 Avatar asked Dec 27 '22 23:12

v00d00


1 Answers

I had the same problem and reading the ZIP-spec came up with the following solution:

zip_size = num_of_files * (30 + 16 + 46) + 2 * total_length_of_filenames + total_size_of_files + 22

with:

  • 30: Fixed part of the Local file header
  • 16: Optional: Size of the Data descriptor
  • 46: Fixed part of the Central directory file header
  • 22: Fixed part of the End of central directory record (EOCD)

This however does not account for comments on files and the zip in total. The compression is store (level 0).

This works for the ZIP-implementation i've written. As nickolay-olshevsky pointed out, other compressors might do things a little different.

like image 174
derflocki Avatar answered Jan 14 '23 18:01

derflocki