【问题标题】:Downloading files as a zip result in corrupted zips in ASP.NET MVC将文件下载为 zip 会导致 ASP.NET MVC 中的 zip 损坏
【发布时间】:2017-09-21 19:03:14
【问题描述】:

我有一个托管大型 XML 文件存档的服务器,并且一个 API 在 zip 中检索请求的文件。如果我选择大约 11 个或更少的文件,则 zip 会很好地返回。如果我检索更多内容,我会在尝试打开 zip 时收到以下错误:

"Windows 无法打开该文件夹。压缩 (zipped) 文件夹是 无效。”

以下是创建 zip 的数据类和方法:

//Archive file containing filename and content as memory stream
public class ArchiveFile {
    public string FileName;
    public System.IO.MemoryStream FileContent;
}

//Method to retrieve archive files and zip them
public static System.IO.MemoryStream GetFilesAsZip (string[] arrFileNames) {
    MemoryStream zipStream  = null;
    using (zipStream = new MemoryStream()) {
        // Retrieve files using above method
        ArchiveFile[] retrievedFiles = GetFilesFromArchive(arrFileNames);
        // Initialize new ZipArchive on the return object's MemoryStream property
        using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Update, leaveOpen: true)) {
            // Write file entries into archive
            foreach (ArchiveFile dataFile in retrievedFiles) {
                if (dataFile.FileContent != null) {
                    // Create new ZipArchiveEntry with content
                    ZipArchiveEntry zipEntry = archive.CreateEntry(dataFile.FileName);
                    dataFile.FileContent.WriteTo(zipEntry.Open());
                }//end if
            } // end foreach
        } //end using
    } //end using
    return zipStream;
}//end method

//API to return content to user as an MVC File Content Result
[HttpGet]
public ActionResult DownloadFiles (string [] fileNames) {
    FileContentResult data = new FileContentResult(GetFiles(fileNames).GetBuffer(), “application/zip”) { FileDownloadName = “files.zip” };
    return data;
} //end method

写入内存流时,损坏可能与空间分配有关。我注意到我所有“成功”的 zip(11 个或更少的文件)的大小为 259 KB,但所有“不成功的”zip(超过 11 个文件)的大小为 517 KB,一些较大的尝试 zip 大小为 1034 KB。这些都是 258.5 KB 的倍数,这让我觉得太巧合了,特别是因为 11 个文件的 zip 会产生 259 KB 的 zip,但 12 个文件的 zip 会产生 517 KB 的 zip。

对它可能是什么有任何见解?

【问题讨论】:

  • 您能以某种方式共享 XML 文件吗?
  • 很遗憾我不能,它们包含私人信息。
  • 您没有正确使用 MemoryStream。而不是在使用之外新建 MemoryStream,新建一个字节数组并返回它。它的 zipStream.ToArray()
  • @Aaron.S 你介意扩展一下你的意思吗?
  • 我会重构你的代码,等等。您是否将有问题的文件发送到控制器,然后返回一个 zip 文件?还是只是文件名及其在服务器上的位置?

标签: c# asp.net asp.net-mvc zip


【解决方案1】:

ASP.Net Core API Controller returns corrupted excel file

在你的控制器中返回这个

return new FileResult("demo.zip", Path.Combine(sWebRootFolder, sFileName), "application/zip");

添加此代码

public class FileResult : ActionResult
    {
        public FileResult(string fileDownloadName, string filePath, string contentType)
        {
            FileDownloadName = fileDownloadName;
            FilePath = filePath;
            ContentType = contentType;
        }

        public string ContentType { get; private set; }
        public string FileDownloadName { get; private set; }
        public string FilePath { get; private set; }

        public async override Task ExecuteResultAsync(ActionContext context)
        {
            var response = context.HttpContext.Response;
            response.ContentType = ContentType;
            context.HttpContext.Response.Headers.Add("Content-Disposition", new[] { "attachment; filename=" + FileDownloadName });
            using (var fileStream = new FileStream(FilePath, FileMode.Open))
            {
                await fileStream.CopyToAsync(context.HttpContext.Response.Body);
            }
        }
    }

您的代码已重构

public byte[] GetFilesAsZip (string[] arrFileNames) {
    byte[] buffer = null;
    using (MemoryStream zipStream = new MemoryStream()) {
        // Retrieve files using above method
        ArchiveFile[] retrievedFiles = GetFilesFromArchive(arrFileNames);
        // Initialize new ZipArchive on the return object's MemoryStream property
        using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Update, leaveOpen: true)) {
            // Write file entries into archive
            foreach (ArchiveFile dataFile in retrievedFiles) {
                if (dataFile.FileContent != null) {
                    // Create new ZipArchiveEntry with content
                    ZipArchiveEntry zipEntry = archive.CreateEntry(dataFile.FileName);
                    dataFile.FileContent.WriteTo(zipEntry.Open());
                }//end if
            } // end foreach
        } //end using
        buffer = zipStream.ToArray();
    } //end using
    return buffer;
}//end method

你应该可以把它改成

FileContentResult data = new FileContentResult(GetFiles(fileNames), “application/zip”) { FileDownloadName = “files.zip” };

【讨论】:

    【解决方案2】:

    我过去在return File(fileLocation, "application/zip", fileName); 中取得了成功,其中fileLocation 是文件夹的路径,fileName 是实际文件夹的名称。您可以在您的 ActionResult 中执行此操作。

    【讨论】:

    • 是的,我为另一个问题整理了一个完整的例子
    • 恐怕这对我不起作用。 zip 不会存储在磁盘上,因为它们是根据请求的任何文件组合动态生成的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多