将照片保存到文件系统
我们现在可以拍摄多张照片,并在我们应用的第二个标签页中以照片库的形式显示它们。然而,这些照片目前还没有被永久存储,所以当应用关闭时,它们将被删除。
🌐 We’re now able to take multiple photos and display them in a photo gallery on the second tab of our app. These photos, however, are not currently being stored permanently, so when the app is closed, they will be deleted.
文件系统 API
🌐 Filesystem API
幸运的是,将它们保存到文件系统只需要几个步骤。首先在 usePhotoGallery.ts 中的 usePhotoGallery() 方法中创建一个新的类方法 savePicture()。
🌐 Fortunately, saving them to the filesystem only takes a few steps. Begin by creating a new class method, savePicture(), in the usePhotoGallery() method in usePhotoGallery.ts.
import { useState } from 'react';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
// CHANGE: Add import
import type { Photo } from '@capacitor/camera';
export function usePhotoGallery() {
// ...existing code...
// CHANGE: Add the `savePicture()` method
const savePicture = async (photo: Photo, fileName: string): Promise<UserPhoto> => {
return {
filepath: 'soon...',
webviewPath: 'soon...',
};
};
return {
addNewToGallery,
photos,
};
}
export interface UserPhoto {
filepath: string;
webviewPath?: string;
}
我们可以立即在 addNewToGallery() 中使用这种新方法。
🌐 We can use this new method immediately in addNewToGallery().
import { useState } from 'react';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import type { Photo } from '@capacitor/camera';
export function usePhotoGallery() {
const [photos, setPhotos] = useState<UserPhoto[]>([]);
const addNewToGallery = async () => {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100,
});
const fileName = Date.now() + '.jpeg';
// CHANGE: Add `savedImageFile`
// Save the picture and add it to photo collection
const savedImageFile = await savePicture(capturedPhoto, fileName);
// CHANGE: Update state with new photo
const newPhotos = [savedImageFile, ...photos];
setPhotos(newPhotos);
};
const savePicture = async (photo: Photo, fileName: string): Promise<UserPhoto> => {
return {
filepath: 'soon...',
webviewPath: 'soon...',
};
};
return {
addNewToGallery,
photos,
};
}
export interface UserPhoto {
filepath: string;
webviewPath?: string;
}
我们将使用 Capacitor Filesystem API 来保存照片。首先,将照片转换为 base64 格式。
🌐 We'll use the Capacitor Filesystem API to save the photo. First, convert the photo to base64 format.
然后,将数据传递给文件系统的 writeFile 方法。回想一下,我们是通过将图片的源路径(src)设置为 webviewPath 属性来显示照片的。因此,设置 webviewPath 并返回新的 Photo 对象。
🌐 Then, pass the data to the Filesystem's writeFile method. Recall that we display photos by setting the image's source path (src) to the webviewPath property. So, set the webviewPath and return the new Photo object.
目前,创建一个新的辅助方法 convertBlobToBase64(),以实现运行在网页上的必要逻辑。
🌐 For now, create a new helper method, convertBlobToBase64(), to implement the necessary logic for running on the web.
import { useState } from 'react';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import type { Photo } from '@capacitor/camera';
// CHANGE: Add import
import { Filesystem, Directory } from '@capacitor/filesystem';
export function usePhotoGallery() {
// ...existing code...
// CHANGE: Update the `savePicture()` method
const savePicture = async (photo: Photo, fileName: string): Promise<UserPhoto> => {
// Fetch the photo, read as a blob, then convert to base64 format
const response = await fetch(photo.webPath!);
const blob = await response.blob();
const base64Data = (await convertBlobToBase64(blob)) as string;
const savedFile = await Filesystem.writeFile({
path: fileName,
data: base64Data,
directory: Directory.Data,
});
// Use webPath to display the new image instead of base64 since it's
// already loaded into memory
return {
filepath: fileName,
webviewPath: photo.webPath,
};
};
// CHANGE: Add `convertBlobToBase64()` method
const convertBlobToBase64 = (blob: Blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = reject;
reader.onload = () => {
resolve(reader.result);
};
reader.readAsDataURL(blob);
});
};
return {
addNewToGallery,
photos,
};
}
export interface UserPhoto {
filepath: string;
webviewPath?: string;
}
usePhotoGallery.ts 现在应该看起来像这样:
import { useState } from 'react';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import type { Photo } from '@capacitor/camera';
import { Filesystem, Directory } from '@capacitor/filesystem';
export function usePhotoGallery() {
const [photos, setPhotos] = useState<UserPhoto[]>([]);
const addNewToGallery = async () => {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100,
});
const fileName = Date.now() + '.jpeg';
// Save the picture and add it to photo collection
const savedImageFile = await savePicture(capturedPhoto, fileName);
const newPhotos = [savedImageFile, ...photos];
setPhotos(newPhotos);
};
const savePicture = async (photo: Photo, fileName: string): Promise<UserPhoto> => {
// Fetch the photo, read as a blob, then convert to base64 format
const response = await fetch(photo.webPath!);
const blob = await response.blob();
const base64Data = (await convertBlobToBase64(blob)) as string;
const savedFile = await Filesystem.writeFile({
path: fileName,
data: base64Data,
directory: Directory.Data,
});
// Use webPath to display the new image instead of base64 since it's
// already loaded into memory
return {
filepath: fileName,
webviewPath: photo.webPath,
};
};
const convertBlobToBase64 = (blob: Blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = reject;
reader.onload = () => {
resolve(reader.result);
};
reader.readAsDataURL(blob);
});
};
return {
addNewToGallery,
photos,
};
}
export interface UserPhoto {
filepath: string;
webviewPath?: string;
}
在网页上以 base64 格式获取相机照片似乎比在移动端更棘手。实际上,我们只是使用了内置的 Web API:用 fetch() 作为将文件读取为 blob 格式的简便方法,然后用 FileReader 的 readAsDataURL() 将照片 blob 转换为 base64。
🌐 Obtaining the camera photo as base64 format on the web appears to be a bit trickier than on mobile. In reality, we’re just using built-in web APIs: fetch() as a neat way to read the file into blob format, then FileReader’s readAsDataURL() to convert the photo blob to base64.
好了!每次拍摄新照片时,它现在都会自动保存到文件系统中。接下来,我们将加载并显示已保存的图片。
🌐 There we go! Each time a new photo is taken, it’s now automatically saved to the filesystem. Next up, we'll load and display our saved images.