Skip to main content

从文件系统加载照片

我们已经实现了拍照并保存到文件系统。缺少最后一项功能:照片存储在文件系统中,但我们需要一种方法来保存每个文件的指针,以便它们可以在照片库中再次显示。

¥We’ve implemented photo taking and saving to the filesystem. There’s one last piece of functionality missing: the photos are stored in the filesystem, but we need a way to save pointers to each file so that they can be displayed again in the photo gallery.

幸运的是,这很容易:我们将利用 Capacitor 首选项 API 将照片数组存储在键值存储中。

¥Fortunately, this is easy: we’ll leverage the Capacitor Preferences API to store our array of Photos in a key-value store.

首选项 API

¥Preferences API

首先定义一个常量变量,该变量将在 src/hooks/usePhotoGallery.ts 中的 usePhotoGallery 函数定义之前充当存储的键:

¥Begin by defining a constant variable that will act as the key for the store before the usePhotoGallery function definition in src/hooks/usePhotoGallery.ts:

const PHOTO_STORAGE = 'photos';
export function usePhotoGallery() {}

然后,使用 Storage 类访问 get 和 set 方法以读取和写入设备存储:

¥Then, use the Storage class to get access to the get and set methods for reading and writing to device storage:

takePhoto 函数的末尾,添加对 Preferences.set() 的调用以保存照片数组。通过将其添加到此处,每次拍摄新照片时都会存储照片数组。这样,应用用户何时关闭或切换到其他应用并不重要 - 所有照片数据均已保存。

¥At the end of the takePhoto function, add a call to Preferences.set() to save the Photos array. By adding it here, the Photos array is stored each time a new photo is taken. This way, it doesn’t matter when the app user closes or switches to a different app - all photo data is saved.

Preferences.set({ key: PHOTO_STORAGE, value: JSON.stringify(newPhotos) });

保存照片数组数据后,我们将创建一个方法,该方法将在钩子加载时检索数据。我们将使用 React 的 useEffect 钩子来做到这一点。将其插入到 takePhoto 声明上方。这是代码,我们将对其进行分解:

¥With the photo array data saved, we will create a method that will retrieve the data when the hook loads. We will do so by using React's useEffect hook. Insert this above the takePhoto declaration. Here is the code, and we will break it down:

useEffect(() => {
const loadSaved = async () => {
const { value } = await Preferences.get({ key: PHOTO_STORAGE });
const photosInPreferences = (value ? JSON.parse(value) : []) as UserPhoto[];

for (let photo of photosInPreferences) {
const file = await Filesystem.readFile({
path: photo.filepath,
directory: Directory.Data,
});
// Web platform only: Load the photo as base64 data
photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
}
setPhotos(photosInPreferences);
};
loadSaved();
}, []);

乍一看这似乎有点可怕,所以让我们来看看它,首先查看我们传递给钩子的第二个参数:依赖数组 []

¥This seems a bit scary at first, so let's walk through it, first by looking at the second parameter we pass into the hook: the dependency array [].

默认情况下,每次组件渲染时都会调用 useEffect 钩子,除非我们传入依赖数组。在这种情况下,它只会在依赖更新时运行。在我们的例子中,我们只希望它被调用一次。通过传入一个不会改变的空数组,我们可以防止钩子被多次调用。

¥The useEffect hook, by default, gets called each time a component renders, unless, we pass in a dependency array. In that case, it will only run when a dependency gets updated. In our case we only want it to be called once. By passing in an empty array, which will not be changed, we can prevent the hook from being called multiple times.

useEffect 的第一个参数是将由效果调用的函数。我们传入一个匿名箭头函数,并在其中定义另一个异步方法,然后立即调用它。我们必须从钩子内部调用异步函数,因为钩子回调本身不能是异步的。

¥The first parameter to useEffect is the function that will be called by the effect. We pass in an anonymous arrow function, and inside of it we define another asynchronous method and then immediately call this. We have to call the async function from within the hook as the hook callback can't be asynchronous itself.

在移动设备上(接下来!),我们可以直接设置图片标签的来源 - <img src=”x” /> - 文件系统上的每个照片文件,自动显示它们。然而,在网络上,我们必须将文件系统中的每个图片读取为 base64 格式,因为文件系统 API 在底层将它们存储在 IndexedDB 内的 base64 中。

¥On mobile (coming up next!), we can directly set the source of an image tag - <img src=”x” /> - to each photo file on the Filesystem, displaying them automatically. On the web, however, we must read each image from the Filesystem into base64 format, because the Filesystem API stores them in base64 within IndexedDB under the hood.

就是这样!我们在 Ionic 应用中构建了一个完整的照片库功能,可以在网络上运行。接下来,我们将把它转变成适用于 iOS 和 Android 的移动应用!

¥That’s it! We’ve built a complete Photo Gallery feature in our Ionic app that works on the web. Next up, we’ll transform it into a mobile app for iOS and Android!