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
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;
};
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.
Upload a file¶
Uploading a file is actually quite simple and does not require any plugins. We just need to read the file as a blob and then upload it via the fetch API:
import { Camera, CameraResultType } from '@capacitor/camera';
import { Capacitor } from '@capacitor/core';
const uploadFile = async (path: string) => {
// 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);
const blob = response.blob();
// 3. Upload the file
const formData = new FormData();
formData.append('file', blob, 'file.jpg');
await fetch('https://example.tld/upload', {
method: 'POST',
body: formData,
});
};
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.