【问题标题】:When creating a Zip file in memory, why do I have to parse the MemoryStream for the Zip to be created correctly?在内存中创建 Zip 文件时,为什么我必须解析 MemoryStream 才能正确创建 Zip?
【发布时间】:2018-10-21 09:10:02
【问题描述】:

我在这里查看了其他问题,但没有人回答我为什么不能使用现有的 MemoryStream 来创建 zip。

这是获取包含文件的压缩文件夹的有效 API 方法。这些文件已经存在于服务器上,该方法只是查找那里的位置,并在内存中检索所有文件,并向用户发送一个 HttpResponMessage 对象,其中包含所有文件的压缩文件夹。然而,下面有一行代码似乎没有意义。我必须将 MemoryStream 解析为字节数组,然后再解析回 MemoryStream,以便正确创建 Zip 并将其发送回。这似乎是错误的,但我不知道如何纠正它。 (见下文“//Works:”和“//Does Not work:”)

public HttpResponseMessage GetEvalByCompany([FromUri]Guid id, [FromUri] int year)
{
        try
        {
            string companyName = _portalDB.Companies.FirstOrDefault(c => c.Id == id).FirmName;
            //Get all evaluation file paths for a given company for a given year
            var files = _evaluationDB.Evaluations.Where(j => j.CompanyId == id).Select(j => @"C:\Path\To\File" + j.RelativePath.Replace("~", @"").Replace(@"/", @"\")).Distinct().ToList<string>();

            using (var outStream = new MemoryStream())
            {
                using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
                {
                    foreach (var record in files)
                    {
                        var fileInArchive = archive.CreateEntryFromFile(record, Path.GetFileName(record), CompressionLevel.Optimal);
                    }
                }

                // Create a response
                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);



                //Works:
                response.Content = new StreamContent(new MemoryStream(outStream.ToArray())); //Why does this need to happen?
                //Does Not work:
                //response.Content = new StreamContent(outStream); //Why doesn't this work?



                // Add appropriate headers to make browser recognize response as a download
                response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
                response.Content.Headers.ContentDisposition.FileName = "Evaluations for Company - " + companyName + ".zip";
                response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
                return response;
            }
        }
        catch (Exception ex)
        {
            return ErrorHandler.HandleException(ex, Request);
        }
}

根据我收到的答案,我能够修复代码,因此它不必不必要地解析流。这是为未来开发人员简单地寻找解决方案的更新代码。删除 using 似乎也不会导致任何 GC 问题。我找到了一个链接来更好地解释这一点。 (MemoryStream.Close() or MemoryStream.Dispose())

public HttpResponseMessage GetEvalByCompany([FromUri]Guid id, [FromUri] int year)
{
        try
        {
            string companyName = _portalDB.Companies.FirstOrDefault(c => c.Id == id).FirmName;
            //Get all evaluation file paths for a given company for a given year
            var files = _evaluationDB.Evaluations.Where(j => j.CompanyId == id).Select(j => @"C:\Path\To\File" + j.RelativePath.Replace("~", @"").Replace(@"/", @"\")).Distinct().ToList<string>();

            var outStream = new MemoryStream();
            using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
            {
                foreach (var record in files)
                {
                    var fileInArchive = archive.CreateEntryFromFile(record, Path.GetFileName(record), CompressionLevel.Optimal);
                }
            }

            // Create a response
            HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);

            //Updated code
            outStream.Seek(0, SeekOrigin.Begin);
            response.Content = new StreamContent(outStream); //works now because outStream isn't in a using block


            // Add appropriate headers to make browser recognize response as a download
            response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = "Evaluations for Company - " + companyName + ".zip";
            response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
            return response;

        }
        catch (Exception ex)
        {
            return ErrorHandler.HandleException(ex, Request);
        }
}

【问题讨论】:

  • 将流寻回零应该不够吗?
  • 当我使用 .Seek(0, SeekOrigin.Begin) 或 .Position = 0 时,我得到与“不起作用”代码相同的结果。浏览器收到“成功”的 200 响应,标题确认内容中的文档但状态为失败且未下载 zip。

标签: c# .net


【解决方案1】:

删除

using (var outStream = new MemoryStream()),

只离开:

var outStream = new MemoryStream();

然后:

outStream.Seek(0, SeekOrigin.Begin)
response.Content = new StreamContent(outStream);

【讨论】:

  • 这行得通。使用是问题所在,基于此链接stackoverflow.com/questions/4274590/…,我似乎并不真的需要它,因为 Stream 的处理似乎没有任何“现实世界影响”。我认为用括号括起来的任何东西都可以使用实现的流,但在这种情况下,至少在返回时,似乎有一个警告。
【解决方案2】:

这里的问题是您的原始 MemoryStream 在使用之前就被处理掉了。 StreamContent 保留对已处置对象的引用。您需要删除 outStream 的 using 块,并以某种方式安排稍后将其处理掉。

【讨论】:

  • 赞成,你的回答,因为它确实有助于解释问题,但 ipavlu 有解决问题的实际代码。两者都有帮助。一是解决问题,一是理解。
猜你喜欢
  • 2016-09-23
  • 1970-01-01
  • 2015-09-11
  • 2014-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-18
相关资源
最近更新 更多