Skip to main content

Upload Files Handler

This page provides complete examples for implementing the Upload Files backend handler. The uploadfiles endpoint receives files from form upload widgets and stores them in your file storage system.

What is Upload Files?

uploadfiles is the backend endpoint that:

  • Receives one or multiple files from file upload widgets
  • Stores files in your storage system (S3, local storage, etc.)
  • Returns file identifiers for tracking uploaded files
  • Handles multipart/form-data requests

Complete Upload Files Implementation

Choose your backend language:

Controllers/FormsPublicController.cs
public class UploadFilesRequest
{
public string? ProjectGuid { get; set; }
public List<IFormFile> Files { get; set; } = new();
}

[HttpPost("uploadfiles")]
[AllowAnonymous]
[Consumes("multipart/form-data")]
[DisableRequestSizeLimit]
[RequestFormLimits(ValueLengthLimit = int.MaxValue, MultipartBodyLengthLimit = int.MaxValue)]
public async Task<IActionResult> UploadFiles([FromForm] UploadFilesRequest request)
{
if (request.Files == null || request.Files.Count == 0)
return BadRequest("No files uploaded");

var files = Request.Form.Files;
List<string> linkedFiles = new();
string tenantId = GetCurrentTenantId(); // Your tenant resolution logic

// Get your file storage service (S3, Azure, local, etc.)
var fileStorageService = GetFileStorageService();
if (fileStorageService == null)
throw new InvalidOperationException("File storage provider not configured.");

foreach (var file in files)
{
byte[] fileData;

// Read file into memory
using (MemoryStream memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
fileData = memoryStream.ToArray();
}

// Generate upload path (e.g., tenant/forms/2026/01/filename.pdf)
string destUploadPath = fileStorageService.GetFileUploadPath(
tenantId,
"forms",
DateTime.Now
);

// Upload to storage
using (MemoryStream fileStream = new MemoryStream(fileData))
{
string filePath = await UploadFileToStorage(
fileStorageService,
file,
$"{destUploadPath}/{file.FileName}",
fileStream
);

if (!linkedFiles.Contains(filePath))
{
linkedFiles.Add(filePath);
}
}
}

// Return response with uploaded file identifiers
var response = new HandlerResponse();
response.Set(HandlerResponse.LinkedFiles, linkedFiles);

return Ok(await response.Get());
}

private async Task<string> UploadFileToStorage(
IFileStorageService fileStorageService,
IFormFile file,
string filePath,
MemoryStream fileStream)
{
return await fileStorageService.UploadFile(
_context,
filePath,
new FormFile(fileStream, 0, fileStream.Length, "file", file.Name)
{
Headers = new HeaderDictionary(),
ContentType = file.ContentType
}
);
}

Request Structure

The upload request is sent as multipart/form-data:

POST /your-api-path/uploadfiles
Content-Type: multipart/form-data

------WebKitFormBoundary
Content-Disposition: form-data; name="files"; filename="document.pdf"
Content-Type: application/pdf

[Binary file data]
------WebKitFormBoundary--

Key Fields:

  • files - Array of files to upload (multipart/form-data)
  • projectGuid - Optional project identifier

Response Structure

Your uploadfiles handler must return:

{
"linkedFiles": [
"bucket-name:tenant/forms/2026/01/27/document.pdf",
"bucket-name:tenant/forms/2026/01/27/image.jpg"
]
}

Response Fields:

  • linkedFiles - Array of file identifiers that can be used to reference uploaded files in other API calls

File Storage Patterns

S3 Storage (Amazon S3, MinIO, etc.)

C# - S3 Storage Upload
public async Task<string> UploadToS3(IFormFile file, string path)
{
var s3Client = new AmazonS3Client(/* credentials */);

using var stream = file.OpenReadStream();
var putRequest = new PutObjectRequest
{
BucketName = "your-bucket",
Key = path,
InputStream = stream,
ContentType = file.ContentType
};

await s3Client.PutObjectAsync(putRequest);

return $"your-bucket:{path}";
}

Local File Storage

C# - Local Storage Upload
public async Task<string> UploadToLocal(IFormFile file, string path)
{
var fullPath = Path.Combine(_uploadDirectory, path);

Directory.CreateDirectory(Path.GetDirectoryName(fullPath));

using var stream = new FileStream(fullPath, FileMode.Create);
await file.CopyToAsync(stream);

return $"local:{path}";
}

Azure Blob Storage

C# - Azure Blob Storage Upload
public async Task<string> UploadToAzure(IFormFile file, string path)
{
var blobClient = new BlobContainerClient(
connectionString,
"forms-container"
);

var blob = blobClient.GetBlobClient(path);

using var stream = file.OpenReadStream();
await blob.UploadAsync(stream, overwrite: true);

return $"azure-blob:forms-container/{path}";
}

Security Considerations

File Validation

Always validate uploaded files:

C# - File Validation
private bool ValidateFile(IFormFile file)
{
// Check file size (e.g., max 50MB)
if (file.Length > 50 * 1024 * 1024)
return false;

// Check allowed extensions
var allowedExtensions = new[] { ".pdf", ".jpg", ".png", ".docx" };
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
if (!allowedExtensions.Contains(extension))
return false;

// Check MIME type
var allowedMimeTypes = new[] {
"application/pdf",
"image/jpeg",
"image/png",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
};
if (!allowedMimeTypes.Contains(file.ContentType))
return false;

return true;
}

Virus Scanning

Consider integrating virus scanning:

C# - Virus Scanning Integration
private async Task<bool> ScanForVirus(IFormFile file)
{
// Integration with ClamAV, Windows Defender, or other antivirus
using var stream = file.OpenReadStream();
var scanResult = await _antivirusService.ScanAsync(stream);
return scanResult.IsClean;
}

Next Steps