Hi i'm trying to resolve an issue where i cannot upload a picture to my cloud storage bucket.
Specifically the issue is that when i upload using my local backend server (via URL removed by Staff)), the photo uploads successfully but after i deploy my backend to Google's App Engine via Firebase functions, the upload function does not work. The following is my frontend React Native UploadImage function:
import React, { useState, useEffect } from 'react';
import { Image, View, TouchableOpacity, StyleSheet } from 'react-native';
import { AntDesign } from '@expo/vector-icons';
import * as ImagePicker from 'expo-image-picker';
import config from '../views/config';
export default function UploadImage(props) {
const [image, setImage] = useState(props.image);
const addImage = async () => {
try {
let _image = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!_image.canceled) {
console.log('image', _image.assets[0].uri, 'id', props.id);
const formData = new FormData();
formData.append('image', {
uri: _image.assets[0].uri,
type: 'image/jpeg',
name: 'image.jpg',
});
// Conditionally add the token only if it's provided
if (props.id) {
formData.append('id', props.id);
console.log('formdata', formData)
}
console.log('image', formData._parts)
// Find the item with the key "image" in the formData._parts array
const imagePart = formData._parts.find(([key]) => key === "image");
// If the item with key "image" exists, extract the URI
if (imagePart) {
const uri = imagePart[1].uri;
console.log('Image URI:', uri);
} else {
console.error('Image not found in formData');
}
const response = await fetch(`${config.apiUrl}/providers/upload`, {
method: 'POST',
headers: {
// 'Content-Type': 'multipart/form-data',
Accept: 'application/json',
},
body: formData
});
const responseData = await response.json();
if (!response.ok) {
throw new Error(responseData.message || 'Failed to upload profile image');
}
console.log(responseData)
setImage(responseData.fileUrl);
}
} catch (error) {
console.error('Error uploading image:', error);
// Handle error
}
};
useEffect(() => {
setImage(props.image)
}, [props.image])
return (
<View style={imageUploaderStyles.container}>
{
image && <Image source={{ uri: image }} style={{ width: 70, height: 70 }} />
}
<View style={imageUploaderStyles.uploadBtnContainer}>
<TouchableOpacity onPress={addImage} style={imageUploaderStyles.uploadBtn} >
<AntDesign name="camera" size={20} color="black" />
</TouchableOpacity>
</View>
</View>
);
}
While my backend Express/Node.js function to perform this is as follows:
const { Storage } = require('@google-cloud/storage');
const multer = require('multer');
const storage = new Storage();
const bucketName = 'xxxx.appspot.com';
// Configure Multer to use Google Cloud Storage
const storageConfig = storage.bucket(bucketName);
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 5 * 1024 * 1024, // 5MB file size limit
},
});
const updateProfilePhoto = async (req, res) => {
try {
const { id } = req.body;
const file = req.file;
console.log('file', file, 'id', id)
if (!file) {
return res.status(400).send({ message: 'No file uploaded' });
}
const fileName = `${file.fieldname}_${Date.now()}_${file.originalname}`;
const fileUpload = storageConfig.file(fileName);
const stream = fileUpload.createWriteStream({
metadata: {
contentType: file.mimetype,
},
resumable: true,
});
stream.on('error', (err) => {
console.error('Error uploading image:', err);
res.status(500).send({ message: 'Internal server error during upload' });
});
stream.on('finish', async () => {
console.log('File uploaded successfully:', fileUrl);
try {
await Provider.findByIdAndUpdate(id, { profileImage: fileUrl });
res.status(200).send({ message: 'Upload successful', fileUrl });
} catch (error) {
console.error('Error updating user profile with image URL:', error);
res.status(500).send({ message: 'Internal server error after upload' });
}
});
stream.end(file.buffer);
} catch (error) {
console.error('Error handling file upload:', error);
res.status(500).send({ message: 'Internal server error during file upload' });
}
};
module.exports = {
upload,
updateProfilePhoto
}
router.post('/upload', upload.single('image'), providerController.updateProfilePhoto);
So basically every time i try to upload via my remote server it gives me an error in Google Cloud like this:
Error: Unexpected end of form
at .Multipart._final ( /workspace/node_modules/busboy/lib/types/multipart.js:588 )
at .callFinal ( node:internal/streams/writable:698 )
at .prefinish ( node:internal/streams/writable:710 )
at .finishMaybe ( node:internal/streams/writable:720 )
at .Writable.end ( node:internal/streams/writable:634 )
at .onend ( node:internal/streams/readable:748 )
at process.processTicksAndRejections ( node:internal/process/task_queues:77 )
and my error showing in my log on my front end is like this:
ERROR Error uploading image: [SyntaxError: JSON Parse error: Unexpected character: <]
Basically if anyone has some insight what I may need to do or how I can modify my code in order to make this work, I'd be very appreciative. Thank you!