Upload and Download Multiple Files Using Web API

Upload and Download Multiple Files Using Web API

Introduces uploading and downloading multiple files using ASP.NET Core 6.0 Web API with a simple process.

Last updated 7/23/2022 9:37 AM
Jay Krishna Reddy
5 min read
Category
ASP.NET Core
Tags
.NET C# ASP.NET Core Web API

Original author: Jay Krishna Reddy

Original link: https://www.c-sharpcorner.com/article/upload-and-download-multiple-files-using-web-api/

Translation: Desert End Wolf (with Google Translate support, upgraded to .NET 6 in the article)

---Start of article---

Today, we will introduce uploading and downloading multiple files using ASP.NET Core 6.0 Web API through a simple process.

Steps

First, create an empty Web API project in Visual Studio and select .NET 6.0 as the target framework.

No external packages are used in this project.

Create a Services folder and within it create a FileService class and an IFileService interface.

We use three methods in this FileService.cs:

  • UploadFile
  • DownloadFile
  • SizeConverter

Since we need a folder to store these uploaded files, we add a parameter here to pass the folder name as a string; it will store all uploaded files in that folder.

FileService.cs

using System.IO.Compression;  

namespace FileUploadAndDownload.Services;  

public class FileService : IFileService  
{  
    #region Property  

    private readonly IWebHostEnvironment _webHostEnvironment;  

    #endregion  

    #region Constructor  

    public FileService(IWebHostEnvironment webHostEnvironment)  
    {  
        _webHostEnvironment = webHostEnvironment;  
    }  

    #endregion  

    #region Upload File  

    public void UploadFile(List<IFormFile> files, string subDirectory)  
    {  
        subDirectory = subDirectory ?? string.Empty;  
        var target = Path.Combine(_webHostEnvironment.ContentRootPath, subDirectory);  

        Directory.CreateDirectory(target);  

        files.ForEach(async file =>  
        {  
            if (file.Length <= 0) return;  
            var filePath = Path.Combine(target, file.FileName);  
            await using var stream = new FileStream(filePath, FileMode.Create);  
            await file.CopyToAsync(stream);  
        });  
    }  

    #endregion  

    #region Download File  

    public (string fileType, byte[] archiveData, string archiveName) DownloadFiles(string subDirectory)  
    {  
        var zipName = $"archive-{DateTime.Now:yyyy_MM_dd-HH_mm_ss}.zip";  

        var files = Directory.GetFiles(Path.Combine(_webHostEnvironment.ContentRootPath, subDirectory)).ToList();  

        using var memoryStream = new MemoryStream();  
        using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))  
        {  
            files.ForEach(file =>  
            {  
                var theFile = archive.CreateEntry(Path.GetFileName(file));  
                using var binaryWriter = new BinaryWriter(theFile.Open());  
                binaryWriter.Write(File.ReadAllBytes(file));  
            });  
        }  

        return ("application/zip", memoryStream.ToArray(), zipName);  
    }  

    #endregion  

    #region Size Converter  

    public string SizeConverter(long bytes)  
    {  
        var fileSize = new decimal(bytes);  
        var kilobyte = new decimal(1024);  
        var megabyte = new decimal(1024 * 1024);  
        var gigabyte = new decimal(1024 * 1024 * 1024);  

        return fileSize switch  
        {  
            _ when fileSize < kilobyte => "Less then 1KB",  
            _ when fileSize < megabyte =>  
                $"{Math.Round(fileSize / kilobyte, 0, MidpointRounding.AwayFromZero):##,###.##}KB",  
            _ when fileSize < gigabyte =>  
                $"{Math.Round(fileSize / megabyte, 2, MidpointRounding.AwayFromZero):##,###.##}MB",  
            _ when fileSize >= gigabyte =>  
                $"{Math.Round(fileSize / gigabyte, 2, MidpointRounding.AwayFromZero):##,###.##}GB",  
            _ => "n/a"  
        };  
    }  

    #endregion  
}  

