RNFetchBlobStat获取图片大小与设备显示不一致问题及修复
设备显示照片大小与RNFetchBlob/ImagePicker获取值不一致的问题解析及修复
问题场景
我在React Native项目中实现了文件选择大小限制:照片超过10MB或视频超过100MB时禁止上传,使用RNFetchBlob.fs.stat获取文件大小,同时发现ImagePicker返回的fileSize结果和stat一致。但遇到一个问题:设备相册显示某照片大小为1.42MB,而代码返回的是3466195字节(约3.31MB),两者存在明显差异。相关核心代码如下:
const MAX_SIZE_PHOTO = 10485760 //10*1024*1024 const MAX_SIZE_VIDEO = 104857600//100*1024*1024 getSizeImg = (type, source) => { RNFetchBlob.fs.stat(source.uri.replace('file:///', '').replace('file://', '').replace('file:/', '')) .then((stats) => { console.log("file size", stats.size) if (type === 'video') { if (stats.size > MAX_SIZE_VIDEO) { Alert.alert(null, "This video is too large to be uploaded") error() return } uploadVideo(source, completeHandler, progressHandler, error, diary_id, token) } else { if (stats.size > MAX_SIZE_PHOTO) { Alert.alert(null, "This image is too large to be uploaded") error() return } uploadImage(source) } }) .catch((err) => { console.log("getSize", err) error(err) }) } // 图片选择和视频选择函数省略...
差异产生的核心原因
- 设备显示的是优化后的预览文件大小:绝大多数设备的相册会对拍摄的原始照片进行优化处理——比如生成低分辨率的缩略图用于快速预览,或者在开启云存储优化(如iCloud照片库、Google照片的“高质量存储”)时,设备本地仅保存压缩后的小尺寸文件,原始高分辨率文件存储在云端。你在设备上看到的1.42MB就是这个预览/优化后的文件大小,而
RNFetchBlob和ImagePicker获取的是原始文件的真实大小。 - URI处理逻辑不严谨:代码中手动替换
file://系列前缀的方式没有适配跨平台的URI格式差异。比如iOS上ImagePicker返回的uri可能是ph://格式的资源URI,直接替换前缀无法正确映射到设备显示的预览文件路径,导致读取到的是原始高分辨率文件而非预览图。
修复方案
方案1:正确处理跨平台URI,确保读取目标文件
放弃手动替换URI前缀的方式,使用RNFetchBlob提供的wrap方法统一处理iOS/Android的资源URI,这能确保你访问到的是用户在相册中看到的对应文件:
getSizeImg = (type, source) => { // 使用RNFetchBlob.wrap适配跨平台URI const targetFilePath = RNFetchBlob.wrap(source.uri); RNFetchBlob.fs.stat(targetFilePath) .then((stats) => { console.log("匹配设备显示的文件大小", stats.size); // 后续大小判断逻辑不变 if (type === 'video') { if (stats.size > MAX_SIZE_VIDEO) { Alert.alert(null, "This video is too large to be uploaded") error() return } uploadVideo(source, completeHandler, progressHandler, error, diary_id, token) } else { if (stats.size > MAX_SIZE_PHOTO) { Alert.alert(null, "This image is too large to be uploaded") error() return } uploadImage(source) } }) .catch((err) => { console.log("getSize", err) error(err) }) }
方案2:压缩图片后再判断大小(对齐设备显示)
如果希望APP内的大小判断逻辑完全和设备显示的一致,可以先对选中的图片进行压缩,再基于压缩后的文件大小做限制:
- 安装图片压缩库:
npm install react-native-image-resizer --save # 或使用yarn yarn add react-native-image-resizer
- 修改
getSizeImg方法,加入压缩逻辑:
import ImageResizer from 'react-native-image-resizer'; getSizeImg = (type, source) => { if (type === 'image') { // 压缩图片到设备预览级别的尺寸和质量 ImageResizer.createResizedImage( source.uri, 1920, // 目标宽度,可根据需求调整 1080, // 目标高度,可根据需求调整 'JPEG', 80 // 压缩质量(0-100,数值越高质量越好) ).then((resizedImage) => { // 获取压缩后的文件大小 RNFetchBlob.fs.stat(RNFetchBlob.wrap(resizedImage.uri)) .then((stats) => { console.log("压缩后文件大小", stats.size); if (stats.size > MAX_SIZE_PHOTO) { Alert.alert(null, "This image is too large to be uploaded") error() return } // 使用压缩后的图片进行上传 uploadImage({ uri: resizedImage.uri, name: source.name, type: source.type }) }) }).catch((err) => { console.log("图片压缩失败", err); error(err); }); } else { // 视频处理逻辑使用wrap适配URI const targetFilePath = RNFetchBlob.wrap(source.uri); RNFetchBlob.fs.stat(targetFilePath) .then((stats) => { console.log("视频文件大小", stats.size); if (stats.size > MAX_SIZE_VIDEO) { Alert.alert(null, "This video is too large to be uploaded") error() return } uploadVideo(source, completeHandler, progressHandler, error, diary_id, token) }) .catch((err) => { console.log("getSize", err) error(err) }) } }
方案3:明确告知用户差异(最简单的方案)
如果不需要对齐设备显示大小,只需修改提示文本,明确说明是原始文件大小超过限制,避免用户困惑:
Alert.alert(null, `This original image is too large to be uploaded (current size: ~${(stats.size/1024/1024).toFixed(2)}MB)`)
内容的提问来源于stack exchange,提问作者Trần Hòa
相关产品推荐
相关产品推荐

