Efficient File Uploads
Handle file uploads securely and efficiently.
By EMEPublished: February 20, 2025
file uploadsstreamingvalidationsecurity
A Simple Analogy
File uploads are like mail delivery. Check packages at the door, validate contents, store safely.
Why Optimization?
- Performance: Don't buffer entire file in memory
- Security: Validate file types and size
- UX: Progress tracking for large files
- Reliability: Handle failures gracefully
- Cost: Minimize bandwidth usage
Basic Upload
[HttpPost("upload")]
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("File is required");
if (file.Length > 10 * 1024 * 1024) // 10MB
return BadRequest("File too large");
var allowedExtensions = new[] { ".pdf", ".docx", ".xlsx" };
var fileExtension = Path.GetExtension(file.FileName).ToLower();
if (!allowedExtensions.Contains(fileExtension))
return BadRequest("Invalid file type");
var filename = Path.Combine(_uploadPath, Guid.NewGuid() + fileExtension);
using (var stream = System.IO.File.Create(filename))
{
await file.CopyToAsync(stream);
}
return Ok(new { filename });
}
Streaming Upload
[HttpPost("stream-upload")]
public async Task<IActionResult> StreamUpload()
{
var boundary = GetBoundary(Request.ContentType);
var reader = new MultipartReader(boundary, Request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
var hasContentDispositionHeader =
ContentDispositionHeaderValue.TryParse(
section.ContentDisposition,
out var contentDisposition);
if (hasContentDispositionHeader && contentDisposition.DispositionType.Equals("form-data"))
{
if (!section.ContentType.StartsWith("image/"))
{
section = await reader.ReadNextSectionAsync();
continue;
}
var filename = contentDisposition.FileName.Value;
var filepath = Path.Combine(_uploadPath, Guid.NewGuid() + Path.GetExtension(filename));
using (var targetStream = System.IO.File.Create(filepath))
{
await section.Body.CopyToAsync(targetStream);
}
}
section = await reader.ReadNextSectionAsync();
}
return Ok();
}
Progress Tracking
const input = document.querySelector('input[type="file"]');
const progressBar = document.querySelector('progress');
input.addEventListener('change', async (e) => {
const file = e.target.files[0];
const formData = new FormData();
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
progressBar.value = percent;
}
});
xhr.addEventListener('load', () => {
console.log('Upload complete');
});
xhr.open('POST', '/upload');
xhr.send(formData);
});
Best Practices
- Validate: Check type, size, content
- Rename: Use GUIDs to prevent conflicts
- Stream: Don't load entire file into memory
- Sanitize: Clean filenames
- Quarantine: Scan for malware
Related Concepts
- Chunked uploads
- Resumable uploads
- Progress tracking
- Cloud storage
Summary
Handle file uploads securely with validation, streaming, and progress tracking. Store safely with sanitized names.