React Native Expo中不使用Blob上传图片至Firebase Storage的方案
解决iOS上Firebase Storage上传图片体积变大的替代方案
针对你遇到的iOS平台用Blob上传图片体积变为四倍的问题,以下是几个无需依赖Blob的替代上传方案,以及修复原Blob方案的思路:
方案一:通过Expo Filesystem读取文件为Uint8Array上传
利用Expo Filesystem直接读取本地图片文件为Base64,再转换为Uint8Array后上传,避免Blob的编码问题:
import * as FileSystem from 'expo-file-system'; import { getStorage, ref, uploadBytesResumable, getDownloadURL } from "firebase/storage"; import { v4 as uuidv4 } from 'uuid'; const uploadImage = async (picture) => { const randomId = uuidv4(); // 读取本地文件为Base64格式 const base64Str = await FileSystem.readAsStringAsync(picture, { encoding: FileSystem.EncodingType.Base64, }); // 将Base64转换为Uint8Array const uint8Array = Uint8Array.from(atob(base64Str), char => char.charCodeAt(0)); const path = `images/${randomId}`; const storage = getStorage(); const metadata = { contentType: "image/jpeg" }; const imagesRef = ref(storage, path); const uploadTask = uploadBytesResumable(imagesRef, uint8Array, metadata); // 上传状态监听逻辑与原代码一致 uploadTask.on( "state_changed", (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`Upload: ${progress}%`); switch (snapshot.state) { case "paused": console.log("Upload is paused"); break; case "running": console.log("Upload is running"); break; } }, (error) => { console.log("Error uploading document: ", error); }, () => { getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { console.log("File available at", downloadURL); }); } ); };
方案二:利用Expo Image Manipulator直接输出Base64上传
既然已经使用expo-image-manipulator处理图片,可以在处理阶段直接获取Base64数据,省去二次读取文件的步骤:
import * as ImageManipulator from 'expo-image-manipulator'; import { getStorage, ref, uploadBytesResumable, getDownloadURL } from "firebase/storage"; import { v4 as uuidv4 } from 'uuid'; const processAndUploadImage = async (originalPicturePath) => { // 处理图片时直接输出Base64 const manipulatedResult = await ImageManipulator.manipulateAsync( originalPicturePath, [{ resize: { width: 800 } }], // 你的缩放规则 { base64: true, format: ImageManipulator.SaveFormat.JPEG, quality: 0.8 // 可根据需求调整画质,平衡体积与清晰度 } ); const randomId = uuidv4(); // 将Base64转换为Uint8Array const uint8Array = Uint8Array.from(atob(manipulatedResult.base64), char => char.charCodeAt(0)); const path = `images/${randomId}`; const storage = getStorage(); const metadata = { contentType: "image/jpeg" }; const imagesRef = ref(storage, path); const uploadTask = uploadBytesResumable(imagesRef, uint8Array, metadata); // 后续监听逻辑与原代码一致 uploadTask.on( "state_changed", (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`Upload: ${progress}%`); switch (snapshot.state) { case "paused": console.log("Upload is paused"); break; case "running": console.log("Upload is running"); break; } }, (error) => { console.log("Error uploading document: ", error); }, () => { getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { console.log("File available at", downloadURL); }); } ); };
方案三:修复原Blob上传方案的编码问题
如果想继续使用Blob上传,可以手动指定Blob的MIME类型,避免iOS上自动识别错误导致的体积膨胀:
const picture = "file:///file/path"; const randomId = uuidv4(); const response = await fetch(picture); const arrayBuffer = await response.arrayBuffer(); // 手动指定Blob的类型为image/jpeg,避免自动识别错误 const blob = new Blob([arrayBuffer], { type: "image/jpeg" }); const path = `images/${randomId}`; const storage = getStorage(); const metadata = { contentType: "image/jpeg" }; const imagesRef = ref(storage, path); const uploadTask = uploadBytesResumable(imagesRef, blob, metadata); // 监听逻辑与原代码一致
以上方案中,方案一和方案二更适合Expo环境,能有效避免Blob在iOS上的编码问题;方案三则是对原有逻辑的最小修改,可快速验证是否解决体积问题。
内容的提问来源于stack exchange,提问作者Asaraspo
相关产品推荐
相关产品推荐

