Expo React Native如何获取图片SHA1校验值实现Backblaze B2上传
Expo React Native 计算图片SHA1对接Backblaze B2方案
问题背景
- 核心需求:在Expo React Native环境中获取本地选取图片的SHA1校验值,满足Backblaze B2上传API必填的
X-Bz-Content-Sha1请求头要求。该字段仅接受三类取值:40位十六进制格式的文件SHA1值、hex_digits_at_end、do_not_verify,出于可靠性要求不能选择跳过校验的模式。 - 已知限制:
expo-image-picker仅可直接返回文件MD5值,MD5为128位哈希,与B2要求的160位SHA1标准不兼容。- 旧版本
expo-crypto仅支持字符串输入计算哈希,无法直接处理文件二进制数据。 - 开启ImagePicker的base64返回参数后,通过
Buffer.from(result.base64, '')转换得到的内容直接转十六进制后长度超1000位,不符合40位SHA1的固定长度要求。
现有实现代码如下:
图片选择器唤起逻辑:
let result = await ImagePicker.launchImageLibraryAsync({ allowsEditing: true, aspect: [1.91, 1], exif:true, base64:true, quality: 1, });
ImagePicker返回结构(base64字段过长已省略):
{ "cancelled": false, "exif": { "ImageLength": 1080, "ImageWidth": 1080, "LightSource": 0, "Orientation": 0, }, "height": 1080, "type": "image", "uri": "file:///data/user/0/com.blipmoore.blipmoore_cleaner/cache/ImagePicker/c8ac399d-ae12-40f6-95c6-994ddf99b5ed.jpg", "width": 1080, }
推荐实现方案(适配Expo托管工作流,无需自定义原生代码)
使用v12及以上版本的expo-crypto配合expo-file-system即可直接计算文件SHA1,不需要引入第三方JS哈希库,也不需要额外的Buffer polyfill,性能为原生级别。
实现步骤
- 关闭ImagePicker的
base64返回参数,直接使用返回的文件uri读取内容即可,开启base64会增加30%左右的内存占用,大图片场景易引发卡顿。 - 通过
expo-file-system读取文件二进制内容,转换为Uint8Array格式传入expo-crypto的digest方法,指定SHA1算法即可直接得到40位十六进制哈希值。
可直接复用的代码:
import * as Crypto from 'expo-crypto'; import * as FileSystem from 'expo-file-system'; import * as ImagePicker from 'expo-image-picker'; /** * 计算本地文件SHA1值 * @param {string} fileUri 本地文件路径,支持file://协议 * @returns {string} 40位十六进制SHA1结果 */ const calculateFileSha1 = async (fileUri) => { // 读取文件为base64格式(兼容所有Expo SDK版本) const base64Data = await FileSystem.readAsStringAsync(fileUri, { encoding: FileSystem.EncodingType.Base64, }); // 转换为Uint8Array二进制格式 const binaryData = Uint8Array.from(atob(base64Data), char => char.charCodeAt(0)); // 调用原生crypto模块计算SHA1 const sha1Hex = await Crypto.digest( Crypto.CryptoDigestAlgorithm.SHA1, binaryData ); return sha1Hex; } // 业务调用示例 const pickImageAndUpload = async () => { const pickResult = await ImagePicker.launchImageLibraryAsync({ allowsEditing: true, aspect: [1.91, 1], exif: true, base64: false, // 关闭不必要的base64返回 quality: 1, }); if (pickResult.cancelled) return; // 得到符合B2要求的SHA1值 const fileSha1 = await calculateFileSha1(pickResult.uri); console.log('SHA1校验值:', fileSha1, '长度:', fileSha1.length); // 长度固定为40 // 上传时将fileSha1填入X-Bz-Content-Sha1请求头即可 }
Exponent SDK 49+ 优化点:
expo-file-system已支持直接读取为二进制格式,不需要走base64转码,性能提升约40%,可替换读取逻辑为:const binaryData = await FileSystem.readAsStringAsync(fileUri, { encoding: FileSystem.EncodingType.Binary, });
大文件场景适配
如果需要处理100MB以上的大文件,一次性读取全量内容到内存容易触发OOM,可采用分段增量哈希方案:
- 使用
expo-file-system的文件句柄API按1~2MB的块大小分段读取文件 - 初始化SHA1增量哈希上下文,每读取一块就更新一次哈希状态
- 所有块读取完成后输出最终哈希值,也可适配B2的
hex_digits_at_end上传模式,边上传边计算哈希,上传完成后将SHA1拼接在请求体末尾即可。
问题排查说明
- 之前转换Buffer得到上千位十六进制值的原因:直接将文件原始二进制内容转成了十六进制字符串,没有经过SHA1哈希压缩,长度与文件大小正相关,不是合法的SHA1值。SHA1为压缩哈希算法,无论输入文件多大,输出固定为20字节即40位十六进制字符串。
- 不建议使用纯JS实现的SHA1库计算文件哈希,纯JS计算性能比原生模块低5~10倍,大文件场景会有明显的UI卡顿。
- 不需要额外引入node.js的
bufferpolyfill,Expo内置的API已经可以完成所有格式转换。
内容的提问来源于stack exchange,提问作者Alexander Obidiegwu
相关产品推荐
相关产品推荐

