- 作者:老汪软件技巧
- 发表时间:2024-08-19 04:04
- 浏览量:
最近项目 app (react native) 有个需求是复制一张本地二维码图片供用户长按下载,使用的 lib 是 react-native-fs,这个需求看似简单,实际上有不少的坑要踩,比如文件的路径,复制成功后无法查看到文件等,以下是具体的解决案例
Android
首先将图片放在 android/app/src/main/assets 目录下,记得重新 build 下,这个可不是添加即用的。调用 existsAssets 判断文件资源是否存在,如果存在,就调用 copyFileAssets 去保存图片
import RNFS from 'react-native-fs';
const androidSavePicture = async () => {
// 无需前缀 / ,如果父目录是 assets ,直接文件名即可,否则需要写文件夹名称,如 common/origin.png
const filepath = 'origin.png';
// 存储在 Pictures 目录下,替换成你需要的名称
const destPath = `${RNFS.PicturesDirectoryPath}/target.png`;
const isExist = await RNFS.existsAssets(filepath);
if (!isExist) {
return;
}
RNFS.copyFileAssets(filepath, destPath)
.then(() => {
console.log('复制成功');
})
.catch((error) => {
console.log('复制失败', error);
});
};
实际测试下来,文件能下载成功,但是,去系统自带的图库却没能看到,要知道,大多数用户可不会打开文件 app,再定位到 Pictures 文件夹去查看的。其实,没法查看到图片的原因是 Android 的媒体扫描器(MediaScanner)没有立即检测到新添加的文件,像我用三星进行测试,重启手机会触发扫描,就能在图库中看到了,因此,解决方法是我们手动触发扫描,这需要写桥接文件,以下仅给出核心代码
@ReactMethod
public void scanFile(@NonNull String filePath) {
Log.d("scanFile", "filePath: " + filePath);
File file = new File(filePath);
if (!file.exists()) {
Toast.makeText(context, "文件不存在: " + filePath, Toast.LENGTH_LONG).show();
return;
}
MediaScannerConnection.scanFile(context, new String[]{filePath}, null, (path, uri) -> {
Log.d("scanFile", "path: " + path);
Log.d("scanFile", "uri: " + uri.getPath());
});
}
在 copyFileAssets 的 then 回调里调用即可
NativeModules.RNMultifunctional.scanFile(destPath);
iOS
首先在 xcode 将图片拖动到 Resources 文件夹下,会跳出弹窗,根据情况会自动判断是创建引用(如果是项目已有的会创建引用)还是直接拷贝图片,project.pbxproj 会出现一些代码的更改,与 android 相同,都需要重新 build 下才可用
代码同样很简单,首先判断文件是否存在,存在就调用 copyFile api 保存即可
const iOSSavePicture = async () => {
const filepath = RNFS.MainBundlePath + '/origin.png';
const isExist = await RNFS.exists(filepath);
if (!isExist) {
return;
}
// 需要前缀 /
const destPath = RNFS.DocumentDirectoryPath + '/target.png';
RNFS.copyFile(filepath, destPath).then(() => {
console.log('复制成功');
}).catch(error => {
console.log('复制失败', error);
});
}
实际测试,发现走了 then 流程,图片成功拷贝,但在 iOS 自带的文件 app 中却无法查看到图片,具体原因跟 iOS 的一些权限配置有关,具体指 UIFileSharingEnabledandLSSupportsOpeningDocumentsInPlace,详情可以查看 /questions/5…
因此在 xcode 中配置,定位到 target、选中 info 的 Custom iOS Target Properties,添加上述两个 key,会分别显示成 Application supports iTunes file sharing 和 Supports opening documents in place,然后把 value 的值都改成 YES 即可
通过上述操作,保存成功后可以在 “我的 iPhone” 中看到一个与开发应用同名的文件夹,里面可以看到保存后的图片,功能成功实现
下载功能
比起复制本地文件,更常见的需求可能是下载网络图片到手机上,这个就比较简单了,用到的 api 是 downloadFile,最重要的参数有两个,fromUrl 和 toFile,fromUrl 很好理解,即图片链接,toFile 则可以等同于上面代码中的 destPath,以下是具体代码
const downloadFile = () => {
const isAndroid = Platform.OS === 'android';
const toFile = isAndroid ? `${RNFS.PicturesDirectoryPath}/target.png` : RNFS.DocumentDirectoryPath + '/target.png';
RNFS.downloadFile({
fromUrl: 'https://www.example.com/example.png',
toFile,
}).promise.then(downloadResult => {
console.log('download result: ', downloadResult);
if (isAndroid) {
NativeModules.RNMultifunctional.scanFile(toFile);
}
});
}