In many web applications, there arises a need to download multiple files as a single zip archive. This scenario is common when dealing with document management systems, exporting data, or providing bulk downloads. In this article, we will explore how to enable users to download files in a zip format from a .NET Core Web API.


Step 1. Setting up the .NET Core Web API project
Open Visual Studio and create a new .NET Core Web API project.
Configure the project as per your requirements, including selecting a target framework and naming the project.

Step 2. Adding Required NuGet Packages

Right-click on the project in the Solution Explorer and select "Manage NuGet Packages."
Search for and install the "System.IO.Compression" and "System.IO.Compression.FileSystem" packages. These packages provide the necessary classes to work with ZIP files.

Step 3. Creating the File Download Endpoint
Create a new controller (or you can use an existing one also) that will handle the file download in zip format. In this example, we'll create a simple controller named FileController.

Add a new action method with an appropriate route to handle the file download.

Code Example
#nullable disable

using Microsoft.AspNetCore.Mvc;
using System.IO.Compression;

namespace ZipDownload.Controllers {
[ApiController]
[Route("api/files")]
public class FilesController: ControllerBase {
private readonly IWebHostEnvironment _hostEnvironment;

public FilesController(IWebHostEnvironment hostEnvironment) {
  _hostEnvironment = hostEnvironment;
}

[HttpGet]
[Route("download-zip")]
public IActionResult DownloadFiles() {
  try {
    var folderPath = Path.Combine(_hostEnvironment.ContentRootPath, "FilesToDownload");

    // Ensure the folder exists
    if (!Directory.Exists(folderPath))
      return NotFound("Folder not found.");

    // Get a list of files in the folder
    var files = Directory.GetFiles(folderPath);

    if (files.Length == 0)
      return NotFound("No files found to download.");

    // Create a temporary memory stream to hold the zip archive
    using(var memoryStream = new MemoryStream()) {
      // Create a new zip archive
      using(var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
        foreach(var file in files) {
          var fileInfo = new FileInfo(file);

          // Create a new entry in the zip archive for each file
          var entry = zipArchive.CreateEntry(fileInfo.Name);

          // Write the file contents into the entry
          using(var entryStream = entry.Open())
          using(var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) {
            fileStream.CopyTo(entryStream);
          }
        }
      }

      memoryStream.Seek(0, SeekOrigin.Begin);

      // Return the zip file as a byte array
      return File(memoryStream.ToArray(), "application/zip", "files.zip");
    }
  } catch (Exception ex) {
    return StatusCode(500, $ "An error occurred: {ex.Message}");
  }
}
}
}

In the above code, the IWebHostEnvironment is injected into the constructor via dependency injection. It provides information about the web hosting environment, such as the content root path.

 Inside the DownloadFiles method, the first step is to construct the path to the folder containing the files to be downloaded. It combines the content root path obtained from _hostEnvironment.ContentRootPath with the folder name "FilesToDownload"

It then checks if the folder exists. If the folder does not exist, it returns a 404 Not Found response with a corresponding error message. Next, it retrieves a list of files present in the folder using Directory.GetFiles(folderPath). If there are no files in the folder, it returns a 404 Not Found response with an appropriate message.

A temporary MemoryStream is created to hold the zip archive that will be generated. Within the using statement, a new ZipArchive is created using the MemoryStream and the ZipArchiveMode.Create mode.

The code then iterates over each file in the folder and performs the following steps:

  • Retrieves file information using FileInfo.
  • Creates a new entry in the zip archive with the file name using the zip archive.CreateEntry(fileInfo.Name).
  • Opens the entry stream and the file stream and copies the file contents into the entry using fileStream.CopyTo(entryStream).
  • After all the files are added to the zip archive, the MemoryStream is rewound to the beginning using memoryStream.Seek(0, SeekOrigin.Begin).
  • Finally, the zip archive is returned as a downloadable response by using the File method of the base controller class. It takes the byte array representation of the zip archive (memoryStream.ToArray()), the MIME type of the zip file ("application/zip"), and the desired file name ("files.zip") as parameters.

In case any exception occurs during the process, it catches the exception, and a 500 Internal Server Error response is returned with the corresponding error message.

Step 4. Testing the File Download Endpoint

  • Run the Web API project and navigate to the download endpoint URL in a web browser or use a tool like Postman.
  • Upon accessing the endpoint, the browser or tool should prompt the user to save the ZIP file.
  • After downloading and extracting the ZIP file, the user will find all the requested files within the folder structure specified during creation.


In this article, we explored how to enable file downloads in ZIP format using a .NET Core Web API. By leveraging the System.IO.Compression namespace, we were able to compress multiple files into a single ZIP file and return it to the client. This approach offers a convenient way to bundle and deliver multiple files efficiently, improving the user experience when downloading content from a web application.