You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Expo上传图片到Firebase文件类型为octet stream

问题原因

你的代码存在三个核心错误,导致上传的文件无法被识别为正常图片:

  • 传参类型错误:uploadBytes 方法仅支持传入 Blob/File/Uint8Array 类型的二进制数据,你直接传入ImagePicker返回的result对象、或者本地路径字符串uri,Firebase无法解析内容,只能标记为通用二进制流application/octet-stream。
  • 上传时序错误:上传逻辑写在组件渲染的根作用域,会在组件每次重渲染时执行,选图前image状态为null,选图后状态更新触发重渲染才会执行上传,时序完全混乱。
  • 格式配置错误:存储路径写死后缀为.png,却硬编码contentType为image/jpeg,后缀和MIME类型不匹配,就算上传成功也会出现解析异常。
修复方法

先将ImagePicker返回的本地URI转换为Blob二进制对象,再在选图成功的回调内执行上传逻辑,自动匹配文件格式,不要硬编码类型。

  1. 新增本地URI转Blob的工具方法,React Native/Expo环境下直接用fetch读取本地资源即可转换:
const uriToBlob = async (uri) => {
  const res = await fetch(uri);
  return res.blob();
};
  1. 调整选图、上传逻辑的位置,修正后的完整可运行代码:
import React, { useState } from 'react';
import { Button, Image, View } from 'react-native';
import * as ImagePicker from 'expo-image-picker';

import { storage } from '../../firebase';
import { auth } from '../../firebase';
import { uploadBytes, ref } from 'firebase/storage';


export default function ImagePickerExample() {
  const [imageUri, setImageUri] = useState(null);

  // 本地uri转二进制Blob
  const uriToBlob = async (uri) => {
    const response = await fetch(uri);
    return await response.blob();
  };

  const pickImage = async () => {
    let result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true,
      aspect: [4, 3],
      quality: 1,
    });

    // 适配新旧版ImagePicker返回结构
    const isCanceled = result.canceled ?? result.cancelled;
    if (!isCanceled) {
      const selectedAsset = result.assets ? result.assets[0] : result;
      const selectedUri = selectedAsset.uri;
      setImageUri(selectedUri);
      
      try {
        // 转Blob后再上传
        const imageBlob = await uriToBlob(selectedUri);
        // 自动识别文件格式,避免硬编码不匹配
        const fileSuffix = selectedUri.split('.').pop().split('?')[0].toLowerCase();
        const contentType = fileSuffix === 'jpg' ? 'image/jpeg' : `image/${fileSuffix}`;
        
        const storageRef = ref(
          storage,
          `user_profile_pictures/${auth.currentUser.uid}.${fileSuffix}`
        );
        await uploadBytes(storageRef, imageBlob, { contentType });
        // 释放Blob内存
        imageBlob.close?.();
        console.log('图片上传完成');
      } catch (error) {
        console.error('上传出错:', error);
      }
    }
  };

  
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button title="从相册选择图片" onPress={pickImage} />
      {imageUri && <Image source={{ uri: imageUri }} style={{ width: 200, height: 200 }} />}
    </View>
  );
}
补充说明
  • 如果你使用的是v13及以上版本的expo-image-picker,选中的资源会放在result.assets数组中,取消状态字段为canceled,旧版本字段为cancelled、资源直接挂在result对象下,上面的代码已经做了兼容。
  • 上传时优先使用Blob自带的type字段作为contentType,或者从文件URI提取后缀自动生成,不要手动硬编码,避免后缀和MIME类型不匹配导致图片无法加载。
  • 上传完成后调用blob.close()释放内存,避免App出现内存泄漏。

内容的提问来源于stack exchange,提问作者suchafoool

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 17:36:18