Skip to content

The File Handling Guide for Capacitor

Handling files in Capacitor can be a crucial part of your app. Whether you want to read, write or share a file, it is essential to understand the best practices in file handling to avoid potential out of memory (OOM) issues. In this guide, we will explore what you need to consider when dealing with files on Android and iOS and how to ensure efficient and reliable file management.

Problem

Caused by: java.lang.OutOfMemoryError: Failed to allocate a 268431376 byte allocation with 100663296 free bytes and 123MB until OOM, target footprint 239514824, growth limit 268435456

This and similar errors are often caused by inefficient file handling. The most common mistake is to load a file into the WebView as a base64 string or data URL. This can quickly lead to OOM errors, especially when dealing with large files.

Best Practices

Capacitor provides powerful capabilities for working with files in a cross-platform app environment. The following best practices will help you to avoid potential pitfalls and ensure efficient and reliable file management.

Read a file

When reading a file, you should make sure that the file is not loaded into the WebView as a base64 string or data URL. So forget about the readFile(...) method of the Capacitor Filesystem plugin. Instead, use the fetch API to load the file as a blob:

import { Camera, CameraResultType } from '@capacitor/camera';

const getPhotoAsBlob = async () => {
  // 1. Pick a photo
  const photo = await Camera.getPhoto({
    resultType: CameraResultType.Uri
  });
  // 2. Load the photo as a blob
  const response = await fetch(photo.webPath);
  return response.blob();
};

A blob is a file-like object that can be used for further operations. As soon as the in-memory space for blobs is getting full, the blob system will automatically uses the disk.1

Tip

In most cases, you don't need to load a file into the WebView. Almost all file operations can also be performed directly on the file system. For example, you should not load a file into the WebView just to upload it to the server via JavaScript. Instead, there are already many plugins that upload the file directly from the file system to the server.

Write a file

If you do not load any files into the WebView, writing a file via the WebView is usually not necessary. Nevertheless, if you want to write a file via the WebView, you should make sure that the file is not written as a base64 string or data URL. So again, forget about the writeFile(...) method of the Capacitor Filesystem plugin. Instead, use a Capacitor plugin that supports writing a file as a blob. For example, you can use the Capacitor Blob Writer plugin:

import { Directory } from "@capacitor/filesystem";
import write_blob from "capacitor-blob-writer";

const writeBlob = async () => {
  const blob = new Blob(["Hello world!"], { type: "text/plain" });
  await write_blob({
    path: "notes/hello.txt",
    directory: Directory.Data,
    blob: blob,
  });
};

Another option is to use the Capacitor File Chunk plugin.

Warning

Writing a file as a blob via the WebView needs a local HTTP server to be started, which is associated with potential security risks. You can find more information in the documentation of the respective plugin.

You can now use the path to the selected file to open, share or upload the file with another plugin.

Display a file

If, for example, the file is an image that you want to display in the WebView, you can just use the webPath directly as the image source:

import { Camera, CameraResultType } from '@capacitor/camera';

const displayPhoto = async () => {
  // 1. Pick a photo
  const photo = await Camera.getPhoto({
    resultType: CameraResultType.Uri
  });
  // 2. Display the photo
  document.getElementById("savedPhoto").src = photo.webPath;
};
<img id="savedPhoto" />

If no webPath is provided, you can create the webPath yourself by using the convertFileSrc(...) method:

import { Capacitor } from '@capacitor/core';

const convertFileSrc = () => {
  return Capacitor.convertFileSrc('file:///path/to/device/file');
};

Open a file

At some point, you may want to open a file with another app. For example, you may want to open a PDF file with a PDF viewer app. To do this, you can use the Capacitor File Opener plugin:

import { FileOpener } from '@capawesome-team/capacitor-file-opener';

const openFile = async () => {
  await FileOpener.openFile({
    path: 'file:///path/to/device/file',
  });
};

Tip

On iOS, the UIDocumentInteractionController is used to preview and open files. If you would rather give the user the option to choose the app to open the file with, you can just use the Capacitor Share plugin to share the file with another app.

Pick a file

When picking a file, it is recommended to use the Capacitor File Picker plugin. This way, you can just get the path to the selected file without the need to load the file into the WebView:

import { FilePicker } from '@capawesome/capacitor-file-picker';

const pickFile = async () => {
  const result = await FilePicker.pickFiles();
  return result.files[0].path;
};

Of course, you can also use the HTML <input type="file"> element to pick a file. However, you then have the problem that you first have to write the file to the file system before you can process it with another plugin.

Download a file

When downloading a file, you should make sure that the file is not loaded into the WebView as a base64 string or data URL. It is best to use the Capacitor Filesystem plugin to download the file directly to the file system:

import { Filesystem, Directory } from '@capacitor/filesystem';

const downloadFile = async () => {
  await Filesystem.downloadfile({
    path: 'image.png',
    url: 'https://example.tld/image.png',
  });
};

Conclusion

In this guide, we have explored the best practices for handling files in Capacitor. By following these best practices, you can avoid potential pitfalls and ensure efficient and reliable file management. If you have any questions or feedback, feel free to reach out to us.