File Upload
The button, drop zone and file list that send documents to a server. Learn native file inputs, drag and drop, mobile document pickers and per-file progress, and the type, size, file name and partial-failure bugs testers find in every upload.
You attach a scanned invoice to an expense claim, upload a signed rental contract, send a CSV of transactions to your accountant, add your CV to a job application. Then you press Submit and trust that every file arrived, complete and readable.
Sometimes it didn’t. The scanner saved the file as SCAN_0042.PDF and the app said “file type not supported”. The hotel invoice uploaded for two minutes before the app mentioned a 10 MB limit. One of three receipts failed on the train and quietly vanished from the list. File upload looks like one button, but it’s a small system of its own, and testers who know what to feed it find bugs every time.
What is a file upload?
A file upload component lets the user choose one or more files from their device and send them to a server. It usually has three jobs:
- Picking: a button that opens the system file picker, and on the web often a drop zone for drag and drop.
- Checking: is the type allowed, is it under the size limit, are there too many files?
- Uploading: sending each file, showing its progress, and handling cancel, failure and retry.
You’ll find it in expense apps (receipts), banking and insurance (statements, claims), job portals (CV), email and chat (attachments), cloud storage, and admin tools (CSV imports).
Anatomy
Receipts
PDF or DOCX · up to 10 MB each · max 5 files
Drag files here, or
Browse files- Hotel_invoice_Grand_Hotel_Europa_Sept.pdf
1.2 MB · Uploading… 64%
Taxi receipt.docx
86 KB · Uploaded
Flight_LH1234.pdf
Connection lost at 40%
- 1
Label and rules
Says what to upload and the limits: allowed types, max size, max number of files. Shown before the user picks anything.
- 2
Drop zone
An area where files can be dropped (web, desktop). A shortcut, never the only way in.
- 3
Browse button
Opens the system file picker. A real, labelled button that works with touch, mouse, keyboard and screen readers.
- 4
File row
Icon, name (long names truncate in the middle, so the extension stays visible) and size.
- 5
Per-file progress
Each file shows its own progress and state: uploading, processing, uploaded, failed.
- 6
Cancel / Remove
Cancel stops a running upload; Remove takes a file out. Each button names its file for screen readers.
- 7
Error + Retry
A failed file stays in the list with a reason and a Retry button. It never silently disappears.
- 8
Summary
"2 of 3 uploaded · 1 failed". Tells the user whether it is safe to press Submit.
The states of one file
Every file in the list goes through its own life cycle, and the whole component is only “done” when all files are. A good list shows each file’s state separately:
| State | What the user sees | What to check |
|---|---|---|
| Rejected | “Not added” with the reason | The reason is specific: type, size, count, empty file |
| Uploading | Progress bar, percentage, Cancel | The bar moves; Cancel really stops the request |
| Processing | Bar full, “Processing…” | The app doesn’t look frozen at 100% |
| Uploaded | Check mark, Remove | The server has the right file, complete and openable |
| Failed | Error text, Retry, Remove | The file stays in the list; Retry works |
| Canceled | “Canceled”, Retry or gone | Matches the spec; nothing half-uploaded is kept |
Designs & variants
The same idea appears as a plain browser input, a drop zone, a single “Upload CV” field, a paperclip in a chat, or the system document picker on a phone.
Most of these respond to taps. The first one is a real browser file input: try it with files from your own device.
Try it: the dialog filters to PDFs, but "All files" is one click away.
Native file input
Web<input type="file"> with accept and multiple. Looks different in every browser, but keyboard and screen reader support come for free.
CV / Résumé
PDF or DOCX, max 5 MB
Single file field
Web · FormsOne file only, for a CV or an ID. After picking, the file shows with Replace and Remove instead of the button.
Here are the documents for the trip
Attachment in a composer
iOS · Android · WebA paperclip in chat or email. Attachments appear as removable chips. Icon-only, so it needs an accessible name.
Upload your ID document
Choose FileMobile browser file input
iOS SafariOn phones the same <input type="file"> opens a menu: photos, camera or files. The accept list changes which options appear.
Document picker (Files)
iOSThe system picker for documents. Apps get only the files the user picks, so no permission prompt is needed. Tap files to select them.
Document picker (Storage Access Framework)
AndroidThe system 'Open from' picker. Also covers Google Drive and other providers. Tap files to select several.
The native file input
On the web, everything starts with <input type="file">. Two attributes matter most for testers:
accept(for exampleaccept=".pdf,.docx"oraccept="application/pdf") filters what the picker shows. It’s a hint, not a check: desktop dialogs offer an “All files” option, and files dropped onto a drop zone ignoreacceptcompletely.multipleallows picking several files at once. Without it, the second file replaces the first, which surprises users who expected to add more.
Custom-designed upload buttons usually hide the real input and trigger it from a styled button. That’s fine, as long as the visible control is a real, focusable button.
Drag and drop is a shortcut, not the way in
A drop zone is great on a desktop with a file explorer open. It’s useless on a phone, for keyboard users, for many screen reader users and for anyone who finds dragging hard. Drag and drop must always come with a Browse button that does the same thing. Testers also check what happens when a file is dropped outside the zone: the browser shouldn’t simply open the PDF and leave the page (with the user’s half-filled form).
Mobile document pickers
Phones don’t let apps browse the whole file system. Instead, the app asks the system picker, the user chooses, and the app gets access to just those files. No storage permissionApp permissionsThe user's approval for an app to use things like the camera, location, contacts or notifications.Open in glossary → is needed for this.
| iOS | Android | |
|---|---|---|
| Picker | Document picker (the Files app UI: Recents, Shared, Browse) | Storage Access Framework picker (“Open from”, Recent, Downloads, Drive) |
| Sources | On My iPhone, iCloud Drive and installed providers like Google Drive or Dropbox | Device storage, Downloads, and providers like Google Drive |
| Multiple | Only if the app enables it | Only if the app requests it |
| Typical traps | Cloud files not downloaded yet, file names with emoji, files from other apps that disappear later | Files without a real name or size, Drive files that need a download first, older OS versions |
On a mobile website, the same <input type="file"> opens a system menu instead of a desktop dialog (on iPhone: Photo Library, Take Photo or Video, Choose File). The accept list changes which options appear, so test on real phones, not only in a desktop browser.
Push it to the limits
This is a correct uploader for an expense claim. Change the rules and the network: the same files are uploaded again each time, so you can compare. Then add files from the test kit at the bottom (drag them, or tap them).
🧪 Upload lab
A correct uploader for an expense claim. Change the rules and the network: the same files are uploaded again, so you can compare the results.
Receipts and documents
PDF, DOCX, CSV or ZIP · up to 10 MB each · max 10 files, 60 MB in total
Drag files here, or
- No files yet.
Screen reader hears (aria-live)
The client catches the duplicate, the server catches the renamed PNG. Now try exact-case checking, turning client checks off, or an unstable network.
Your test files
Drag a file onto the drop zone, or tap it to add it.
A few things to try, and what they teach:
- Extension, exact case rejects
SCAN_0042.PDF. Users don’t care about letter case, and many scanners and older systems write uppercase extensions. - MIME type (File.type) looks smarter, but the browser usually guesses the type from the extension.
holiday-photo.pdf(a PNG renamed to .pdf) still passes. Only the server, looking at the actual bytes, can catch it. - Client-side checks off shows why the app should check early: the 48 MB ZIP uploads completely before the server says no. The server still protects itself, but the user wasted time and mobile data.
- Unstable network: failed files stay in the list with Retry. Nothing silently disappears.
File names are test data
The name is the part of a file that developers forget most. Keep these in your test kitTest dataThe input values, accounts and records you use while testing.Open in glossary →:
| Name | What can go wrong |
|---|---|
SCAN_0042.PDF | Uppercase extension rejected |
Lease agreement (signed) final.docx | Spaces and brackets break links or downloads |
| A 120-character name | Overflows the row, pushes the Remove button off-screen, or is cut by the server |
Mietvertrag_endgültig.pdf, Résumé.pdf, 报告.pdf, 🧾.pdf | diacriticsDiacriticsAccent marks on letters, like é, ü, č or ž. Search usually should ignore them.Open in glossary →, other scripts and emoji turn into ??? or ü |
invoice.pdf.exe, report. and a name without any extension | Wrong type detected, or the app crashes on a missing extension |
Invoice_March.pdf twice (from two folders) | Duplicate, or the second silently overwrites the first on the server |
When to use it
- The user has to provide a document that already exists on their device or in the cloud: receipts, contracts, statements, CVs.
- The content is too big or too structured to type: a CSV import, a ZIP of project files.
- Several files belong together and should be checked and submitted as one set (an insurance claim with photos, invoices and a police report).
Good and bad uses
Drag and drop plus a Browse button
Drag files here, or
Browse filesWorks with a mouse, a finger, a keyboard and a screen reader.
Drag and drop only
Drop your files here
On a phone or with a keyboard there is simply no way to add a file.
Rules shown before picking
Upload your bank statement
PDF or CSV · up to 10 MB
statement_2025_full_year.pdf
24.3 MB is over the 10 MB limit. Not uploaded.
The user knows the limit up front, and the oversized file is stopped instantly with a specific message.
Rules revealed by the error
Upload your bank statement
statement_2025_full_year.pdf
Upload failed (after 2 min 10 s)
No limit mentioned, two minutes of uploading, then a generic failure.
Long names truncate in the middle
1.4 MB · Uploaded
The extension and the Remove button stay visible.
Long names push everything out
Hotel_invoice_Grand_Hotel_Europa_Innsbruck_2026-09-14_booking_884213.pdf
1.4 MB · Uploaded
The name runs off the card and takes the Remove button with it.
Also don’t ask for a file when a few typed fields would do (an IBAN, a date, a policy number), and don’t make users upload something the app could fetch itself.
Common bugs
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Browse not keyboard reachable | The drop zone works with a mouse, but Tab never reaches a button | High (for affected users) |
| 2 | Case-sensitive type check | SCAN_0042.PDF or .JPG rejected | Medium–High |
| 3 | Limits hidden | Size or count limit shown only after a long upload fails | Medium |
| 4 | Wrong file removed | Tapping Remove on one file removes a different one | High (wrong documents submitted) |
| 5 | Failure swallowed | A failed file disappears, or the claim is sent without it | High |
| 6 | Long names break the layout | Name overflows, Remove button off-screen, text overlaps | Medium |
| 7 | No progress or cancel | Spinner forever, no way to stop a wrong 200 MB upload | Medium |
| 8 | Duplicates | Same file attached twice, or a second file with the same name overwrites the first | Medium |
| 9 | Server trusts the client | Renamed or oversized files are accepted when sent directly | High (security) |
| 10 | Partial failure hidden | “Upload complete” while 1 of 3 files failed | High |
| 11 | Special characters mangled | Mietvertrag_endgültig.pdf arrives as Mietvertrag_endgültig.pdf | Low–Medium |
| 12 | Submit during upload | Submit works while files are still uploading; the claim arrives without them | High |
Find the bug
This expense claim form has six upload bugs. Explore it like a tester:
- Click in Description, then use only Tab and Enter to add a receipt.
- Add every file from Files on your computer, by dragging and by tapping. Read every message and the network log.
- Add two or three files, then remove the top one.
- Turn on Unstable network and add several small files. Count them.
🐞 Bug hunt: Expense claim receipts
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
New expense claim
Receipts
PDF, DOCX, XLSX or CSV
Drop receipts here or browse
Files on your computer
Drag a file onto the drop zone, or tap it to add it.
Device settings
Network log
- Add a receipt to see upload requests…
- 1
Hint: Click in the Description field, then press Tab. Where does focus go?
- 2
Hint: Add the file from the scanner.
- 3
Hint: Read the hint under "Receipts", then add Hotel_folio_scan_600dpi.pdf and watch.
- 4
Hint: Add Taxi_receipt.pdf, then Flight_LH1234.pdf. Remove the top one.
- 5
Hint: Turn on Unstable network and add three or four small files one by one.
- 6
Hint: Add the hotel invoice with the very long name. Try to remove it.
How to test it
Upload limits are perfect for boundary value analysis: with a 10 MB limit, test 9.9 MB, exactly 10 MB and 10.1 MB, plus a 0-byte file. With “max 5 files”, try 4, 5 and 6, and also 3 + 3 in two rounds.
Checklist
File upload test checklist
0 of 16 checked
Example test cases
TC-FU-001Add a receipt with the keyboard onlypositivehigh
Preconditions
- Web, desktop browser, no mouse
Steps
- Tab to the Browse files button
- Press Enter
- Pick Taxi_receipt.pdf in the dialog and confirm
Expected result
Focus ring is visible on Browse. The picker opens; after picking, focus returns near the upload area and the file appears with progress. A screen reader announces "Taxi_receipt.pdf added".
TC-FU-002Uppercase extension is acceptededgemedium
Steps
- Add SCAN_0042.PDF (a valid PDF)
Test data
Also: report.Pdf, contract.DOCX
Expected result
The file is accepted and uploaded like a lowercase .pdf.
TC-FU-003File size at the boundariesedgehigh
Preconditions
- Limit is 10 MB per file
Steps
- Add a 9.9 MB PDF
- Add a PDF of exactly 10 MB
- Add a 10.1 MB PDF
- Add a 0-byte PDF
Expected result
9.9 MB and 10 MB upload. 10.1 MB is rejected instantly, before any upload request, with "Larger than 10 MB". The empty file is rejected with "This file is empty".
TC-FU-004Server rejects a renamed filenegativehigh
Preconditions
- Test environment
- Proxy tool or DevTools available
Steps
- Rename a PNG image to holiday-photo.pdf
- Upload it through the app
- Send it again directly to the upload endpoint, bypassing the app
Expected result
Both attempts are rejected (for example 415) with a clear message. Nothing is stored.
TC-FU-005Remove the correct filepositivehigh
Steps
- Add A.pdf, B.pdf and C.pdf
- Remove B.pdf
- Remove the top file
- Remove the bottom file
Expected result
Each time exactly the file in that row disappears. The counter and the submitted claim match the list.
TC-FU-006Partial failure with retrynegativehigh
Steps
- Add three files
- Switch the network off while the second one uploads
- Switch it back on
- Tap Retry on the failed file
Expected result
Files 1 and 3 upload (or wait for the network). File 2 shows "Upload failed" with Retry and stays in the list. Submit is blocked or warns. After Retry, all three are uploaded.
TC-FU-007Long and unicode file namesedgemedium
Steps
- Add a file with a 120-character name
- Add Mietvertrag_endgültig.pdf and 报告.pdf
- Submit, then open the claim and download each file
Expected result
Names truncate in the middle with the extension visible; Remove stays visible. After download, names and contents are unchanged.
TC-FU-008Pick several documents on Androidpositivemedium
Preconditions
- Android device with a PDF in Downloads and one in Google Drive
Steps
- Tap Attach
- In the system picker, long-press to select both files
- Tap Select
Expected result
Both files appear with their real names and sizes and upload. The Drive file is downloaded first if needed, with progress.
Tip: click a test case to expand it.
Accessibility
- A real, labelled button. The control that opens the picker must be a real button with a clear name like “Browse files” or “Attach receipt”. An icon-only paperclip needs an accessible name. Test keyboard navigationKeyboard navigationUsing a website with only the keyboard: Tab to move, Enter or Space to activate, Esc to close.Open in glossary →: Tab reaches it, Enter and Space open the picker (WCAG 2.1.1 Keyboard, Level A).
Dev note
A<button>(or a native file input). - Drag and drop is optional. Everything must work without dragging. That’s what keyboard, switch, screen readerScreen readerSoftware that reads the screen aloud for blind and low-vision users, like VoiceOver or TalkBack.Open in glossary → and most touch users rely on.
- Rules are instructions. Allowed types and limits are visible text near the control (WCAG 3.3.2 Labels or Instructions, Level A).
Dev note
The text is connected witharia-describedby. - Announce what happens. “Invoice.pdf added”, “uploaded” and “failed” should reach screen readers without moving focus, through a live region, per WCAG 4.1.3 Status Messages (Level AA). Errors are text, not only a red border (WCAG 3.3.1 Error Identification, Level A).
Dev note
aria-live="polite", orrole="status". - Name every row action. “Remove Invoice_March.pdf”, “Retry Flight_LH1234.pdf”, not five buttons called ”×”. Progress bars need a name and value.
Dev note
role="progressbar"witharia-valuenow, or a native<progress>. - Focus after actions. After picking files, focus returns to the upload area. After removing a file, focus moves to a sensible place (the next row or the Browse button), not to the top of the page.
- Touch targetsTouch targetThe area of the screen that responds to a tap, which should be large enough for a finger.Open in glossary →: Browse, Remove and Retry are at least 44×44 pt on iOS and 48×48 dp on Android. On the web, WCAG 2.5.8 (AA) asks for at least 24×24 CSS px; 44×44 px (2.5.5, AAA) is the comfortable target.
- Not color alone. Failed and uploaded states use an icon and text, not just red and green. Text needs 4.5:1 contrast, icons and borders 3:1 (WCAG 1.4.3 / 1.4.11).
Mobile vs web
- Apps open the document picker. Multiple selection and allowed types are set by the app; check both match the spec.
Dev note
UIKitUIDocumentPickerViewController, SwiftUIfileImporter. - Picked files can live in iCloud Drive or another provider and may need to download first. Test with a file that isn’t on the device yet, and in airplane mode.
- Test Cancel in the picker: the app should simply return, not show an error or an empty attachment.
- Uploads should survive locking the phone or switching apps, or fail clearly (Background and foreground).
- In Safari, the file input offers Photo Library, camera and Files. Choosing a photo there is where Image Upload topics (HEIC, EXIF) start.
- Apps use the Storage Access Framework picker. It needs no storage permission; an app that asks for full storage access just to attach a PDF is worth questioning (App permissions).
Dev note
ACTION_OPEN_DOCUMENT/ACTION_GET_CONTENT, or the Activity Result APIs in modern apps. - Name and size come from the provider and can be missing; check the list still shows something sensible.
Dev note
The app gets acontent://link, not a file path. - Test files from Downloads, Google Drive and an SD card or USB drive if the device has one, and selecting several files with a long-press.
- Rotate the device and switch apps while the picker is open: the app must not lose the files already attached (Interruptions).
<input type="file">withacceptandmultiple. Remember:acceptonly filters the dialog; “All files” and drag and drop bypass it.- Drag and drop: one file, several files, a folder, a non-allowed type, and a drop outside the zone.
- Use the DevTools Network panel: is the request
multipart/form-data, does the size match, what status comes back? Throttle to “3G” to watch progress and cancel (DevTools for testers). - Reloading or closing the tab during an upload should warn the user or resume cleanly.
- Test Chrome, Safari and Firefox: the native input looks and behaves differently in each, and so do drop events.
Check your understanding
Quick quiz
0/5 answered · 0 correct
1.An upload form has accept=".pdf" on its file input. Does that guarantee only PDFs reach the server?
2.The drop zone works with a mouse, but Tab never reaches a Browse button. What kind of bug is it?
3.The size limit is 10 MB. Which set of files is best for boundary testing?
4.Three files are uploaded and one fails. What should the user see?
5.Why should a long file name truncate in the middle?
Key takeaways
- File upload is three jobs: picking, checking and uploading. Each file has its own state: rejected, uploading, processing, uploaded, failed, canceled.
- Drag and drop is a shortcut. A real, labelled Browse button must do the same, for keyboard, screen reader and phone users.
- Show the rules up front (types, size, count) and reject bad files before uploading, with a specific message.
- Client checks are convenience. The server must check the real content, size and count. Type checks must ignore letter case.
- File names are test data: long, unicode, spaces, uppercase extensions, the same name twice.
- Partial failures must stay visible with Retry; Remove must remove exactly that file; Submit must not run ahead of uploads.
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