Hướng Dẫn Xây Dựng File Upload Service với ASP.NET Core

Hướng Dẫn Xây Dựng File Upload Service với ASP.NET Core

July 18, 2025 0 By Nam Vu

Tải tệp là một tính năng rất phổ biến trong các ứng dụng hiện đại, từ hình ảnh, tài liệu đến video. Trong bài viết này, chúng ta sẽ xây dựng một dịch vụ tải file đơn giản sử dụng ASP.NET Core Web API (.NET 9). Cấu trúc sẽ được thiết kế đủ linh hoạt để mở rộng sang các hệ thống lưu trữ đám mây như AWS S3 hoặc Azure Blob Storage.

Kiến trúc tổng quát

Luồng hoạt động bao gồm các bước:

  1. Frontend gửi file lên backend.
  2. Backend lưu trữ file vào thư mục nội bộ.
  3. Backend trả về URL file.
  4. Frontend lưu URL đó vào cơ sở dữ liệu.

Tất cả logic xử lý file sẽ được đóng gói trong một dịch vụ riêng (FileUploadService), từ đó dễ dàng mở rộng hoặc thay thế sang các giải pháp đám mây trong tương lai.

Xây dựng các lớp model (DTO)

FileUploadRequest

public record FileUploadRequest(IFormFile File);

FileUploadResponse

public record FileUploadResponse(string FileUrl);

DeleteFileRequest

public record DeleteFileRequest(string FileName);

Tạo Interface cho FileUploadService

public interface IFileUploadService
{
    Task<FileUploadResponse> UploadFileAsync(FileUploadRequest request);
    Task<bool> DeleteFileAsync(DeleteFileRequest request);
}

Implement lớp FileUploadService

public class FileUploadService : IFileUploadService
{
    private readonly IWebHostEnvironment _env;

    public FileUploadService(IWebHostEnvironment env)
    {
        _env = env;
    }

    public async Task<FileUploadResponse> UploadFileAsync(FileUploadRequest request)
    {
        var folderPath = Path.Combine(_env.WebRootPath, "uploads");

        if (!Directory.Exists(folderPath))
            Directory.CreateDirectory(folderPath);

        var extension = Path.GetExtension(request.File.FileName);
        if (!IsValidImageFile(extension)) return new FileUploadResponse(string.Empty);

        var fileName = $"{Path.GetFileNameWithoutExtension(request.File.FileName)}_{Guid.NewGuid()}{extension}";
        var filePath = Path.Combine(folderPath, fileName);

        await using var stream = new FileStream(filePath, FileMode.Create);
        await request.File.CopyToAsync(stream);

        return new FileUploadResponse($"/uploads/{fileName}");
    }

    public async Task<bool> DeleteFileAsync(DeleteFileRequest request)
    {
        var folderPath = Path.Combine(_env.WebRootPath, "uploads");
        var filePath = Path.Combine(folderPath, request.FileName);

        if (!File.Exists(filePath)) return false;

        try
        {
            File.Delete(filePath);
            return true;
        }
        catch
        {
            return false;
        }
    }

    private bool IsValidImageFile(string ext)
    {
        return new[] { ".jpg", ".png", ".jpeg", ".gif" }.Contains(ext.ToLower());
    }
}

Cấu hình Dependency Injection và Middleware

Trong Program.cs:

builder.Services.AddScoped<IFileUploadService, FileUploadService>();

app.UseStaticFiles(); // để server có thể trả file tĩnh như ảnh
app.UseDefaultFiles();

Tạo API Controller

[ApiController]
[Route("api/files")]
public class FileUploadController : ControllerBase
{
    private readonly IFileUploadService _fileUploadService;

    public FileUploadController(IFileUploadService fileUploadService)
    {
        _fileUploadService = fileUploadService;
    }

    [HttpPost]
    public async Task<IActionResult> UploadFile(IFormFile file)
    {
        var result = await _fileUploadService.UploadFileAsync(new FileUploadRequest(file));
        return Ok(result);
    }

    [HttpDelete]
    public async Task<IActionResult> DeleteFile(string fileName)
    {
        var result = await _fileUploadService.DeleteFileAsync(new DeleteFileRequest(fileName));
        return Ok(result);
    }
}

Kiểm thử với Swagger

Nếu bạn đã cài Swagger, bạn có thể thử tải file và xóa file trực tiếp từ giao diện.

Kết quả trả về khi tải file:

Kết quả khi xoá file:

Mở rộng cho Cloud Storage (AWS S3, Azure Blob)

Thay vì lưu trữ nội bộ, bạn có thể chuyển sang lưu trữ trên đám mây để mở rộng quy mô:

Cách hoạt động:

  • Backend tạo Pre-Signed URL hoặc SAS token (Azure) cho phép frontend upload file trực tiếp lên cloud.
  • Sau khi upload thành công, frontend có thể lưu URL vào DB.
  • Bạn cũng có thể tạo Pre-signed URL để truy cập file (tự động hết hạn sau một khoảng thời gian).

Tài liệu tham khảo:

Việc xây dựng một File Upload Service có cấu trúc tốt giúp hệ thống của bạn dễ bảo trì và dễ mở rộng. Bằng cách đóng gói logic trong một service riêng biệt, bạn có thể nhanh chóng chuyển đổi từ lưu trữ cục bộ sang lưu trữ đám mây chỉ với một vài thay đổi nhỏ.

Nếu bạn thấy bài viết hữu ích hoặc có góp ý, đừng ngần ngại để lại bình luận nhé!

#ntechdevelopers