A mobile upload progress bar reaching 100% does not prove that an image was attached to its draft. The connection may fail after object storage accepts the bytes but before the backend confirms the relationship. A signed upload URL may also expire while the task is waiting.

Separate selection, transfer and business confirmation. Keep a stable task identity and make retry safe for the same attachment. This avoids treating every network interruption as a completely new upload.

Distinguish transferred bytes from a completed action

Consider a hypothetical notes app that uploads an image and attaches it to a note. The phone, application API and object store have separate responsibilities. Receiving an object does not by itself establish which account and draft may use it.

A presigned URL delegates an operation subject to the provider's conditions and lifetime. Amazon S3 describes these URLs as bearer tokens that should be protected. Do not include the full URL in analytics, public logs or a shareable error message.

An upload session connects source selection, object transfer and backend confirmation

Create the task before transmitting the file

One possible API contract starts by creating an upload session. The backend records its owner and intended draft; the client preserves the identifier through retry. These endpoint names illustrate responsibilities rather than prescribing an implementation.

POST /upload-sessions
  client_operation_id, draft_id, media_type, expected_size

PUT the URL supplied by the backend
  send the file under the signed conditions

POST /upload-sessions/{id}/complete
  ask the backend to verify and attach the object

The backend must check ownership rather than trusting a draft identifier supplied by the client. It also needs to validate the expected object and current draft state. A late upload should not silently recreate a draft the person already deleted.

Keep the distinction between a task and a selected image. Replacing an image should establish which task is now current, so an earlier upload cannot overwrite the later choice when its response finally arrives.

Classify failures before retrying

ObservationQuestion to answerRecovery to design
Connection unavailableIs the local source still readable?Preserve the task and wait appropriately
Signed request rejectedExpired permission or mismatched conditions?Ask the backend to inspect and renew when allowed
Transfer may have succeededDoes the expected object exist?Reconcile before sending again
Completion request repeatedIs this session already confirmed?Return its consistent accepted result
Person replaced the imageDoes the task still own the selection?Do not attach an obsolete result

Do not translate every 403 into URL expiration. Signature conditions, permissions and request headers can also matter. Retain a safe error category and session identifier for diagnosis without exposing the signed credential.

If completion is retried after a lost response, the service should recognize an already completed session. Creating a fresh attachment record each time simply moves the duplicate-delivery problem from the transfer layer to the application database.

Decide whether multipart upload earns its complexity

S3 documents multipart upload as initiation, individual part uploads and completion or cancellation. That can help manage larger transfers, but adds state and cleanup responsibilities. It does not automatically make an application resilient to interruption.

Start with the actual file sizes, mobile platform behavior and network conditions you need to support. A small avatar may not justify a multipart state machine. If you do use one, persist the upload identifier and relevant part state, and define how abandoned transfers are canceled or cleaned up.

Do not leave cleanup solely to whether the person returns to the app. A task can be abandoned permanently, so the storage and backend design need their own retention rules.

Test controlled interruption points

Disconnect before sending, during transfer and after the store accepts the object. Each point tests a different assumption. For the last one, verify that retry produces one intended attachment rather than relying only on the message shown in the interface.

Close the app while the task waits. Change accounts, delete the draft and replace the selected image. The interface should preserve unrelated text, distinguish waiting from source reselection, and explain what retry will attempt.

For Android sources, see Photo Picker URI access for background uploads. For queued changes and operation identity, read outbox synchronization with WorkManager.

Frequently asked questions

Is an HTTP success from object storage the final success condition?

Only if your product contract defines it that way. A flow that subsequently attaches an object to a draft still needs business confirmation and ownership checks.

Does extending the signed URL lifetime solve the problem?

No. It does not repair a missing source, mismatched signature conditions, duplicate completion or a changed draft. Give each state an appropriate recovery path.

Primary sources

Check these original references again before making a release or policy decision.