【问题标题】:Creating Zip Archive in memory, and returning it from a web api在内存中创建 Zip 存档,并从 Web api 返回
【发布时间】:2018-06-06 12:22:41
【问题描述】:

所以我正在尝试创建一个 zip 存档并从我的 web api 中返回它。控制器是从 Angular 2 站点调用的。目前已创建 zip 文件,但当我打开它时,我收到一条无效消息。最初我在 using 语句中有流,但必须更改它们,因为它们在请求完成之前被处置。

我需要创建 zip 文件,将 csv 文件添加到其内容中。然后返回 zip 文件。但是 zip 文件总是无效的。我已经阅读了需要处理 zip 存档才能写入其内容,但是我不确定实现此功能的最佳方法是什么。感谢您的任何指导。

public async Task<IHttpActionResult> ExportReport(int id, ReportModel report)
    {
        try
        {
            var result = await ReportGenerationService.ExportReportForId(id, report.Page, report.PageSize, report.SortField, report.SortDir, report.SearchTerm, report.StartDate, report.EndDate, report.UserId, report.TeamId, report.SelectedDateItem);
            if (result != null)
            {

                var compressedFileStream = new MemoryStream();

                var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Update, false);

                var zipEntry = zipArchive.CreateEntry("textExport.csv");
                var origionalFileSteam = new MemoryStream(result.ExportToBytes());
                var zipEntryStream = zipEntry.Open();
                origionalFileSteam.CopyTo(zipEntryStream);


                var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(compressedFileStream)};
                response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                {
                    FileName = "Export.zip"
                };

                var t = compressedFileStream.CanRead;
                return ResponseMessage(response);

            }

            return NotFound();
        }
        catch (Exception ex)
        {
            return InternalServerError(ex);
        }
    }

响应 using 语句:

在某一时刻,我将所有内容都包含在 using 语句中,但响应会失败,因为流已经被释放。你可以在下面看到这个。

public async Task<IHttpActionResult> ExportReport(int id, ReportModel report)
    {
        try
        {
            var result = await ReportGenerationService.ExportReportForId(id, report.Page, report.PageSize, report.SortField, report.SortDir, report.SearchTerm, report.StartDate, report.EndDate, report.UserId, report.TeamId, report.SelectedDateItem);
            if (result != null)
            {

                var compressedFileStream = new MemoryStream();
                var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Update, false);

                        //Create a zip entry for each attachment
                    var zipEntry = zipArchive.CreateEntry("textExport.csv");

                    //Get the stream of the attachment
                    using (var originalFileStream = new MemoryStream(result.ExportToBytes()))
                    using (var zipEntryStream = zipEntry.Open()) {
                        //Copy the attachment stream to the zip entry stream
                        originalFileStream.CopyTo(zipEntryStream);
                    }
                    compressedFileStream.Position = 0;

                    var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(compressedFileStream)};
                    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                    response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                    {
                        FileName = "Export.zip"
                    };

                    return ResponseMessage(response);
            }

            return NotFound();
        }
        catch (Exception ex)
        {
            return InternalServerError(ex);
        }
    }

【问题讨论】:

  • 能否使用浏览器调用web-api方法,并将结果保存到文件中?并查看它是合法文件还是非法文件....又名,从等式中取出角度。

标签: c# .net asp.net-web-api2 memorystream


【解决方案1】:

您缺少一些using(){} 块。

确保以正确的顺序关闭 originalFileSteam、zipEntryStream 和 zipArchive。

为了确定,重置 memoryStream。我不知道这是否需要,但它不会伤害。

//using (var compressedFileStream = new MemoryStream())
var compressedFileStream = new MemoryStream();

using (var zipArchive = new ZipArchive(...)) 
{
        //Create a zip entry for each attachment
        var zipEntry = zipArchive.CreateEntry("textExport.csv");

        //Get the stream of the attachment
        using (var originalFileStream = new MemoryStream(result.ExportToBytes()))
        using (var zipEntryStream = zipEntry.Open()) 
        {
            //Copy the attachment stream to the zip entry stream
            originalFileStream.CopyTo(zipEntryStream);
        }
}

//compressedFileStream .Position = 0;
var responseBytes =new MemoryStream(compressedFileStream.ToArray());

var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(responseBytes )};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

return ResponseMessage(response);

【讨论】:

  • 我已将 using 语句添加到问题中。我之前尝试过,但出现更多错误,因为当我们发送响应时,流已经被释放。
  • 是的,我没有提到 memStream。它应该保持开放或重新开放。其他用途应该在发送响应之前关闭。
  • 我编辑了,我把缩进弄得有点乱,这样你就可以看到哪里出错了。在发送前关闭 zipArchive。
  • 我看到我在 zip 存档的 using 语句中得到了响应。这里唯一的问题是,当我们尝试设置位置时,compressedFileStream 已关闭。
  • 你已经把它们放进去,压缩文件返回但仍然无效
【解决方案2】:

释放 zip 存档时正在释放内存流。

您应该处理存档以强制将其内容写入其底层内存流,但请注意以下事项

ZipArchive.Dispose()

除非您使用ZipArchive(Stream, ZipArchiveMode, Boolean) 构造函数重载构造对象并将其leaveOpen 参数设置为true,否则所有底层流都将关闭并且不再可用于后续写入操作。

当您使用完此ZipArchive 实例后,调用Dispose() 以释放此实例使用的所有资源。您应该消除对该ZipArchive 实例的进一步引用,以便垃圾收集器可以回收该实例的内存,而不是保持它处于活动状态以进行终结。

而且由于您想继续使用内存流,因此您需要确保它保持打开状态并且流指针已重置,以便可以从头开始读取。

public async Task<IHttpActionResult> ExportReport(int id, ReportModel report) {
    try {
        var result = await ReportGenerationService.ExportReportForId(id, report.Page, report.PageSize, report.SortField, report.SortDir, report.SearchTerm, report.StartDate, report.EndDate, report.UserId, report.TeamId, report.SelectedDateItem);
        if (result != null) {
            var compressedFileStream = new MemoryStream();
            using(var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create, 
                leaveOpen: true)) { //<--This is important to keep stream open
                //Create a zip entry for each attachment
                var zipEntry = zipArchive.CreateEntry("textExport.csv");
                //Get the stream of the attachment
                using (var originalFileStream = new MemoryStream(result.ExportToBytes()))
                using (var zipEntryStream = zipEntry.Open()) {
                    //Copy the attachment stream to the zip entry stream
                    await originalFileStream.CopyToAsync(zipEntryStream);
                }
            }// disposal of archive will force data to be written/flushed to memory stream.
            compressedFileStream.Position = 0;//reset memory stream position.
            
            var response = new HttpResponseMessage(HttpStatusCode.OK) {
                Content = new StreamContent(compressedFileStream)
            };
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
                FileName = "Export.zip"
            };
            return ResponseMessage(response);
        }
        return NotFound();
    } catch (Exception ex) {
        return InternalServerError(ex);
    }
}

【讨论】:

  • 感谢您对此的帮助,我希望理想情况下将两个答案都标记为正确。让 ziparchive 打开有很大帮助。再次感谢您。
  • @Tony_89 不用担心。另一个答案非常有价值,可以帮助您完成大部分工作。
猜你喜欢
  • 2021-10-20
  • 2020-08-30
  • 2022-06-24
  • 2013-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多