简体   繁体   中英

Downloading files as a zip result in corrupted zips in ASP.NET MVC

I have a server hosting a large XML file archive and an API retrieves requested files inside a zip. If I select around 11 or less files, the zip returns just fine. If I retrieve more, I receive the following error when attempting to open the zip:

"Windows cannot open the folder. The Compressed (zipped) Folder is invalid."

Here are the data classes and methods to create the 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

The corruption may have something to do with space allocation when writing to the memory stream. I noticed all my "successful" zips (11 or less files) were of size 259 KB but all "unsuccessful" zips (more than 11 files) were of size 517 KB, with some larger attempted zips of size 1034 KB. It strikes me as too much of a coincidence that these are all multiples of 258.5 KB, especially since a zip of 11 files results in a 259 KB zip but a zip of 12 files results in a 517 KB zip.

Any insight on what it could be?

ASP.Net Core API Controller returns corrupted excel file

return this in your controller

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

add this code

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);
            }
        }
    }

your code refactored

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

you should be able to change this to

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

I've had success in the past doing return File(fileLocation, "application/zip", fileName); where fileLocation is the path to the folder and fileName is the name of the actual folder. You can do this right in your ActionResult .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM