react native - Uploaded image to S3 but it's broken - ImagePicker issue? - Stack Overflow

Problem DescriptionS3 upload is successful, and the image object appears in the S3 bucket.But when I t

Problem Description

  1. S3 upload is successful, and the image object appears in the S3 bucket.
  2. But when I try to view the uploaded image, it shows a broken image icon instead of the actual image.
  3. The Content-Type is correctly set to image/jpeg or image/png, so that doesn’t seem to be the issue.
  4. I'm using Expo ImagePicker to select images and upload them to S3.
  5. The exact same logic worked fine on web, but it's not working properly in my Expo project.

Here's the code for uploading images to S3:

import axios from 'axios'
import { ImagePickerAsset } from 'expo-image-picker'

const API_URL = process.env.EXPO_PUBLIC_APP_AWS_IMAGE_API

export const uploadService = {
  getPresignedUrl: async () => {
    try {
      const response = await axios.get(`${API_URL}/routines/images`)
      return response.data.url
    } catch (error) {
      console.error('Error requesting presigned URL:', error)
      throw error
    }
  },

  uploadImageToS3: async (presignedUrl: string, file: ImagePickerAsset) => {
    try {
      // ✅ Convert to Blob (instead of Base64)
      const response = await fetch(file.uri)
      const blob = await response.blob()

      // ✅ Upload to S3 using PUT
      await axios.put(presignedUrl, blob, {
        headers: {
          'Content-Type': blob.type || 'image/jpeg', // Default content type
        },
      })

      console.log('S3 Upload Success:', presignedUrl)
      return presignedUrl.split('?')[0] // ✅ Return the uploaded S3 URL
    } catch (error) {
      console.error('Error uploading to S3:', error)
      throw error
    }
  },

  uploadImageToServer: async (url: string, params: { storeId: string; routineId: string; userTaskId: string }) => {
    try {
      const response = await axios.post(
        `${API_URL}/routines/images`,
        { url, ...params },
        { headers: { 'Content-Type': 'application/json' } },
      )
      return response.data
    } catch (error) {
      console.error('Error uploading to server:', error)
      throw error
    }
  },
}

Image Picker & Upload logic

import { useState } from 'react'
import * as ImagePicker from 'expo-image-picker'
import { Alert, Platform, ActionSheetIOS } from 'react-native'
import { uploadService } from '../services/fileUpload'
import { router } from 'expo-router'
import { postMessage } from '../components/WebviewContainer'
import { requestCameraPermission } from '../services/camera'
import { ImagePickerAsset } from 'expo-image-picker'

interface FileState {
  file: ImagePickerAsset | null
  preview: string | null
}

interface UploadParams {
  storeId: string
  routineId: string
  userTaskId: string
}



async function launchCamera() {
  const { permission } = await requestCameraPermission()
  if (permission === 'granted') {
    const result = await ImagePicker.launchCameraAsync({
      allowsEditing: true,
      quality: 0.8,
    })
    return result
  }
  return null
}

async function launchGallery() {
  const { permission } = await requestPhotoPermission()
  if (permission === 'granted') {
    const result = await ImagePicker.launchImageLibraryAsync({
      quality: 0.8,
      selectionLimit: 1,
      allowsMultipleSelection: false,
    })
    return result
  }
  return null
}

export default function useFileUpload() {
  const [fileState, setFileState] = useState<FileState>({ file: null, preview: null })
  const [isLoading, setIsLoading] = useState(false)

  const handleChange = async () => {
    if (Platform.OS === 'ios') {
      ActionSheetIOS.showActionSheetWithOptions(
        {
          options: ['Take a photo', 'Choose from gallery', 'Cancel'],
          cancelButtonIndex: 2,
          destructiveButtonIndex: 2,
        },
        async (buttonIndex) => {
          if (buttonIndex === 0) {
            const result = await launchCamera()
            if (result.assets?.[0]) {
              setFileState({ file: result.assets[0], preview: result.assets[0].uri })
            }
          } else if (buttonIndex === 1) {
            const result = await launchGallery()
            if (result.assets?.[0]) {
              setFileState({ file: result.assets[0], preview: result.assets[0].uri })
            }
          }
        },
      )
    } else {
      Alert.alert(
        'Select Image',
        'Choose how you want to upload the image',
        [
          { text: 'Take a photo', onPress: async () => {
            const result = await launchCamera()
            if (result?.assets?.[0]) {
              setFileState({ file: result.assets[0], preview: result.assets[0].uri })
            }
          }},
          { text: 'Choose from gallery', onPress: async () => {
            const result = await launchGallery()
            if (result?.assets?.[0]) {
              setFileState({ file: result.assets[0], preview: result.assets[0].uri })
            }
          }},
          { text: 'Cancel', style: 'cancel' }
        ],
        { cancelable: true }
      )
    }
  }

  const handleUpload = async (params: UploadParams) => {
    if (!fileState.file) {
      return null
    }
    try {
      setIsLoading(true)
      const preSignedUrl = await uploadService.getPresignedUrl()
      await uploadService.uploadImageToS3(preSignedUrl, fileState.file)

      const s3FileURL = preSignedUrl.split('?')[0]
      await uploadService.uploadImageToServer(s3FileURL, params)

      setIsLoading(false)
      postMessage('uploadImage', { isUploaded: true, url: s3FileURL, ...params })
      router.back()
    } catch (error) {
      console.error('Error uploading file:', error)
      setIsLoading(false)
      postMessage('uploadImage', { isUploaded: false, url: '', ...params })
      throw error
    }
  }

  return {
    fileState,
    isLoading,
    handleChange,
    handleUpload,
  }
}

What I've tried:

  • Confirmed that S3 upload succeeds.
  • Checked that Content-Type is correctly set (image/jpeg or image/png).
  • The exact same logic worked on web, but it's failing on Expo.
  • Tried uploading the file as a Base64 string, but that didn’t work either.
  • Converted file.uri to Blob before uploading, still no luck.

Why is the uploaded image broken?

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744856855a4597458.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信