代码之家  ›  专栏  ›  技术社区  ›  Daniel García Rubio

从字节(具有任意编码的文本)在内存中创建zip文件

  •  6
  • Daniel García Rubio  · 技术社区  · 7 年前

    我正在开发的应用程序需要将xml文件压缩成zip文件,并通过http请求将其发送到web服务。因为我不需要保存zip文件,所以我只是在内存中执行压缩。web服务拒绝了我的请求,因为zip文件显然格式不正确。

    我知道有一个解决办法 this question 它工作得很好,但它使用 StreamWriter . 我的问题是 StreamWriter 需要编码或假定 UTF-8 ,我不需要知道xml文件的编码。我只需要从这些文件中读取字节,并将它们存储在zip文件中,无论它们使用何种编码。

    所以,要明确的是,这个问题与编码无关,因为我不需要将字节转换为文本或oposite。我只需要压缩 byte[] .

    我正在使用下一个代码来测试zip文件的格式有多不正确:

    static void Main(string[] args)
    {
        Encoding encoding = Encoding.GetEncoding("ISO-8859-1");
    
        string xmlDeclaration = "<?xml version=\"1.0\" encoding=\"" + encoding.WebName.ToUpperInvariant() + "\"?>";
        string xmlBody = "<Test>ª!\"·$%/()=?¿\\|@#~€¬'¡º</Test>";
        string xmlContent = xmlDeclaration + xmlBody;
        byte[] bytes = encoding.GetBytes(xmlContent);
        string fileName = "test.xml";
        string zipPath = @"C:\Users\dgarcia\test.zip";
    
        Test(bytes, fileName, zipPath);
    }
    
    static void Test(byte[] bytes, string fileName, string zipPath)
    {
        byte[] zipBytes;
    
        using (var memoryStream = new MemoryStream())
        using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: false))
        {
            var zipEntry = zipArchive.CreateEntry(fileName);
            using (Stream entryStream = zipEntry.Open())
            {
                entryStream.Write(bytes, 0, bytes.Length);
            }
    
            //Edit: as the accepted answer states, the problem is here, because i'm reading from the memoryStream before disposing the zipArchive.
            zipBytes = memoryStream.ToArray();
        }
    
        using (var fileStream = new FileStream(zipPath, FileMode.OpenOrCreate))
        {
            fileStream.Write(zipBytes, 0, zipBytes.Length);
        }
    }
    

    如果我试图打开该文件,则会出现“意外的文件结尾”错误。很明显,web服务正确地报告了格式错误的zip文件。到目前为止,我已经尝试了:

    • 冲洗 entryStream .
    • 关闭 入口流 .
    • 冲洗和关闭 入口流 .

    请注意,如果我打开 zipArchive 直接从 fileStream zip文件的格式没有错误。然而 文件流 只是作为测试,我需要在内存中创建zip文件。

    2 回复  |  直到 7 年前
        1
  •  10
  •   Evk    7 年前

    您正在尝试从中获取字节 MemoryStream 太早了, ZipArchive 还没有全部写完。相反,请这样做:

    using (var memoryStream = new MemoryStream()) {
        // note "leaveOpen" true, to not dispose memoryStream too early
        using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: true)) {
            var zipEntry = zipArchive.CreateEntry(fileName);
            using (Stream entryStream = zipEntry.Open()) {
                entryStream.Write(bytes, 0, bytes.Length);
            }                    
        }
        // now, after zipArchive is disposed - all is written to memory stream
        zipBytes = memoryStream.ToArray();
    }
    
        2
  •  -1
  •   Franck    7 年前

    如果使用内存流加载文本,则可以控制编码类型,它可以跨WCF服务工作。这是我目前正在使用的实现,它适用于我的WCF服务

        private byte[] Zip(string text)
        {
            var bytes = Encoding.UTF8.GetBytes(text);
    
            using (var msi = new MemoryStream(bytes))
            using (var mso = new MemoryStream())
            {
                using (var gs = new GZipStream(mso, CompressionMode.Compress))
                {
                    CopyTo(msi, gs);
                }
    
                return mso.ToArray();
            }
        }
    
        private string Unzip(byte[] bytes)
        {
            using (var msi = new MemoryStream(bytes))
            using (var mso = new MemoryStream())
            {
                using (var gs = new GZipStream(msi, CompressionMode.Decompress))
                {
                    CopyTo(gs, mso);
                }
    
                return Encoding.UTF8.GetString(mso.ToArray());
            }
        }