The SizeConverter function is used to obtain the actual size of the files we upload to the server.

IFileService.cs

namespace FileUploadAndDownload.Services;  

public interface IFileService  
{  
    void UploadFile(List<IFormFile> files, string subDirectory);  
    (string fileType, byte[] archiveData, string archiveName) DownloadFiles(string subDirectory);  
    string SizeConverter(long bytes);  
}  

Let's add this service dependency in the Program.cs file.

Program.cs

using FileUploadAndDownload.Services;  

var builder = WebApplication.CreateBuilder(args);  

// Add services to the container.  

builder.Services.AddControllers();  
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen();  

// Mainly add the line below to inject the file service  
builder.Services.AddTransient<IFileService, FileService>();  

var app = builder.Build();  

// Configure the HTTP request pipeline.  
if (app.Environment.IsDevelopment())  
{  
    app.UseSwagger();  
    app.UseSwaggerUI();  
}  

app.UseHttpsRedirection();  

app.UseAuthorization();  

app.MapControllers();  

app.Run();  

Create a FileController and inject IFileService into its constructor.

FileController.cs

using FileUploadAndDownload.Services;  
using Microsoft.AspNetCore.Mvc;  
using System.ComponentModel.DataAnnotations;  

namespace FileUploadAndDownload.Controllers;  

[Route("api/[controller]")]  
[ApiController]  
public class FileController : ControllerBase  
{  
    private readonly IFileService _fileService;  

    public FileController(IFileService fileService)  
    {  
        _fileService = fileService;  
    }  

    [HttpPost(nameof(Upload))]  
    public IActionResult Upload([Required] List<IFormFile> formFiles, [Required] string subDirectory)  
    {  
        try  
        {  
            _fileService.UploadFile(formFiles, subDirectory);  

            return Ok(new { formFiles.Count, Size = _fileService.SizeConverter(formFiles.Sum(f => f.Length)) });  
        }  
        catch (Exception ex)  
        {  
            return BadRequest(ex.Message);  
        }  
    }  

    [HttpGet(nameof(Download))]  
    public IActionResult Download([Required] string subDirectory)  
    {  
        try  
        {  
            var (fileType, archiveData, archiveName) = _fileService.DownloadFiles(subDirectory);  

            return File(archiveData, fileType, archiveName);  
        }  
        catch (Exception ex)  
        {  
            return BadRequest(ex.Message);  
        }  
    }  
}  

We can test our API in both swagger and postman.

Here we see the two APIs we created for upload and download, so let's test each of them.

Enter the folder name in the subDirectory field, and add files below to be saved in the corresponding subdirectory on the server. As a response, we will see the total number of files and the total actual size of all uploaded files.

Now let's check the download API. Since we have multiple files in our folder, it will be downloaded as a Zip file, which we need to extract to inspect the files.

Summary

Uploading and downloading files via Web API is suitable for client applications such as Blazor Server, Blazor Client, MAUI, Winform, WPF, etc. Later, when time permits, we will write about how clients call these interfaces.

.... Keep Learning!!!

Keep Exploring

Related Reading

More Articles
Same category / Same tag 6/22/2022

ASP.NET Core WebAPI Localization (Single Resource File)

Microsoft's default approach is one class corresponding to multiple resource files, which is cumbersome to use. This article introduces the use of a single resource file, where all classes in the entire project correspond to one set of multilingual resource files.

Continue Reading
Same category / Same tag 4/13/2022

Practice of uniform wrapping of ASP.NET Core WebApi return results

Regarding the unified return of WebApi results, it also made me think further. First, how to better restrict the unified format of the return, and secondly, the packaging of results must be simpler and more powerful. Through continuous thinking and improvement, I finally achieved preliminary results. I share them out. Learning has no end, thinking has no end. I hope this can encourage us together.

Continue Reading