从对象直接创建Zip文件而无需进行磁盘IO

8
我正在编写一个REST API,它将接受一个JSON请求对象。该请求对象必须以JSON格式序列化到文件中;文件必须被压缩为ZIP文件,并将ZIP文件发布到另一个服务上,我需要反序列化ZIP文件。所有这些都是因为我要调用的服务期望我以ZIP文件形式发送数据。我想知道是否有一种方法可以避免磁盘I/O。是否有一种方法可以直接将对象转换为表示内存中ZIP内容的字节数组,而不是执行上述所有步骤?
注意:我更喜欢使用.NET框架库来完成这个(而不是外部库)。

如果可以不使用IO,就像你所要求的那样完成这个任务,我会非常感兴趣。 - gpullen
使用内置的ZipArchive更新了答案。 - user3473830
嘿 @Aadith,你觉得有没有找到任何有用的答案?如果是的话,请给作者一些赞赏并标记一个为已接受!谢谢。 - Yogster
当然,@Jorge,感谢您提供的解决方案和为所需解决方案铺平道路。 - Aadith Ramia
没问题!我很高兴你最终找到了方法 :-) - Yogster
4个回答

14

是的,完全可以在内存中创建zip文件,在这里使用SharpZip库进行示例(更新:在末尾添加了一个使用ZipArchive的示例):

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();
}

您可以使用Nuget命令PM> Install-Package SharpZipLib获取SharpZip

更新:

注意:我更喜欢使用 .NET Framework 库(而不是外部库)来实现此目的

这里是一个使用内置的System.IO.Compression.Dll中的ZipArchive的示例。

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);

}

你不能仅使用 System.IO.Compression.ZipArchive 而不安装 NuGet 吗? - Yogster
@Jorge,是的,这是可能的,它基于个人偏好,我会更新答案。 - user3473830
感谢 @user3473830,您修改后的解决方案符合我的要求。 - Aadith Ramia
你如何修改此代码以在zip归档文件中创建一个文件夹? - ElFik
@user3473830,感谢提供ZipArchive版本的代码。我正在尝试对依赖于ZipArchive的组件进行单元测试。已经花费了大约2个小时搜索解决方案,但是直到看到你的答案才找到了。 - Farhad Jabiyev

4
你可以使用GZipStream类和MemoryStream类。
一个快速的例子:
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();

当我尝试这样做时,zippedJsonBytes返回一个大小为0的数组。看起来有一些小问题..你能帮我解决吗? - Aadith Ramia
如果我没有错的话,OP 需要创建一个 Zip 容器,恐怕建议的解决方案只是使用 Gzip 算法压缩数据,而没有符合 Zip 容器格式。 - user3473830
谢谢@user3473830..那将是我的另一个问题..zip和gzip之间有什么区别? - Aadith Ramia
@Aadith,我的错,我忘记调用jsonStreamWriter.Flush(); 我已经编辑了这个例子。 - Yogster
1
@Aadith,如果你需要的是整个zip归档文件(而不是压缩数据),那么你可以像下面建议的那样使用ZipArchive类。MSDN中有一个使用FileStream的示例,但你也可以像我的示例一样使用MemoryStreams。 - Yogster
1
@Aadith,https://dev59.com/bGIi5IYBdhLWcg3w_AfV - user3473830

2

0

可以。你可以将它作为二进制流返回。根据语言,你可以使用特殊的库。客户端还需要这些库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接