首页 > 解决方案 > 无法打开从 System.IO.Compression 命名空间创建的 zip 文件

问题描述

我正在尝试压缩不同数量的文件,以便可以将一个 zip 文件夹提供给用户,而不必单击多个锚标记。我在 asp.net core 3.1 中使用 System.IO.Compression 命名空间来创建 zip 文件夹。

这是我用来创建 Zip 文件夹的代码。

        public IActionResult DownloadPartFiles(string[] fileLocations, string[] fileNames)
        {
            List<InMemoryFile> files = new List<InMemoryFile>();
            for (int i = 0; i < fileNames.Length; i++)
            {
                InMemoryFile inMemoryFile = GetInMemoryFile(fileLocations[i], fileNames[i]).Result;
                files.Add(inMemoryFile);
            }
            byte[] archiveFile;
            using (MemoryStream archiveStream = new MemoryStream())
            {
                using (ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
                {
                    foreach (InMemoryFile file in files)
                    {
                        ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Fastest);
                        using (Stream zipStream = zipArchiveEntry.Open())
                        {
                            zipStream.Write(file.Content, 0, file.Content.Length);
                            zipStream.Close();
                        }
                    }
                    archiveStream.Position = 0;
                }
                archiveFile = archiveStream.ToArray();
            }

            return File(archiveFile, "application/octet-stream");
        }

我要压缩的文件是远程存储的,所以我用这段代码来抓取它们。InMemoryFile 是一个将文件名和文件字节组合在一起的类。

        private async Task<InMemoryFile> GetInMemoryFile(string fileLocation, string fileName)
        {
            InMemoryFile file;
            using (HttpClient client = new HttpClient())
            using (HttpResponseMessage response = await client.GetAsync(fileLocation))
            {
                byte[] fileContent = await response.Content.ReadAsByteArrayAsync();
                file = new InMemoryFile(fileName, fileContent);
            }
            return file;
        }

使用 Ajax 调用 DownloadPartFiles 方法。我使用 javascript 获取文件的远程路径及其各自的名称,并将它们传递给 Ajax 调用。

function downloadAllFiles() {
    let partTable = document.getElementById("partTable");
    let linkElements = partTable.getElementsByTagName('a');
    let urls = [];
    for (let i = 0; i < linkElements.length; i++) {
        urls.push(linkElements[i].href);
    }

    if (urls.length != 0) {

        var fileNames = [];
        for (let i = 0; i < linkElements.length; i++) {
            fileNames.push(linkElements[i].innerText);
        }

        $.ajax({
            type: "POST",
            url: "/WebOrder/DownloadPartFiles/",
            data: { 'fileLocations': urls, 'fileNames': fileNames },
            success: function (response) {
                var blob = new Blob([response], { type: "application/zip" });
                var link = document.createElement('a');
                link.href = window.URL.createObjectURL(blob);
                link.download = "PartFiles.zip";
                link.click();
                window.URL.revokeObjectURL(blob);
            },
            failure: function (response) {
                alert(response.responseText);
            },
            error: function (response) {
                alert(response.responseText);
            }
        });
    }
}

现在我一直遇到的问题是我无法在 Windows 10 中打开 zip 文件夹。每次我尝试使用 Windows 或 7-zip 打开 zip 文件夹时,都会收到一条错误消息,指出文件夹无法打开或文件夹无效。我试过在 stackoverflow 上查看各种类似的问题,即使用 System.IO.Compression 创建后的 zip 文件无效,但仍然无法弄清楚这是为什么。

会不会是编码?我发现 Ajax 期望它的响应被编码为 UTF-8,当我使用带有 UTF-8 的 notepad++ 查看 zip 文件时,我看到有指示损坏的字符。

对此的任何想法都会有所帮助。如果需要更多信息,请告诉我。

如果需要其中一个损坏的 zip 文件,我也可以提供。

编辑:

我已经改变了我在 javascript 中接收字节数组的方法。我正在使用 XMLHttpRequest 来接收字节数组。

        var parameters = {};
        parameters.FileLocations = urls;
        parameters.FileNames = fileNames;

        var xmlhttp = new XMLHttpRequest();
        xmlhttp.open("POST", "/WebOrder/DownloadPartFiles/", true);
        xmlhttp.setRequestHeader("Content-Type", "application/json");
        xmlhttp.responseType = "arraybuffer";

        xmlhttp.onload = function (oEvent) {
            var arrayBuffer = xmlhttp.response;
            if (arrayBuffer) {
                var byteArray = new Uint8Array(arrayBuffer);
                var blob = new Blob([byteArray], { type: "application/zip" });
                var link = document.createElement('a');
                link.href = window.URL.createObjectURL(blob);
                link.download = "PartFiles.zip";
                link.click();
                window.URL.revokeObjectURL(blob);
            }
        }

        xmlhttp.send(JSON.stringify(parameters));

根据我的阅读,Ajax 不是接收字节数组和二进制数据的最佳选择。使用这种方法,我可以使用 7-zip 打开一个 zip 文件,但不能打开 Windows,但是,存档中的一个文件显示为 0KB 大小并且无法打开。存档中的其他三个文件都很好。但是,其他具有不同文件的 zip 文件夹根本无法打开。

标签: javascriptajaxasp.net-coresystem.io.compression

解决方案


一段时间后,我发现了一个能够解决我的问题的帖子,从字节创建 zip 文件 []

从那篇文章来看,这是我用来创建一个包含文件的 zip 文件夹的修改后的方法。

        public IActionResult DownloadPartFiles([FromBody] FileRequestParameters parameters)
        {
            List<InMemoryFile> files = new List<InMemoryFile>();
            for (int i = 0; i < parameters.FileNames.Length; i++)
            {
                InMemoryFile inMemoryFile = GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i]).Result;
                files.Add(inMemoryFile);
            }
            byte[] archiveFile = null;
            using (MemoryStream archiveStream = new MemoryStream())
            {
                using (ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
                {
                    foreach (InMemoryFile file in files)
                    {
                        ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Optimal);
                        using (MemoryStream originalFileStream = new MemoryStream(file.Content))
                        using (Stream zipStream = zipArchiveEntry.Open())
                        {
                            originalFileStream.CopyTo(zipStream);
                        }
                    }
                }
                archiveFile = archiveStream.ToArray();
            }
            return File(archiveFile, "application/octet-stream");
        }

我仍然不知道为什么以前的方法会出现问题,所以如果将来有人知道答案,我很想知道。


推荐阅读