All articles

Guide

How to Handle File Uploads and Downloads in a TWA

August 16, 2026 · 7 min read

A common friction point when converting a Progressive Web App (PWA) into an Android application package is managing file system operations. When using standard WebView wrappers, handling uploads and downloads requires substantial native development. Developers must manually implement file choosers, override download listeners, and manage complex runtime Android permissions.

By contrast, a Trusted Web Activity (TWA) delegates web rendering and execution to the system browser, typically Google Chrome. This design choices simplifies file handling, as Chrome manages standard HTML upload inputs and file download prompts natively. However, specific configurations, storage policies, and edge cases must be addressed to ensure seamless operations across all Android device versions.

How File Uploads Function within a TWA

In a TWA, standard HTML file input elements behave exactly as they would in a modern mobile browser. When a user interacts with a file upload interface, Chrome intercepts the interaction and presents the system-level document picker automatically.

This mechanism supports standard HTML5 attributes, allowing you to control the device behavior directly from your web codebase. For example, you can target specific media sources or limit selectable file types using simple markup configurations:

<input type="file" accept="image/*" />

If you want to suggest that the user capture media immediately using their device camera instead of choosing from the library, you can utilise the HTML5 capture attribute:

<input type="file" accept="image/*" capture="environment" />

The TWA shell handles the hand-off to the Android system camera app transparently. No native Java or Kotlin overrides are required to allow users to select, snap, or upload files within your interface.

Managing File Downloads in the Chrome Sandbox

While uploads require minimal platform-specific adjustment, handling downloads inside a TWA requires an understanding of how the Android sandbox interacts with browser downloads. Because a TWA is structurally a wrapper around the system browser, downloads are executed within Chrome's context, not inside a separate native application environment.

When a user triggers a download link inside your TWA, Chrome processes the download stream in the background. The file is placed directly in the native system Downloads folder. The system notification tray will display the standard browser download progress bar and fire a system-wide completion event.

However, to ensure this flow works perfectly across different device environments, you must ensure that your web application serves downloads using robust HTTP standards. Avoid relying on complex client-side generation mechanisms when targeting a native package environment unless you have tested their compatibility extensively.

The Challenge of Blob URLs and In-Memory Data

A common issue when running PWAs inside a TWA shell is the failure of client-side generated files using Blob URLs. Developers frequently construct Blobs and initiate downloads in client-side JavaScript using schemes like the one below:

blob:https://yourdomain.com/a1b2c3d4

Because a TWA utilizes Chrome Custom Tabs, passing a dynamic in-memory pointer to the system Download Manager can occasionally fail. The operating system may lack context to access the browser's sandboxed memory allocation. To resolve this, configure your application to serve downloads directly from a secure web server using standard anchor elements with static routes. Alternatively, convert the data on the client side to a structured Base64 Data URI scheme, though physical file server endpoints remain the most reliable standard.

TWA vs WebView File Handling Comparison

Understanding the architectural differences in how files are processed helps developers select the appropriate container technology and avoid common configuration pitfalls.

CapabilityTrusted Web Activity (TWA)Standard WebView Wrapper
File Input HandlingNative browser integration. Full file picker UI works automatically.Requires overriding onShowFileChooser in WebChromeClient via native code.
Camera Access (Capture)Uses browser camera permissions directly. No custom native code needed.Requires manually declaring camera permissions and building native intents.
Download PipelineChrome handles download stream. Files go directly to Downloads directory.Requires custom DownloadListener implementation in Java/Kotlin.
File Type SupportSupports all MIME types recognized by the underlying browser.Requires manual MIME-type mapping and storage permission management.

Understanding Android Storage Permissions

Modern Android operating systems enforce strict sandboxing policies via Scoped Storage. This design limits the access applications have to files stored outside of their private directory structures.

Historically, Android apps required explicit declarations in the manifest to write and read from external media directories. Under Scoped Storage rules, apps do not require any permission declarations to save files into the public Downloads directory using Chrome Custom Tabs. Because Chrome manages the write process, your TWA application itself never directly touches the physical device file storage system.

If your application requires advanced storage operations, such as saving files to custom directories outside the system-allocated standard paths, you cannot rely purely on standard browser download mechanisms. In these situations, using the native Android download manager through custom intent filters is necessary, but for standard document and media delivery, no native permissions are required in your manifest.

Optimising File Inputs for Best Mobile UX

To ensure file operations feel as native as possible to the end-user, apply web-level optimisations that adapt specifically to mobile web views. Large images can easily cause out-of-memory states on budget devices during upload processing.

To mitigate this risk, implement client-side image compression using modern Canvas API methods before sending the payload to your API. Compressing a multi-megapixel photo down to a reasonable file size reduces request size, limits user bandwidth consumption, and prevents TWA container crashes during heavy payload operations.

Furthermore, ensure your web forms clearly display active upload states. When Chrome handles a file upload, there is a momentary transition between selecting a file and the web application processing it. Implementing a visual upload progress indicator inside your web interface keeps users informed, preventing repeated form submissions or premature application closures.

Ready to ship your Android app?

Paste your PWA URL, get a signed APK and a Google Play ready AAB in minutes.

Build my app