Home > Back-end >  How to upload react-native image from expo-image-picker to Express.js backend that is using multer
How to upload react-native image from expo-image-picker to Express.js backend that is using multer

Time:04-11

as title says, I am trying to upload picture using expo-image-picker and express.js backend

Expo code:

const openImagePickerAsync = async () => {
        let permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
        
        if (permissionResult.granted === false) {
            alert("Permission to access camera roll is required!");
            return;
        }
        
        let pickerResult = await ImagePicker.launchImageLibraryAsync();
        const dataa = Object.values(pickerResult);
        var data = new FormData();
        data.append('image', { 
          path: dataa[2], //image uri
          originalname: "profilePic"   userId   ".jpg",
          type: "image/jpg",
          fieldname: "demo_image"
        })
        console.log(data)
        const uploadImage = await axios.post(`http://localhost:8000/api/upload/picture/`   userId, data);    }

Express code: Router:

var multer = require("multer")

var storage = multer.diskStorage({   
    destination: function(req, file, cb) { 
      console.log(file)
       cb(null, './controllers/assets/profilePic/');    
    }, 
    filename: function (req, file, cb) { 
      console.log(file)
       cb(null , file.originalname);   
    }
 });
 var upload = multer({ storage: storage }).single("demo_image");
router.route("/upload/picture/:userId").post(upload,  UserController.updatePhoto)

updatePhoto function:

static updateGamePhoto = async (req, res) => {
        try {
                const query = await GamMdl.find({game_id: req.params.gameId})
                console.log(req.file)
                if(query.length > 0){
                        //get file route
                        //get route to db
                        const queryy = await GamMdl.updateOne({game_id: req.params.gameId}, {picture: req.file.originalname})
                        console.log(queryy)
                        res.send("1")
                    }
                    else
                    {
                        //vymaz file
                        fs.unlinkSync(req.file.path)
                        res.send("0")
                    }
                }
        catch (error) {
            console.log(error)
        }
    }

The error I am getting is

2022-04-10T18:17:29.165105 00:00 app[web.1]: undefined
2022-04-10T18:17:29.165435 00:00 app[web.1]: TypeError: Cannot read properties of undefined (reading 'originalname')
2022-04-10T18:17:29.165436 00:00 app[web.1]:     at updatePhoto (/app/controllers/userController.js:166:119)
2022-04-10T18:17:29.165436 00:00 app[web.1]:     at runMicrotasks (<anonymous>)
2022-04-10T18:17:29.165439 00:00 app[web.1]:     at processTicksAndRejections (node:internal/process/task_queues:96:5)

Which most likely means, that object is not being passed or is unable to be parsed correctly.

When I test uploading image only with backend using Postman all works well. Postman preset:

postman preset

How to fix this?

CodePudding user response:

Don't use axios for file upload because you need to read the image from the native filesystem. You can use uploadAsync() from the FileSystem module from Expo. Documentation Here

In your case the function call would be:

const openImagePickerAsync = async () => {
  let permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
  if (permissionResult.granted === false) {
    alert("Permission to access camera roll is required!");
    return;
  }
  let pickerResult = await ImagePicker.launchImageLibraryAsync();
  if (!pickerResult.cancelled) {
    const uploadResult = await FileSystem.uploadAsync('http://localhost:8000/upload/picture/1', pickerResult.uri, {
      httpMethod: 'POST',
      uploadType: FileSystemUploadType.MULTIPART,
      fieldName: 'demo_image'
    });
  }
}

By the way, avoid using localhost since the app may run on a device or emulator, and localhost would be the device itself instead of your webserver. Pass the API endpoint via environment variables. More info here. Use something like dotenv to load variables during development.

  • Related