creating a zip file from an object directly without disk IO
Asked Answered
S

4

8

I am writing a REST API which will take in a JSON request object. The request object will have to be serialized to a file in JSON format; the file has to be compressed into a zip file and the ZIP file has to be posted to another service, for which I would have to deserialize the ZIP file. All this because the service I have to call expects me to post data as ZIP file. I am trying to see if I can avoid disk IO. Is there a way to directly convert the object into a byte array representing ZIP content in-memory instead of all the above steps?

Note : I'd prefer accomplishing this using .net framework libraries (as against external libraries)

Smoothtongued answered 15/12, 2014 at 11:31 Comment(5)
I'd be very interested if this can be accomplished without the use of IO as you askedHackler
updated the answer using built-in ZipArchive.Agar
Hey @Aadith, did you find any of the answers useful? If so please give the author some kudos and mark one as accepted! Thanks.Bleareyed
Sure @Jorge .. thanks for your solution and paving the way to the required solutionSmoothtongued
No problem! I am glad you eventually found the way :-)Bleareyed
A
14

Yes, it is possible to create a zip file completely on memory, here is an example using SharpZip Library (Update: A sample using ZipArchive added at the end):

public static void Main()
{
    var fileContent = Encoding.UTF8.GetBytes(
        @"{
            ""fruit"":""apple"",
            ""taste"":""yummy""
          }"
        );


    var zipStream = new MemoryStream();
    var zip = new ZipOutputStream(zipStream);

    AddEntry("file0.json", fileContent, zip); //first file
    AddEntry("file1.json", fileContent, zip); //second file (with same content)

    zip.Close();

    //only for testing to see if the zip file is valid!
    File.WriteAllBytes("test.zip", zipStream.ToArray());
}

private static void AddEntry(string fileName, byte[] fileContent, ZipOutputStream zip)
{
    var zipEntry = new ZipEntry(fileName) {DateTime = DateTime.Now, Size = fileContent.Length};
    zip.PutNextEntry(zipEntry);
    zip.Write(fileContent, 0, fileContent.Length);
    zip.CloseEntry();
}

You can obtain SharpZip using Nuget command PM> Install-Package SharpZipLib

Update:

Note : I'd prefer accomplishing this using .net framework libraries (as against external libraries)

Here is an example using Built-in ZipArchive from System.IO.Compression.Dll

public static void Main()
{
    var fileContent = Encoding.UTF8.GetBytes(
        @"{
            ""fruit"":""apple"",
            ""taste"":""yummy""
          }"
        );

    var zipContent = new MemoryStream();
    var archive = new ZipArchive(zipContent, ZipArchiveMode.Create);

    AddEntry("file1.json",fileContent,archive);
    AddEntry("file2.json",fileContent,archive); //second file (same content)

    archive.Dispose();

    File.WriteAllBytes("testa.zip",zipContent.ToArray());
}


private static void AddEntry(string fileName, byte[] fileContent,ZipArchive archive)
{
    var entry = archive.CreateEntry(fileName);
    using (var stream = entry.Open())
        stream.Write(fileContent, 0, fileContent.Length);

}
Agar answered 15/12, 2014 at 12:16 Comment(5)
Couldn't you just use System.IO.Compression.ZipArchive instead of installing a NuGet?Bleareyed
@Jorge, yes it's possible, it was based on personal preferences, I'll update the answer.Agar
Thanks @Agar your revised solution suits my requirementSmoothtongued
How can you modify this to create a folder within the zip archive?Smallman
@Agar Thanks for ZipArchive version of the code. Trying to do unit test to the component which has dependency to ZipArchive. It is about 2 hours I am searching the solution, but wasn't able to find until saw your answer.Astrosphere
B
4

You could use the GZipStream class along with MemoryStream.

A quick example:

using System.IO;
using System.IO.Compression;

//Put JSON into a MemoryStream
var theJson = "Your JSON Here";
var jsonStream = new MemoryStream();
var jsonStreamWriter = new StreamWriter(jsonStream);
jsonStreamWriter.Write(theJson);
jsonStreamWriter.Flush();

//Reset stream so it points to the beginning of the JSON
jsonStream.Seek(0, System.IO.SeekOrigin.Begin);

//Create stream to hold your zipped JSON
var zippedStream = new MemoryStream();

//Zip JSON and put it in zippedStream via compressionStream.
var compressionStream = new GZipStream(zippedStream, CompressionLevel.Optimal);
jsonStream.CopyTo(compressionStream);

//Reset zipped stream to point at the beginning of data
zippedStream.Seek(0, SeekOrigin.Begin);

//Get ByteArray with zipped JSON
var zippedJsonBytes = zippedStream.ToArray();
Bleareyed answered 15/12, 2014 at 11:34 Comment(6)
When I try this, I get a 0-size array for zippedJsonBytes. Looks like theres a minor snag somewhere..could you pls help me outSmoothtongued
If I'm not mistaken, OP needs to create a Zip container, I'm afraid the proposed solution only compress data with Gzip algorithm and does not conform Zip container format.Agar
Thanks @Agar ..That was going to be my other question..whats the difference between zip and gzip?Smoothtongued
@Aadith, my bad, I forgot to call jsonStreamWriter.Flush(); I have edited the example.Bleareyed
@Aadith, if what you need is a whole zip archive (as opposed to Zipped data) then you can use the ZipArchive class as suggested below. There is an example in MSDN using FileStreams, but you could also use MemoryStreams as in my example.Bleareyed
@Aadith, #20762594Agar
F
2

You should try the ZipArchive Class streaming to a MemoryStream Class

Filagree answered 15/12, 2014 at 11:36 Comment(0)
W
0

Yes. You can return it as a binary stream. Depending on the language, you can use special libraries. You will also need libraries on the client.

Wampumpeag answered 15/12, 2014 at 11:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.