React Native使用Vision Camera+RNFS保存照片失败问题求助
问题
基于react-native-vision-camera开发照片保存功能,使用react-native-fs(RNFS)访问Android文件路径,保存camera的useRef实例拍摄的照片。已获取CAMERA和WRITE_EXTERNAL_STORAGE权限,能正常获取data.path与RNFS.ExternalDirectoryPath,日志显示moveFile和writeFile操作成功,但找不到保存的照片。
代码实现
function Cam(){ const devices = useCameraDevices() const device = devices.back const camera = useRef(null) const takePhotoOptions = { qualityPrioritization: 'speed', flash: 'on' } const captureHandler = async()=>{ try{ if (camera.current == null) throw new Error('Camera Ref is Null'); console.log('Photo taking ....'); const data = await camera.current.takePhoto(takePhotoOptions) await RNFS.moveFile(`/${data.path}`, `${RNFS.ExternalDirectoryPath}/temp.jpg`) .then(()=>console.log("Image Moved",`${data.path}`,'-- to --',`${RNFS.ExternalDirectoryPath}`)) await RNFS.writeFile(`${RNFS.ExternalDirectoryPath}/temp.jpg`, data, 'ascii') .then(()=>console.log("Image wrote",`${data.path}`,'-- to --',`${RNFS.ExternalDirectoryPath}`)) }catch(err){ console.log(err) } } return(<> {device != null&&<> <Button title='Take a Photo' onPress={()=>captureHandler()}/> <View style={{ flex: 1 }}> <Camera ref={camera} style={StyleSheet.absoluteFill} device={device} photo={true} isActive={true} /> </View> </>} </>) }
日志信息
LOG Photo taking... LOG Image Moved /data/user/0/com.camera/cache/mrousavy6998557498651783260.jpg -- to -- /storage/emulated/0/Android/data/com.camera/files LOG Image wrote /data/user/0/com.camera/cache/mrousavy6998557498651783260.jpg -- to -- /storage/emulated/0/Android/data/com.camera/files
解决方案
你的代码存在几个关键问题,导致照片无法正常被找到或识别:
- 路径错误:
data.path本身已是绝对路径,你给它加了前缀/,导致路径变为//data/user/...,属于无效路径。虽然日志返回成功,但实际moveFile操作并未正确执行。 - 错误覆盖文件:
takePhoto返回的data是包含路径、尺寸等信息的对象,不是图片文件的二进制内容。你在moveFile之后调用writeFile,用ascii编码把这个对象写入文件,直接覆盖了刚移动的有效图片,导致文件变成无效格式。 - 媒体库未刷新:即使文件保存成功,Android媒体库不会自动扫描新文件,导致图库无法显示新保存的照片。
修复后的代码
import RNFS from 'react-native-fs'; import { Platform, NativeModules } from 'react-native'; function Cam(){ const devices = useCameraDevices() const device = devices.back const camera = useRef(null) const takePhotoOptions = { qualityPrioritization: 'speed', flash: 'on' } // 触发Android媒体库扫描,让图库能识别新照片 const scanFile = async (filePath) => { if (Platform.OS === 'android') { const { MediaScanner } = NativeModules; await MediaScanner.scanFile(filePath); } } const captureHandler = async()=>{ try{ if (!camera.current) throw new Error('Camera Ref is Null'); console.log('Photo taking ....'); const data = await camera.current.takePhoto(takePhotoOptions); // 加时间戳生成唯一文件名,避免重复拍照覆盖 const targetPath = `${RNFS.ExternalDirectoryPath}/temp_${Date.now()}.jpg`; // 直接移动原文件,不要给data.path加前缀/ await RNFS.moveFile(data.path, targetPath); console.log("Image Moved", data.path, '-- to --', targetPath); // 触发媒体扫描 await scanFile(targetPath); console.log('照片已保存,可在图库查看'); }catch(err){ console.error(err); } } return(<> {device != null&&<> <Button title='Take a Photo' onPress={captureHandler}/> <View style={{ flex: 1 }}> <Camera ref={camera} style={StyleSheet.absoluteFill} device={device} photo={true} isActive={true} /> </View> </>} </>) }
额外注意事项
- Android 13+需要申请
READ_MEDIA_IMAGES权限,WRITE_EXTERNAL_STORAGE在Android 10+已被限制,ExternalDirectoryPath属于应用私有外部存储,无需WRITE权限,但媒体扫描需要对应权限。 - 如果想让照片直接出现在图库的相机相册,可保存到
RNFS.PictureDirectoryPath,但Android 10+需要处理存储访问框架(SAF)。
内容的提问来源于stack exchange,提问作者JayCold
相关产品推荐
相关产品推荐

