Topics
Recent articles

Android & Mobile

Upload Photos from Android to Google Drive Safely

Design a reliable Android photo upload flow for Google Drive with explicit ownership, narrow OAuth scope, stable folder IDs, retries, and observable upload state.

Table of Contents12 sections
Developer holding an Android phone beside a laptop showing a secure photo upload workflow
Reliable photo upload starts with explicit ownership, persisted state, and retry-safe remote identity.

Uploading a photo from Android to Google Drive is straightforward only if you ignore the parts that usually fail in production.

The reliable architecture separates four concerns: acquiring the image, deciding where it belongs, uploading it with the narrowest practical authorization, and persisting enough state to retry safely. Treat Drive as a remote system, not as a folder that happens to live on the internet.

A useful flow looks like this:

Capture or select -> persist local upload record -> resolve destination folder -> upload -> store Drive file ID -> mark complete

That small state machine is more important than the API call itself.

Start With Ownership and Authorization

Before writing upload code, decide who should own the uploaded file.

With user OAuth, a file created in My Drive belongs to that user. Google also documents service-account ownership separately, which has different lifecycle and visibility implications. That makes authentication an architecture decision rather than a networking detail. See Google’s Drive file creation guidance before choosing an account model.

For an Android app acting on files the user intentionally creates or selects, prefer the narrowest scope that satisfies the workflow. Google’s current OAuth scope catalog describes drive.file as access to specific Drive files used with the app, while the broader Drive scope can see and manage all Drive files.

Do not request broad Drive access merely because it makes development easier. Broader authorization increases the consequences of a credential or implementation mistake and can make consent harder to justify.

Keep Capture Separate From Upload

A camera or gallery result should not immediately become a long network operation owned by the screen.

Persist an upload record first. A minimal model can contain:

data class PendingUpload(
    val localId: String,
    val contentUri: Uri,
    val targetFolderId: String?,
    val remoteFileId: String?,
    val state: UploadState
)

The UI can create that record after capture or selection and then enqueue background work. The worker owns the network attempt. The screen observes persisted state.

This separation gives you three useful properties:

  1. Rotating or leaving the screen does not redefine the upload.
  2. A failed request can be retried without asking the user to pick the image again.
  3. The app has one place to explain whether a photo is queued, uploading, complete, or failed.

The same principle appears in reliable Android delivery pipelines: persist the identity of the thing being processed, then let asynchronous work move it through explicit states. If you are designing background execution generally, the WorkManager and AlarmManager guide is a useful companion.

Store Folder IDs, Not Folder Names

Drive folders are files with the folder MIME type. When creating a file inside a folder, Google Drive uses the folder ID in the file’s parents property. A file has a single parent folder in the current Drive model. Google’s folder guide documents this directly.

That means a destination such as Customer Photos/2026/September should not be treated as a filesystem path.

If your product has naming rules, resolve them into Drive folder IDs and persist those IDs. Names are presentation. IDs are identity.

Avoid searching by name before every upload. Duplicate folder names are valid, and repeated search calls make retries harder to reason about.

Make Retries Idempotent

The dangerous failure is not “upload failed.” It is “the server accepted the upload, but the client did not record the response.”

If the app retries blindly, one photo can become two Drive files.

The Drive API provides generated file IDs that can be supplied when creating supported files. Google’s file creation guide notes that retrying creation with a successfully used pre-generated ID results in a conflict instead of another file. That can be useful when your workflow needs strong duplicate protection.

Even when you do not pre-generate a Drive ID, keep a stable local operation ID and persist the returned remote file ID immediately. Your retry policy should distinguish a request that definitely never reached Drive, an unknown result, an existing remote file with incomplete local state, a retryable response, and an authorization problem that requires user action.

Do not turn every exception into an automatic retry.

Use WorkManager for Deferrable Uploads

Photo uploads are usually deferrable background work rather than exact-time work. That makes WorkManager a natural owner when the upload must survive normal UI lifecycle changes and should run under network constraints.

The worker should receive a stable local upload ID, not an in-memory bitmap and not a large serialized payload.

