Failing to Upload Photo to Storage Bucket via App Engine Remote Server Environment

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 () => {
        const fileUrl = `https://storage.googleapis.com/${bucketName}/${fileName}`;
        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!
 
0 1 183
1 REPLY 1

I can see that you've limited your image to a max of  5MB. Can you confirm that part of the code works (i.e. it raises an error if you have a large image)? Do you know what is the overall size of your request? Asking because I'm wondering if the issue is related to the 32MB maximum size of an "incoming request' to GAE (see documentation)


    ......NoCommandLine ......
https://nocommandline.com
        Analytics & GUI for 
App Engine & Datastore Emulator