Blog article

File uploads — resumable, asynchronous, and reliable

Published on October 1, 2024

Uploading a single small file from a good connection is the easy case. Every piece of web software handles it. The cases that generate complaints are the other ones: a ten-megabyte file on a flaky train Wi-Fi connection that drops twice during the upload. Ten files at once from a sales rep on a phone. A screenshot that somebody wants to paste directly into a ticket without saving it to the desktop first. A form that re-renders because of a validation error — and the upload that was in progress suddenly vanishes, forcing the user to start over. A modern uploader handles all of these silently, which is why users don't notice it's there. That's the right outcome.

The default upload behavior is asynchronous. Files start uploading as soon as they're selected or dropped — independent of the form they're attached to and independent of the user clicking save. By the time the user has filled in the rest of the form and reaches the submit button, the upload has usually already completed. If it hasn't, the submit waits briefly for it. What the user doesn't see is themselves staring at a progress bar for ten seconds after clicking save, which is the worst shape a file upload can take.

Resumable transfers are the feature that keeps larger uploads from being a lottery. A broken connection — a Wi-Fi drop, a tunnel, a browser tab that was briefly paused — doesn't restart the upload from zero. The upload picks up where it left off, streams the remaining bytes, and completes. For a fifty-megabyte file on a marginal connection, that's the difference between a one-attempt upload and an endless loop of restarts. Most users will never notice this feature exists, which means it's working exactly as it should.

Paste-to-upload is the small convenience that saves a surprising amount of time. Users paste screenshots from the clipboard directly into a file field, or into a comment, or into any rich-text editor. No trip to the desktop, no save-as dialog, no finding the file to attach. The screenshot goes straight from the clipboard into the upload pipeline. For teams that generate a lot of visual evidence — support agents screenshotting error dialogs, designers attaching mockups to tasks, QA engineers documenting bugs — this alone is often the feature people point to when they explain what they like about the platform.

Drag and drop remains the primary way to add multiple files. Drop a folder onto a file field and the uploader queues each file with its own progress indicator. Users see the overall progress and per-file progress simultaneously, so a slow file doesn't leave them wondering whether the whole upload is frozen. Upload queueing prevents large batches from flooding the server; files go up in a controlled manner, which keeps the browser responsive and the server happy.

File promises are the technical-sounding feature with a very concrete user benefit. When a file is being uploaded, the form holds a promise that represents the pending upload. If the form re-renders — because of a validation error on another field, because the user navigates away and back, because of any event that would normally destroy in-progress state — the promise is preserved. The upload continues in the background; when it completes, the form correctly associates the file with its field. Users don't have to restart an upload because they forgot to fill in a required field. That's the kind of small robustness that's invisible when it works and extremely annoying when it doesn't.

Promise lifespan management is the cleanup side of the same mechanism. Promises that aren't claimed — because the user abandoned the form, or the browser crashed — don't accumulate indefinitely. The platform reaps them on a sensible schedule, so the storage layer stays clean without manual maintenance.

Max size per field and file type constraints give implementers control over what users can upload where. A profile-photo field constrains to images under two megabytes. A contract-attachment field accepts PDFs and Word documents up to twenty megabytes. A general-purpose file field might allow almost anything. Constraints are per field, not global, so each context gets the right policy — and the uploader enforces them client-side for fast feedback and server-side for trust.

Uploader options cover the common UX variations. Some fields benefit from the file picker opening automatically when the user reaches them — "start opened" is the toggle. Some fields want uploads to begin the moment a file is selected without an extra confirmation click — "auto upload" handles that. Some fields want multi-file selection to be the default. These are small configuration choices, and getting them right per context is one of those small things that adds up to a form that feels considered.

Multi-file ordering with drag-to-sort handles the common case where attachment order matters. An invoice's supporting documents often have an intended order (the main invoice, then the supporting proofs, then the appendices). The uploader lets users drag to reorder, and the order is persisted with the form. Saved files on the record retain that order for everyone who looks at them later.

Original filename preservation means that whatever the user saw as the file's name on their desktop is the name that sticks. Not a generated hash, not a timestamped replacement, not a sanitized version — the actual name. That matters for files users look up later ("where's that contract I uploaded last month?") and for exported bundles that include the attachments.

Canvas forms inherit the same uploader automatically. A file field in a canvas custom form behaves identically to a file field in a standard object form — the same resumability, the same paste-to-upload, the same promises, the same constraints. Implementers don't have to choose a form builder based on its upload behavior; every form uses the same uploader.

From the form's perspective, a successful upload produces a file reference that the record retains. From the storage perspective, the file lands in the tenant's configured backend — local, cloud, or whatever the tenant is routed to. The cloud storage article covers where files go after upload; the file repository article covers how to navigate them as a library; the image handling article covers what happens to images specifically on the way to storage. The uploader is where all of that begins.

Good uploads are a subtle feature: when they work, nobody notices, and when they don't, nobody forgets. We've invested in making this one of the silent parts of the platform. The users who don't complain about file uploads are the users who are free to think about their actual work — and that's what the feature is for.