Android SharedPreferences Tutorial and Example

Multipart file uploads from Android

Android applications regularly send user-selected files to a backend, including profile images, receipts, evidence, and signed documents. HTTP multipart requests let one request carry binary attachments alongside fields such as category or customer ID, making them a natural fit for Android file upload workflows.

Each part has its own headers, content type, and content, separated by a generated boundary. Android should stream bytes from a content URI instead of loading the whole file into memory. This keeps consumption predictable on modest devices and works with files exposed by document providers.

Australian deployments make regional hosting and privacy especially relevant. Sydney, Melbourne, and Brisbane host major cloud regions, giving local teams practical data-residency choices for customer documents. Australian education, health, and field-service apps also need careful handling of sensitive records and uploads over variable mobile connections.

This guide uses Kotlin, Retrofit, OkHttp, and Android's Storage Access Framework. It covers selecting files, streaming request bodies, reporting progress, deciding when retries are safe, protecting data, and validating the result against a deployed service. The same request model also transfers cleanly to Java or plain OkHttp.

How a multipart request is organised

Multipart differs from sending raw bytes or embedding a file in JSON. It supports several attachments, ordinary form fields, and server-side validation without a custom envelope. A photo might use name="file" with image/jpeg, while metadata might use name="purpose" with invoice.

Let OkHttp generate the boundary and delimiters; manually concatenating them can make the request unparsable. Treat field names, part order, size limits, and required values as the API contract. Read the backend's parser documentation because multipart handling differs between frameworks and servers.

Select and prepare a file

Use ACTION_OPEN_DOCUMENT when the user chooses an attachment. Persist the returned URI permission if work may continue later, and query MediaStore only when shared media requires it. The system picker avoids broad media permission on recent Android releases.

Read the display name and MIME type through ContentResolver, but treat both as untrusted. Providers may report misleading types, and filenames can contain spaces, accents, or path-like characters. Check the byte length against the service limit, generate a safe server filename, and preserve the original name only for display.

Build the upload request

Retrofit's @Multipart annotation maps a method to a multipart request. A MultipartBody.Part carries a file, while RequestBody values represent ordinary fields. Send authentication through an OkHttp interceptor rather than embedding tokens in every method.

interface UploadApi {
    @Multipart
    @POST("v1/files")
    suspend fun upload(
        @Part("purpose") purpose: RequestBody,
        @Part file: MultipartBody.Part
    ): Response<UploadResponse>
}

Create a custom RequestBody for large files so writeTo can copy the provider stream in chunks; contentLength should return a known size or -1. Call the suspend function from a lifecycle-aware coroutine and prevent duplicate submissions while it runs. A successful HTTP response still needs parsing: retain the returned file identifier and surface any server validation message.

Compare the available approaches

Most apps should use Retrofit's multipart support because field names stay visible and coroutine integration is straightforward. Raw OkHttp gives precise control but adds boilerplate, while JSON with Base64 content suits tiny text records at the cost of larger payloads.

Approach Best fit Main advantage Main caution
Retrofit multipart Standard API endpoints Clear field mapping Needs a streaming body for large files
Raw OkHttp Custom queues and headers Full transport control More setup and error handling
JSON with Base64 Small text records Simple payload shape About 33% larger and memory-heavy
Presigned object upload Direct cloud storage Scales large files Does not use the server's multipart endpoint

Choose the approach that matches the endpoint rather than the easiest local demo. A multipart application API remains the right choice when the server must coordinate metadata, permissions, and virus scanning. Confirm whether it accepts one file or repeated file parts before coding.

Report progress and handle failure

Progress can be reported by counting bytes written to the sink and exposing a Flow or callback to the UI. Display bytes and percentage, but avoid updating the interface after every small chunk. Cancel the coroutine when the user leaves the screen unless the upload has been handed to durable background work.

Retries need an idempotency key because reconnecting after a lost response can otherwise create duplicate records. Give each logical upload a stable identifier and let the server reject or deduplicate repeats. Configure sensible connect and write timeouts, and distinguish a retryable network failure from a rejected file, a missing field, or an expired session.

Secure the transfer and stored file

Use HTTPS for every part, including small metadata fields, and reject cleartext traffic in the Android network security configuration. Never log file contents, signed URLs, access tokens, or personal details during debugging. Store temporary credentials in Android's protected storage and revoke them after upload.

Australian privacy duties may apply when the app handles identifiable records under the Privacy Act and Australian Privacy Principles. Obtain consent where required, minimise collected metadata, define retention periods, and restrict staff access. A common Australian deployment custom is keeping records in an Australian jurisdiction, often Sydney or Melbourne, while still planning for outages and regional users.

Verify the complete workflow

Test the Android client against a local mock server, then repeat the contract against a staging endpoint. Parse the actual multipart request to verify boundaries, field names, filenames, content types, and body length. Unit-test filename sanitisation and response handling separately, since device tests alone can miss server parser differences.

For acceptance coverage of the deployed API, end-to-end test tooling can submit a controlled sample and check the server response and stored object. Include filenames with spaces and Unicode, empty files, oversized images, false MIME types, expired tokens, and a dropped connection. Repeat the run from Perth, Adelaide, and Darwin on mobile broadband to expose latency and upload variability.