REST API File Upload and Download
APIs handle more than JSON. Users upload profile photos, attach PDFs to support tickets, and download invoices and reports. Files are large, binary, and risky, so they need special design.
The Post Office Picture
Sending a letter requires only an address. Sending a heavy parcel requires a box, a label, a weight check, and a customs form. A file upload follows the same pattern. The request carries extra details about the file along with the file itself.
Upload with multipart/form-data
The multipart/form-data format splits a request into parts. One part holds the file, and other parts hold text fields such as a title.
POST /documents Content-Type: multipart/form-data; boundary=XYZ --XYZ Content-Disposition: form-data; name="title" Quarterly Report --XYZ Content-Disposition: form-data; name="file"; filename="report.pdf" Content-Type: application/pdf (binary data) --XYZ--
A successful upload returns 201 Created with a Location header that points to the new file resource.
Direct Upload with Pre-Signed URLs
Large files strain an API server. A better pattern sends the file straight to cloud storage. The API issues a temporary signed URL, and the client uploads to that URL.
Client ── 1. request upload link ──→ API Server Client ←── 2. signed URL (expires in 10 minutes) ── API Server Client ── 3. PUT file ──────────────→ Cloud Storage Client ── 4. confirm upload ────────→ API Server
The API server never touches the file bytes. This design saves bandwidth and scales well.
Download Basics
A download response sets the Content-Type header to the file type and the Content-Length header to the file size. The Content-Disposition header controls browser behavior.
HTTP/1.1 200 OK Content-Type: application/pdf Content-Length: 482113 Content-Disposition: attachment; filename="invoice-204.pdf"
The value attachment prompts a save dialog. The value inline displays the file in the browser.
Resumable Downloads with Range Requests
Large downloads break when connections drop. Range requests let a client ask for part of a file.
GET /videos/55/file Range: bytes=1000000-1999999 HTTP/1.1 206 Partial Content Content-Range: bytes 1000000-1999999/8400000
Video players use this feature to jump to any point in a clip, and download managers use it to resume after a failure.
Upload Safety Checklist
- Set a maximum file size and return 413 Content Too Large above it.
- Allow only approved file types, and check the real content, not just the extension.
- Generate a new random file name on the server and ignore the client name.
- Store uploads outside the web root so the server never runs them as code.
- Scan files for malware before other users can open them.
- Require authentication and confirm that the user may access each file.
Metadata and Resource Design
Treat a file as a resource with its own record. The record holds the id, name, size, type, owner, and upload date. Use /documents/15 for the metadata and /documents/15/content for the raw bytes. Clients can list files cheaply without downloading any content.
Common Mistakes
Sending Files as Base64 in JSON
Base64 inflates file size by about one third and consumes memory on both sides. Use multipart or direct upload for anything beyond tiny images.
Trusting the Content-Type Header
A client can label any file as an image. Inspect the file signature on the server before accepting it.
Public Links Without Expiry
Permanent open links leak private files. Use signed URLs that expire after a short time.