class PhotoUploadWorker(
    appContext: Context,
    params: WorkerParameters,
    private val repository: UploadRepository
) : CoroutineWorker(appContext, params) {

    override suspend fun doWork(): Result {
        val uploadId = inputData.getString("upload_id")
            ?: return Result.failure()

        return repository.upload(uploadId)
    }
}

The repository can load the content URI, destination folder ID, authorization state, and previous remote ID from persistent storage. This keeps the worker input small and makes a retry reconstructable.

For large files or unreliable networks, choose the Drive upload strategy deliberately rather than hiding it behind a generic upload() abstraction. The important boundary is that transport details stay below the persisted operation model.

Treat Content URIs as Capabilities

Android photo pickers and document providers commonly give the app a content URI, not a permanent filesystem path.

Do not build the upload layer around “real path” conversion. Open the content through ContentResolver, copy it into app-owned storage when the workflow needs durable access, and record which copy is authoritative.

This matters when background work may run after the original screen has disappeared. A URI that worked during selection is not automatically a durable promise for every future execution context.

A practical policy is to upload immediately when access remains valid for the operation, persist URI permission when the provider and flow support it, or copy the selected content into app-owned temporary storage before enqueueing durable work.

Delete temporary files only after the upload reaches a terminal state that no longer needs them.

Make Folder Creation a Repository Concern

UI code should express intent such as “upload this receipt to this logical destination.” It should not know Drive query syntax or folder MIME types.

A repository boundary can expose something closer to:

interface PhotoBackupRepository {
    suspend fun enqueuePhoto(source: Uri, destination: Destination): String
    fun observeUpload(id: String): Flow<UploadStatus>
}

Internally, the repository coordinates local persistence, destination resolution, Drive calls, and retry state.

This boundary also makes it possible to replace Drive later. A product requirement that starts as “put images in Drive” can eventually become object storage, a backend upload endpoint, or another document provider. The UI should not need a rewrite when storage ownership changes.

If you are using lightweight remote storage for other app records, compare this boundary with the Google Sheets Android backend architecture. The common lesson is to keep a convenient external service behind an application-owned contract.

Surface Progress Without Pretending It Is Precise

Users care about whether their photo is safe, not about a fake progress bar.

If the transport exposes meaningful byte progress, show it. Otherwise prefer states such as Waiting for network, Uploading, Uploaded, Sign-in required, and Retry available.

An indeterminate progress indicator is more truthful than inventing a percentage from unrelated milestones.

For multiple photos, show progress per item. One failed upload should not erase evidence that the other nine succeeded.

Design the Failure Matrix Before the Happy Path

A useful implementation review asks what happens for each failure class.

Failure Automatic retry? User action? State to preserve
Temporary network loss Usually No Local URI, destination, operation ID
Server-side transient error Usually, with backoff No Same upload identity
OAuth expired Depends on auth layer Possibly Pending upload
Access revoked No blind loop Yes Pending upload and reason
Source no longer readable No Yes Failure reason
Destination removed Policy-dependent Maybe Logical destination
Unknown result after request Reconcile first Usually no Operation and possible remote ID

This table is more valuable than adding retries after QA reports duplicates.

Verify the Whole Contract

Unit tests should cover the state machine independently of Google Drive. Test transitions from queued to uploading, retryable failure, user-action failure, and complete.

Integration tests should verify the assumptions you cannot prove locally:

Do not use a production Drive account as an informal test fixture. Keep test ownership and cleanup explicit.

A Small Architecture Beats a Clever Upload Call

The Drive API call is only one step in a reliable Android upload feature.

The durable design is to persist the operation, use stable remote identities, request narrow authorization, isolate Drive behind a repository, let background work own retries, and make failure states visible.

That architecture is slightly more work than calling files.create() from a ViewModel. It is also the difference between a demo that uploads one photo and a feature that can survive process recreation, weak networks, duplicate retries, changed permissions, and months of maintenance.

For the API-level details, keep Google’s Drive overview, file creation guide, and OAuth scope catalog close to the implementation. The application architecture should make those details replaceable rather than spread them across the UI.

Continue Exploring

You Might Also Like

View all articles