Expo 44 Android 11环境下如何无重复保存ImagePicker图片至应用专属相册
解决Expo 44 Android 11图片存储到专属相册的问题
针对你遇到的两种情况,我整理了对应的解决方案,适配Expo 44和Android 11的权限规则:
途径一:直接将图片保存至专属相册,避免DCIM目录创建文件
这个方法的核心是让相机拍摄的照片不自动存入DCIM,同时处理相册选择的图片直接复制到目标相册,避免中间生成多余文件:
1. 相机拍摄场景
调用launchCameraAsync时添加saveToPhotos: false参数,这样照片不会自动保存到系统DCIM目录,仅生成临时URI:
const cameraImage = await ImagePicker.launchCameraAsync({ saveToPhotos: false, // 关键:不自动存到系统相册 mediaTypes: ImagePicker.MediaTypeOptions.Images });
之后我们手动将临时文件复制到应用专属相册对应的媒体目录,再创建资源:
if (!cameraImage.cancelled) { // 先获取或创建目标相册 let album = await MediaLibrary.getAlbumAsync('myAppAlbum'); if (!album) { album = await MediaLibrary.createAlbumAsync('myAppAlbum'); } // 使用FileSystem将临时图片复制到可被MediaLibrary识别的位置 const tempUri = cameraImage.uri; const fileName = tempUri.split('/').pop(); // Android 11下,Pictures目录是公共媒体目录,我们指定到myAppAlbum子目录 const targetUri = `${FileSystem.documentDirectory}Pictures/myAppAlbum/${fileName}`; // 确保目录存在 await FileSystem.makeDirectoryAsync(`${FileSystem.documentDirectory}Pictures/myAppAlbum/`, { intermediates: true }); // 复制临时文件到目标位置 await FileSystem.copyAsync({ from: tempUri, to: targetUri }); // 基于目标URI创建资源并添加到相册 const asset = await MediaLibrary.createAssetAsync(targetUri); await MediaLibrary.addAssetsToAlbumAsync([asset], album, false); }
2. 相册选择场景
对于从相册选的图片,直接复制到目标相册,避免移动操作触发权限:
const galleryImage = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images }); if (!galleryImage.cancelled) { let album = await MediaLibrary.getAlbumAsync('myAppAlbum'); if (!album) { album = await MediaLibrary.createAlbumAsync('myAppAlbum'); } // 复制原图片到目标目录 const fileName = galleryImage.uri.split('/').pop(); const targetUri = `${FileSystem.documentDirectory}Pictures/myAppAlbum/${fileName}`; await FileSystem.makeDirectoryAsync(`${FileSystem.documentDirectory}Pictures/myAppAlbum/`, { intermediates: true }); await FileSystem.copyAsync({ from: galleryImage.uri, to: targetUri }); const asset = await MediaLibrary.createAssetAsync(targetUri); await MediaLibrary.addAssetsToAlbumAsync([asset], album, false); }
这种方式全程不会在DCIM生成多余文件,所有操作都围绕应用专属的媒体目录进行。
途径二:仅请求一次权限,避免每次操作弹窗
Android 11对存储权限做了严格限制,WRITE_EXTERNAL_STORAGE权限在Android 11中已经无法完全覆盖文件移动的操作权限,你需要额外申请MANAGE_EXTERNAL_STORAGE权限(这个权限需要用户跳转到系统设置开启,而非普通弹窗):
1. 在app.json中添加权限
{ "expo": { "android": { "permissions": [ "CAMERA", "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE", "MANAGE_EXTERNAL_STORAGE" ] } } }
2. 代码中申请权限并引导用户开启
import * as Permissions from 'expo-permissions'; // Expo 44使用expo-permissions import * as Linking from 'expo-linking'; // 检查并申请MANAGE_EXTERNAL_STORAGE权限 const checkStoragePermission = async () => { const { status } = await Permissions.getAsync(Permissions.MANAGE_EXTERNAL_STORAGE); if (status !== 'granted') { const { status: newStatus } = await Permissions.askAsync(Permissions.MANAGE_EXTERNAL_STORAGE); if (newStatus !== 'granted') { // 引导用户到系统设置开启权限 await Linking.openSettings(); } } }; // 在应用启动或首次使用图片功能时调用 await checkStoragePermission();
当用户在系统设置中开启了MANAGE_EXTERNAL_STORAGE权限后,后续的文件移动操作(即createAlbumAsync第三个参数设为false)就不会再每次弹窗请求权限了。
注意:
MANAGE_EXTERNAL_STORAGE是一个特殊权限,Google Play对其使用有严格审核要求,如果你只是处理图片存储,优先推荐途径一的方案,避免权限审核问题。
内容的提问来源于stack exchange,提问作者Oscarina
相关产品推荐
相关产品推荐

