On this page

Skip to content

Practical Exploration of File Upload Handling in ASP.NET Core Web API

TLDR

  • It is recommended to adopt an "architecture that separates file uploads from business logic," where files are uploaded first to obtain an ID, which is then submitted to the business API.
  • Avoid mixing [FromForm] and [FromBody] in the same API to maintain consistency in API formatting.
  • Using Base64 for file transmission increases data size by approximately 1.33 times, which may lead to performance issues.
  • File downloads should ideally be integrated into business Controllers to allow for fine-grained permission control.
  • For incomplete business data, scheduled tasks should be established to clean up expired or unused files.

Development Patterns and Data Reception Methods

In ASP.NET Core Web API, common approaches to handling file uploads depend on the frontend-backend architecture:

  • Full-stack Development: Typically uses form submission, with the backend receiving data via [FromForm].
  • Decoupled Frontend/Backend: Typically uses AJAX to transmit JSON, with the backend receiving data via [FromBody].

Two Main Approaches and Their Pain Points

1. Using Form Submit or FormData

When this issue arises: When developers choose to use [FromForm] to handle file uploads to avoid the extra transmission overhead caused by Base64.

  • Disadvantages:
    • [FromBody] and [FromForm] use different ModelBinders. If custom type handling is required, one must implement both ModelBinder and JsonConverter, leading to high maintenance costs.
    • Having both binding methods in the system leads to inconsistent development standards.
    • For the frontend, if an API did not originally support file uploads, switching to [FromForm] requires a major refactoring of the frontend transmission logic.

2. Using JSON to Transmit Base64 Strings

When this issue arises: When developers prioritize consistency in frontend and backend processing and choose to convert files to Base64 within JSON for transmission.

  • Disadvantages:
    • Data size increases by approximately 1.33 times, which may cause performance bottlenecks.

To balance performance and API consistency, it is recommended to decouple file uploads from business logic.

Implementation Workflow

  1. Dedicated Upload API: Create a specialized file upload API that uses [FromForm] to receive files.
  2. File Information Persistence: The backend saves the file to the server, records information such as filename, path, and size in the database, and returns a unique file ID to the frontend.
  3. Business Data Association: When the frontend calls the API to create or update business data, it only needs to pass the file ID rather than the file content.

Data Management and Validation Mechanisms

  • Cleanup Mechanism: Establish scheduled tasks to periodically clean up files and their database records that have not been associated with business data for more than a day, preventing storage space waste.
  • File Validation: You can create a custom ValidationAttribute and use dependency injection to obtain IFileService for validation.
csharp
protected override ValidationResult IsValid(
    object value, ValidationContext validationContext
) {
    using IServiceScope scope = validationContext.CreateScope();
    IFileService service = scope.ServiceProvider.GetRequiredService<IFileService>();
    // Execute validation logic
}

Important Notes

File type validation (such as checking extensions) cannot completely prevent malicious file tampering. Even when using file signature identification, there is still a risk of misjudgment.


Further Discussion: Considerations for File Download Implementation

The implementation of file downloads should consider permission control requirements:

  • Generic File Download API: Simple to implement and highly reusable, but difficult to implement fine-grained permission checks for specific business scenarios.
  • Download via Business Logic Controller: Although more complex to implement, it ensures the rigor of permission control.

Conclusion: It is recommended to integrate download logic into business Controllers and encapsulate repetitive download logic into a base service class, which can then be used by various modules via dependency injection.


Change Log

  • 2025-03-10 Initial document creation.
  • 2026-05-21 Added link to GitHub sample project.