Question:
How to create a zip file and return it to the client for download?

Problem:

I'm making a .NET Core 7 MVC project, I have an endpoint that I call with a fetch from the front that should get some files from the server zip them and return them.


So far I have this

[HttpGet]

public HttpResponseMessage DownloadPDFs(string jobId)

{

    var selectedFiles = _requestsManager.GetPDFsZipFromJobId(jobId);


    using var memoryStream = new MemoryStream();


    using var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);


    foreach (var pdfFile in selectedFiles)

    {

        if (pdfFile != string.Empty)

        {

            var entry = archive.CreateEntry(Path.GetFileName(pdfFile));


            using var entryStream = entry.Open();

            using var pdfFileStream = System.IO.File.OpenRead(pdfFile);


            pdfFileStream.CopyTo(entryStream);

        }

    }

    //UPDATE 1

    archive.Dispose();

    //

    memoryStream.Seek(0, SeekOrigin.Begin);


    var response = new HttpResponseMessage(HttpStatusCode.OK)

    {

        Content = new StreamContent(memoryStream)

    };


    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

    response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")

    {

        FileName = $"{jobId}_PDFs.zip"

    };


    return response;

}


This gets me a file with some data I see it's size not 0, but I can't open it, when I try with WinRAR I get




The archive is either in unknown format or damaged


This is the javascript for sending the request

function handlePDFsBatch() {

    fetch("/Engine/DownloadPDFs")

        .then((response) => {

            if (response.status === 200) return response.blob();

            else if (response.status === 404) throw new Error("File not found.");

            else throw new Error("Unexpected error.");

        })

        .then((blob) => {

            const url = window.URL.createObjectURL(blob);


            const a = document.createElement("a");

            a.style.display = "none";

            a.href = url;

            a.download = `${crntJobId}.zip`;

            document.body.appendChild(a);


            a.click();

            window.URL.revokeObjectURL(url);

        })

        .catch((error) => {

            console.error("Error downloading file:", error);

        });

}


UPDATE 1 The code for creating a zip file is working correctly and making a valid zip file, I tried it on a console app and saved it to the disk. So the problem is either in sending the file or receiving it.


UPDATE 2 Solved here is a working code.


public MemoryStream GetZipStream(List<string> files)

{

    var memoryStream = new MemoryStream();


    using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))

    {


        foreach (var pdfFile in files)

        {

            if (pdfFile != string.Empty)

            {

                var entry = archive.CreateEntry(Path.GetFileName(pdfFile));


                using var entryStream = entry.Open();

                using var pdfFileStream = System.IO.File.OpenRead(pdfFile);


                pdfFileStream.CopyTo(entryStream);

            }

        }

    }


    return memoryStream;

}


[HttpGet]

public IActionResult DownloadPDFs(string jobId)

{

     var selectedFiles = _ruleEngineRequestsManager.GetPDFsNamesFromJobId(jobId);


     var memoryStream = _ruleEngineRequestsManager.GetZipStream(selectedFiles);


     memoryStream.Seek(0, SeekOrigin.Begin);


     return File(memoryStream, "application/zip", $"{jobId}_PDFs.zip");

}


JavaScript:

fetch("/Engine/DownloadPDFs")

    .then((response) => {

        if (response.ok) return response.blob();

        else throw new Error('Failed to fetch the file');

    })

    .then(blob => startDownload(blob))

    .catch((error) => {

        console.error("Error downloading file:", error);

    });


function startDownload(blob) {

    const url = window.URL.createObjectURL(blob);

    const a = document.createElement('a');

    a.href = url;

    a.download = 'PDFs.zip';

    document.body.appendChild(a);

    a.click();

    window.URL.revokeObjectURL(url);

}


Solution:

MVC and Web API actions return IActionResult or IResult objects, not HttpResponseMessage. All the code after memoryStream.Seek should be replaced with return File(memoryStream,"application/zip","MyZip.zip"), eg:


Stream PackageFiles(IEnumerable<string> files)

{

    var memoryStream = new MemoryStream();


    using(var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))

    {

        foreach (var pdfFile in files)

        {

            if (pdfFile != string.Empty)

            {

                var entry = archive.CreateEntry(Path.GetFileName(pdfFile));


                using var entryStream = entry.Open();

                using var pdfFileStream = System.IO.File.OpenRead(pdfFile);


                pdfFileStream.CopyTo(entryStream);

            }

        }

    }

    memoryStream.Seek(0, SeekOrigin.Begin);

    return memoryStream;


}


[HttpGet]

public IActionResult DownloadPDFs(string jobId)

{

    var selectedFiles = _requestsManager.GetPDFsZipFromJobId(jobId);


    var archiveStream= PackageFiles(selectedFiles)

    return File(archiveStream,"application/zip",$"PDFs_for_{jobId}.zip")

}


The >File can return an >ETag and >Last-Modified header too.


If the PDFs are stored in a specific folder, the entire PackageFiles method can be replaced by >ZipArchive.CreateFromDirectory :


[HttpGet]

public IActionResult DownloadPDFs(string jobId)

{

    ...

    var tempPath=$"path_to_temp/PDFs_for_{jobId}.zip";

    ZipArchive.CreateFromDirectory(jobFilePath,

        tempPath,

        CompressionLevel.SmallestSize,

        false);


    var archiveStream= PackageFiles(selectedFiles)

    return File(tempPath,"application/zip",$"PDFs_for_{jobId}.zip")

}


The temporary file won't be deleted automatically. You'll have to clean up the temporary folder periodically, or use a custom FileResult object >like this one that deletes the temporary file at the end


Suggested blogs:

>What are common syntax errors and exceptions in Python

>What are the steps to fix error while downloading PDF file- Python

>How to do PHP Decryption from Node.js Encryption?

>How to do PHP gd Installation on Ubuntu?

>How to do Web Scraping with Python?

>How to do wild grouping of friends in Python?

>How to download an entire S3 bucket - Complete Guide



Ritu Singh

Ritu Singh

Submit
0 Answers