Image Upload
How photo and file uploads work on iOS, Android and the web, why HEIC, EXIF rotation, big files and bad networks break them, and how to test uploads end to end.
A seller on a second-hand marketplace takes three photos of her sofa and taps Publish. The first photo shows up lying on its side. The second fails with “Something went wrong”. The third uploads fine, and with it, hidden inside the file, the GPS coordinates of her living room, which any buyer can now download.
Profile pictures, receipts in expense apps, ID checks in banking apps, damage photos in insurance claims, food photos in reviews: image upload is everywhere. It looks like one button, but behind it are cameras, permissions, a dozen file formats, huge files, flaky networks and real privacy risks. That’s why upload is a feature where testers who know what to feed it find a lot of serious bugs.
How it works
- The user picks a source. On a phone: take a new photo with the camera, or choose from the photo library (the system photo picker) or the Files app. On the web: a file chooser (
<input type="file">), drag and drop, or paste from the clipboard. On phones, the browser’s file chooser also offers the camera and photo library. - The app may need permission. Using the camera inside the app needs the user’s permission (on Android, an app that simply hands off to the system camera app may not). Photo library access depends on how the app asks (more below).
- The app checks the file. Is it an allowed type? Is it under the size limit? Are there too many files? Good apps check before uploading, so the user doesn’t wait a minute just to get an error.
- The app prepares the image. Often it resizes and compresses (a 12-megapixel photo is rarely needed for a 400-pixel avatar), converts formats, applies rotation and removes private metadata.
- The upload runs. The file is sent over HTTPS, ideally with a progress bar and a Cancel button. On a slow network this can take a long time, so the app must handle drops, retries and the user leaving the screen.
- The server validates it again. It checks the real content (not just the file name), the size, and the type, then usually re-encodes the image and creates thumbnails. Never trust the client: anyone can skip the app’s checks.
- The app shows the result. A thumbnail, a clear error, or a “Retry” option. The uploaded image should look exactly like the one the user picked.
Sources and permissions
| Source | iOS | Android | Web |
|---|---|---|---|
| Camera | Permission prompt on first use | Permission prompt on first use capture on a file input opens the phone’s camera app; a live camera view inside the page makes the browser ask for permission | |
| Photo picker | System picker: no permission. Full library access: prompt with Limit Access | Photo Picker: no permission. Media access: Android 14+ allows selected photos only | File input opens the phone picker or desktop dialog |
| Files / drag & drop / paste | Files app via document picker | Files via the system file chooser | Drag onto a drop zone, or paste a screenshot with Ctrl/Cmd+V |
PermissionsApp permissionsThe user's approval for an app to use things like the camera, location, contacts or notifications.Open in glossary → create their own test cases. The system shows a modal the app can’t style, and the user can say Don’t Allow, pick Limit Access / selected photos only, or later revoke access in Settings. Each answer needs a sensible result: no crash, no empty screen, and a clear way to the Settings app if the user changes their mind. The differences between platforms are covered in iOS vs Android.
Formats, sizes and the files you should keep ready
| Format | Where it comes from | What to watch |
|---|---|---|
| JPEG | Most cameras, Android, older iPhones | Big files from modern cameras, EXIF rotation |
| HEIC | iPhone camera default (“High Efficiency”) | Servers and browsers that don’t support it; must be accepted or converted |
| PNG | Screenshots, logos, graphics | Transparency turned black or white; large file sizes |
| WebP | Web downloads, some Android apps | Accepted by the server? Shown correctly everywhere? |
| GIF | Memes, animations | Animation lost after resizing, or rejected without a clear message |
Every product has limits: allowed types, a maximum file size (for example 10 MB), maybe a maximum resolution and a maximum number of files. Treat them like any other rule and use boundary value analysisBoundary value analysisTesting values at and just around the edges of valid ranges, where bugs love to hide.Open in glossary →: just under, exactly at, and just over the limit. And the user must be told the limit, ideally before they pick, and again in the error.
EXIF: rotation and location hidden in the file
Phone cameras don’t actually rotate the image when you hold the phone upright. They save the pixels as the sensor saw them, and add a small EXIF tag (photo metadata) called Orientation that says “rotate this 90° when you show it”. Apps that ignore that tag show portrait photos sideways. EXIF can also contain the camera model, the date and the exact GPS location where the photo was taken.
Try it: pick the portrait photo, then switch the app’s handling on and off. Then see what the server keeps.
1. Pixels stored in the file
2. What the app shows
The app ignored the tag and drew the raw pixels. This is the classic "my photo uploaded sideways" bug.
3. What the server keeps
Anyone who downloads this photo can see where it was taken, often the seller's home.
Upload on a real network
A 4 MB photo takes a fraction of a second on office Wi-Fi and half a minute on a weak mobile signal. Testers check what happens in between:
- Progress: the bar moves, and after reaching 100% the app shows that the server is still processing (instead of a full bar that seems frozen).
- Cancel: the user can stop an upload that’s taking too long or was the wrong photo.
- Lost connection: the app tells the user, keeps the photo, and retries when the network returns (or offers a Retry button). It never silently drops the photo. See Network conditions for how to simulate this.
- Leaving the app: if the user switches apps or locks the phone, either the upload continues in the background (iOS and Android both support background transfers) or it resumes cleanly when the app returns (Background and foreground).
- Double submit: tapping Post twice must not upload the same photo twice.
Server-side validation (the defensive basics)
File upload is one of the classic places where attackers try to sneak in something that isn’t an image. You don’t need to be a security expert to check the basics in a test environment with harmless test files:
- Rename a text file (or a PDF) to
photo.jpgand upload it. The server should reject it because it looks at the content, not the name. A clear message like “This file isn’t a valid image” is enough. - Upload a file over the size limit through the web version and through the app. Both should be rejected by the server too, not only by the app.
- After a successful upload, open the stored image’s URL. It should be served as an image, from a random-looking file name, and the original file name shouldn’t be used in the path.
- Check the stored file’s metadata: no location data where the spec says it must be removed.
Report anything suspicious and let the security team go deeper. See Security basics for the wider picture.
Example requirements
What usually goes wrong
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | HEIC not supported | iPhone photos fail, often with a generic error | High (many users affected) |
| 2 | EXIF orientation ignored | Portrait photos sideways or upside down, in the app, on the web or only in thumbnails | Medium–High |
| 3 | Size limit hidden | Big files upload for a minute and then fail, with no limit mentioned | Medium |
| 4 | No progress, no cancel | Spinner forever, bar stuck at 100%, no way to stop | Medium |
| 5 | Network loss handled badly | Photo silently disappears, or the app hangs; no retry | High |
| 6 | Location metadata kept | Downloaded photo reveals where the user lives | High (privacy) |
| 7 | Transparency lost | Transparent PNG logo gets a black background | Low–Medium |
| 8 | Over-compression | Uploaded photo is blurry or blocky, text in receipts unreadable | Medium |
| 9 | Permission edge cases | Crash or blank screen after “Don’t Allow” or limited access | High |
| 10 | Content not validated on the server | A renamed non-image file is accepted and stored | High (security) |
| 11 | Duplicates | Double tap or retry uploads the same photo twice | Low–Medium |
| 12 | Accessibility gaps | Upload button with no label, progress not announced, drag and drop only | Medium–High |
Find the bug
Swapsy’s “New listing” screen below has six bugs. Use the test photo library like a tester would:
- Add photos one at a time and several at once. Read every message.
- Compare each thumbnail with the photo in the picker.
- Switch the network to Slow 3G, watch the progress bar, then try Offline in the middle of an upload.
- Tap uploaded photos to see what the server stored, and watch the network log.
- Check the Files tab too.
🐞 Bug hunt: Swapsy photo upload
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
Photos · 0/5
Add up to 5 photos. The first one is the cover.
Details
Network
From the spec
Accepted: JPEG, PNG, HEIC, WebP · max 10 MB per photo · up to 5 photos. Uploads can be canceled. Location data must be removed before photos are stored.
Tap an uploaded photo to see what the server stored. Use Slow 3G to watch uploads closely.
Network log
- Add photos to see requests…
- 1
Hint: Add IMG_2041.HEIC, the iPhone camera default format.
- 2
Hint: Add IMG_2087.JPG and compare the thumbnail with the picker.
- 3
Hint: Add DSC_0912.JPG (12.4 MB) and read the error.
- 4
Hint: Switch to Slow 3G and add a photo. Try to stop it.
- 5
Hint: Start an upload on Slow 3G, then switch to Offline.
- 6
Hint: Tap an uploaded phone photo and read what the server stored.
Test scenarios
Functional
- Add a photo from the camera, the photo library and Files. On the web: file chooser, drag and drop, paste.
- Add one photo, then several at once, then exactly the maximum, then try one more.
- Each accepted format uploads and looks right: JPEG, PNG, HEIC, WebP (and GIF if supported).
- Remove a photo, reorder photos (if supported), replace the cover photo.
- The uploaded image looks the same in the app, on the web, and in notifications or emails that show it.
Negative
- A file over the size limit, a format that isn’t allowed, a 0-byte file, a corrupted image.
- A non-image file renamed to
.jpg(use a harmless text file). - Don’t Allow for camera or photos; then enable it later in Settings and come back.
- Cancel the picker without choosing anything: the app should return to where it was.
Edge cases
- Boundaries: a file just under, exactly at and just over the size limit; the maximum number of photos and one more.
- Rotation: portrait, landscape, upside down and selfie (front camera) photos. Check thumbnails, full view and the web version.
- Extreme shapes: a very wide panorama, a 1×1 pixel image, a huge-resolution image under the size limit.
- Transparency: a transparent PNG keeps its transparency (or gets an intentional background).
- Limited photo access on iOS and Android 14+: only selected photos are visible, and the user can add more.
- Same photo twice, and double tapping Upload or Post.
Network and app lifecycle
- Upload on a slow network: progress moves, a processing state follows, Cancel works at every stage.
- Switch to airplane modeAirplane modeA device setting that turns off all wireless connections at once.Open in glossary → mid-upload, then back. The photo is kept and resumes or offers Retry.
- Send the app to the background, lock the phone, or take a call during an upload (Interruptions).
- Kill the app mid-upload and reopen it: no half-uploaded, broken image is shown as “done”.
- Watch requests with a proxy or DevToolsDevToolsDeveloper tools built into browsers for inspecting pages, network traffic and errors.Open in glossary →: one request per photo, sensible status codesHTTP status codeA three-digit number in every server response: 200 means OK, 404 not found, 500 server error.Open in glossary →, no duplicates on retry.
Security and privacy
- Uploaded photos have no GPS location if the spec requires stripping. Download one and check its metadata with an EXIF viewer.
- The server rejects renamed non-image files and oversized files, even when you bypass the app (for example by uploading through the website or APIAPIThe way different software parts talk to each other, like an app asking a server for data.Open in glossary → client in a test environment).
- Other users can’t see a photo before the listing is published, and a deleted photo’s URL stops working.
Accessibility
- The upload button has a clear label (“Add photos”), and each thumbnail is announced with its state (“Photo 2 of 3, uploading, 40%”).
- Errors are announced by the screen readerScreen readerSoftware that reads the screen aloud for blind and low-vision users, like VoiceOver or TalkBack.Open in glossary →, not just shown in red.
- On the web, everything works with the keyboard: drag and drop must have a normal button alternative.
- Where users publish images for others (posts, listings), they can add alt textAlt textA text description of an image that screen readers read aloud.Open in glossary →. See Accessibility basics.
Example test cases
TC-UPL-001Upload a HEIC photo from an iPhonepositivehigh
Preconditions
- iPhone camera format set to High Efficiency
- A new photo taken with the camera
Steps
- Open New listing
- Tap Add photos and choose the photo
- Wait for the upload to finish
- Open the listing on the website
Expected result
The photo uploads without errors and is shown correctly in the app and on the website (stored as JPEG).
TC-UPL-002Portrait photo is shown uprightedgehigh
Preconditions
- Photo taken holding the phone upright (EXIF orientation 6)
Steps
- Add the photo to a listing
- Check the thumbnail, the full-screen view and the website
Expected result
The photo is upright everywhere. Stored dimensions are portrait (e.g. 3024 × 4032).
TC-UPL-003Size limit boundariesedgehigh
Steps
- Add a 9.9 MB photo
- Add a photo of exactly 10 MB
- Add a 10.1 MB photo
Test data
Limit: 10 MB per photo
Expected result
9.9 MB and 10 MB upload. 10.1 MB is rejected before uploading with "This photo is larger than 10 MB. Choose a smaller one."
TC-UPL-004Location metadata is removednegativehigh
Preconditions
- A JPEG photo that contains GPS coordinates
Steps
- Upload the photo
- Download the stored image from the listing
- Inspect it with an EXIF viewer
Expected result
No GPS or camera metadata in the stored image. The photo is still upright.
TC-UPL-005Connection lost during uploadnegativehigh
Preconditions
- Network throttled to a slow 3G profile
Steps
- Add a 4 MB photo
- At about 50%, switch on airplane mode
- Wait 10 seconds
- Switch airplane mode off
Expected result
The photo stays in the grid with "Waiting for connection" and finishes uploading after the network returns. Only one copy is stored.
TC-UPL-006Cancel an uploadpositivemedium
Preconditions
- Slow network
Steps
- Add a large photo
- Tap the cancel (×) button while it uploads
Expected result
The upload stops, the photo is removed from the grid, and the server does not store it.
TC-UPL-007Renamed non-image file is rejectednegativehigh
Preconditions
- Test environment
- A plain text file renamed to notes.jpg
Steps
- Upload notes.jpg from Files (app) or the file chooser (web)
Expected result
The upload is rejected by the server with a specific message ("This file isn't a valid image"). Nothing is stored.
TC-UPL-008Photo access deniednegativemedium
Preconditions
- Fresh install
Steps
- Tap Add photos › Take photo
- Choose "Don't Allow" for camera access
- Tap Take photo again
Expected result
No crash. The app explains why it needs the camera and offers a button that opens the app's Settings page.
Tip: click a test case to expand it.
Mobile vs web
- Photo pickers: the iOS system picker and the Android Photo Picker don’t need library permission. Apps that build their own gallery do, and must handle full, limited and denied access. On Android, test both new and older OS versions, because the permission model changed several times (iOS vs Android).
- HEIC: iPhones save HEIC by default. Some apps let the system convert to JPEG when picking; others send HEIC as-is. Test with a real iPhone photo, not a downloaded JPEG.
- Camera: test the front and back cameras, flash, and rotating the phone while the camera is open.
- Getting test photos onto devices: drag images onto the iOS Simulator, or
adb push photo.jpg /sdcard/Pictures/on Android (the gallery may need a moment, or a reboot of the emulator, to show it). - Background uploads: lock the phone or switch apps mid-upload. Does it finish, resume or fail clearly?
- Storage and memory: picking ten 48-megapixel photos at once can crash apps that load full-size images into memory. Try it on an older, low-memory device.
- Network: use the Network Link Conditioner (iOS) or emulator network settings (Android), and airplane mode on real devices (Network conditions).
- File chooser: check the
acceptfilter (does the dialog hide non-images?), multiple selection, and that the “All files” option still gets validated. - Drag and drop: drop one file, several files, a folder, a non-image, and a file onto the page outside the drop zone (the browser shouldn’t just open the image and leave your page).
- Paste: paste a screenshot from the clipboard with Ctrl/Cmd+V, if the feature supports it.
- Mobile browsers: on phones the file input offers camera, photo library and files. iOS Safari may convert HEIC to JPEG on the way (depending on the input’s
acceptlist); don’t assume, check what the server received. - DevTools: throttle the network, watch the upload request, its size and status code (DevTools for testers). Try going offline from the Network panel mid-upload.
- Leaving the page: refreshing or closing the tab during an upload should warn the user or resume cleanly.
- Cross-browser: check Chrome, Safari and Firefox, especially for HEIC, WebP and EXIF rotation.
Checklist
Image upload checklist
0 of 14 checked
Check your understanding
Quick quiz
0/6 answered · 0 correct
1.An iPhone user's portrait photo shows up sideways after upload, but landscape photos are fine. What is the most likely cause?
2.Uploads from iPhones fail, uploads from Android phones work. Which format should you test first?
3.The limit is 10 MB. Which set of files gives the best boundary test?
4.Why is location data in an uploaded photo a problem on a marketplace?
5.You rename notes.txt to notes.jpg and the server accepts it. What does this tell you?
6.A photo is uploading on a weak signal and the user loses connection. What is the best behavior?
Key takeaways
- Upload is a flow: source, permission, client checks, preparation (resize, rotate, strip metadata), upload, server validation, result. Bugs hide in every step.
- Keep a test kit: HEIC, portrait, huge, just-under-the-limit, transparent PNG, GIF, WebP, a photo with GPS, a 0-byte file and a renamed non-image.
- The classic bugs: HEIC rejected, sideways photos, hidden size limits, no cancel or progress, silent loss when offline, and location data left in the file.
- Test uploads on slow and lost networks and while the app goes to the background.
- Never trust the client: the server must check the real content, the size and the type, and errors must say exactly what’s wrong.
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 ⚡