When we take a photo on an iOS device, the resulting file is stored inside of a temporary folder called tmp
. If we use the Camera API to retrieve a photo, the resulting URI will look like this:
file:///private/var/mobile/Containers/Data/Application/5C3AFB5A-8A33-4152-932D-32889401E966/tmp/photo-1.jpg
If we are interested in long-term storage then this isn’t the best solution. The way you approach this will depend on what it is your application is trying to achieve. You might want to send this photo to a server, or maybe you want to store the base64
data that is returned, in which case it isn’t going to matter if this temporary file gets deleted. However, for this example, we are going to rely on displaying a photo in an application by referencing the URI of the file on the device directly.
In this tutorial, we will be covering how to use Capacitor to retrieve a photo from the user’s camera or photo library, and then moving that photo into a permanent storage folder. I use the term “permanent” loosely, as of course data can always be deleted. If it is important that the photos in your application are never lost, you should investigate also storing them elsewhere (e.g.on a server). There is also the option to save a copy of any photos your users take to the user’s own photo library, so if the data ever is lost at least they will have their own copy of it.
In order to take a photo and move it to permanent storage, we will need to use both the Camera
API and the Filesystem
API that Capacitor provides. I’ve dicussed using the Camera API with Ionic before, but the Filesystem API will allow us to work directly with files on the device – we can create files, modify files, delete files, create directories, and more. One of the goals of Capacitor was to implement a Filesystem API that would be easier to use than the existing File API in Cordova.
1. Install Capacitor
In order to work with Capacitor, you will need to install its dependencies and initialise a project. The generic way to add Capacitor to your application is described here: Adding Capacitor to an existing web app - however, if you are using the Ionic CLI you can also use the ionic integrations enable capacitor
command.
Whether you are using Ionic/Angular, Ionic/Stencil, or something else, it doesn’t really have any bearing on the result. All we really need here is the Camera API and FileSystem API from Capacitor.
2. Import the Camera and Filesystem APIs
As I mentioned, we are going to need to use both the Camera
and Filesystem
APIs. The Camera API will allow us to retrieve a photo from the user, and the Filesystem API will allow us to move it into permanent storage.
To set these up, you will need to import the following:
import {
Capacitor,
Plugins,
CameraResultType,
FilesystemDirectory,
} from '@capacitor/core';
and create a reference to the Camera
and Filesystem
:
const { Camera, Filesystem } = Plugins;
We are also importing the Capacitor
runtime itself, which we will need in order to access Capacitor’s convertFileSrc
method which converts the URI returned by the Filesystem API into one that is readable/displayable in our webview.
3. Take a Photo
In order to achieve the functionality we want, we mostly just use the Camera API as we usually would, with one important distinction:
const options = {
resultType: CameraResultType.Uri,
};
Camera.getPhoto(options).then(
(photo) => {
console.log(photo);
},
(err) => {
console.log(err);
}
);
It is important that we supply the resultType
option and use the CameraResultType.Uri
value. If we supply this option, the result we receive will contain a path
property that contains the path to the file on the device (in the temporary folder). We need this in order to copy it elsewhere.
4. Move the Photo to Permanent Storage
With a reference to the photo in the temporary folder, we can move it to another folder using the Filesystem API. Let’s take a look at how to do that, and then talk through it:
takePhoto(){
const options = {
resultType: CameraResultType.Uri
};
Camera.getPhoto(options).then(
photo => {
Filesystem.readFile({
path: photo.path
}).then(
result => {
let date = new Date(),
time = date.getTime(),
fileName = time + ".jpeg";
Filesystem.writeFile({
data: result.data,
path: fileName,
directory: FilesystemDirectory.Data
}).then(
() => {
Filesystem.getUri({
directory: FilesystemDirectory.Data,
path: fileName
}).then(
result => {
let path = Capacitor.convertFileSrc(result.uri);
console.log(path);
},
err => {
console.log(err);
}
);
},
err => {
console.log(err);
}
);
},
err => {
console.log(err);
}
);
},
err => {
console.log(err);
}
);
}
OR if you prefer you can use the async/await
syntax which simplifies the code a bit:
async takePhoto() {
const options = {
resultType: CameraResultType.Uri
};
const originalPhoto = await Camera.getPhoto(options);
const photoInTempStorage = await Filesystem.readFile({ path: originalPhoto.path });
let date = new Date(),
time = date.getTime(),
fileName = time + ".jpeg";
await Filesystem.writeFile({
data: photoInTempStorage.data,
path: fileName,
directory: FilesystemDirectory.Data
});
const finalPhotoUri = await Filesystem.getUri({
directory: FilesystemDirectory.Data,
path: fileName
});
let photoPath = Capacitor.convertFileSrc(finalPhotoUri.uri);
console.log(photoPath);
}
There are three stages here. First, we read the data from the file that was created by the Camera API. We then use that data to write a new file into the data directory. We use the current time to create a unique file name. Then we use getUri
to return the URI of the file that we just created in the data directory. I suspect in future we might be able to grab that URI directly from the result of the writeFile
method, but for now, this method does not return data about the file that was written.
If we take a look at the URI that is returned, it will look something like this (on Android):
file:///data/user/0/com.joshmorony.example/files/1564467264719.jpeg
If we want to display this image in an Ionic application it won’t work, as we cannot display an image using an <img>
tag that loads over the file://
protocol. However, Capacitor provides a way to access these files that is compatible with a web view. All we need to do is run the result through Capacitor.convertFileSrc
.
which will result in a URI like the following:
http://localhost/_capacitor_file_/data/user/0/com.joshmorony.example/files/1564467264719.jpeg
You could now safely display that inside of an <img>
tag:
<img
src="http://localhost/_capacitor_file_/data/user/0/com.joshmorony.example/files/1564467264719.jpeg"
/>
Summary
We now have our photo stored outside of the tmp
folder which is more likely to be wiped by the operating system at some point. Although we are specifically looking at moving a photo around the file system in this example, the Filesystem API is very powerful and can be used in a multitude of situations with different file types. To see the other types of file operations that this API can perform, you should take a look at the documentation.