How to send an image file using formdata to React/Next js api?

18.8k Views Asked by At

I am trying to send a file to api in a next js app. The image will be uploaded to cloudinary:

Function calling api is:

  async uploadButtonClicked(imageUpload) {
      const formData = new FormData();
      //formData.append('test', "testing");
      formData.append('postPic', imageUpload.files[0]);
      const res = await fetch('/api/hello', {
        method: 'POST',
        headers: {
          'Content-Type': 'multipart/form-data',
          'Accept': 'application/json'
        },
        body: formData,
      })

console.log(imageUpload.files[0]) in front end gives me the values below and looks good. enter image description here

In the api,

export default (upload.single('postPic'), async (req, res) => {
  console.log(req.body)

The above is undefined when I use

export const config = {
  api: {
    bodyParser: false,
  },
};

When I remove the bodyParser setting (bodyParser is true), the data is as a stream which is not useful for upload. I get a upload error as shown below: enter image description here

Cloudinary will upload if the body reaches the api in this format below: enter image description here

What should be changed to get the body (which is basically an image file) to reach the api in the proper format as shown above?

I think the question can also be asked as: Why is the req.body undefined when using bodyParser: false ?

3

There are 3 best solutions below

9
On

If you're uploading to Cloudinary, why are you trying to send the file from the client to your nextjs api? You can upload directly to Cloudinary.

With a library like react-uploady. It's as simple as this:

import React from "react";
import Uploady from "@rpldy/uploady";
import UploadButton from "@rpldy/upload-button";

const CLOUD_NAME = "<your-cloud-name>";
const UPLOAD_PRESET = "<your-upload-preset>";

const App = () => (<Uploady
    destination={{ 
        url: `https://api.cloudinary.com/v1_1/${CLOUD_NAME}/upload`,
        params: {
            upload_preset: UPLOAD_PRESET,
        }
    }}>
    <UploadButton/>
</Uploady>);

4
On

I ran into the same error but Formidable worked for me. Try putting the image into Form Data first.

From Client:

export const uploadImage = async (image) => {

    const formData = new FormData();
    formData.append('image', image);
    const response = await axios.post('/api/v1/image', formData);
    return response.data;
}

And below is on server API : /api/v1/image

import formidable from 'formidable';


export const config = {
  api: {
    bodyParser: false,
  },
}

export default async (req, res) => {
  const form = new formidable.IncomingForm();
  form.uploadDir = "./";
  form.keepExtensions = true;
  form.parse(req, (err, fields, files) => {
    console.log(err, fields, files);
  });
};
1
On

This works for me

import cloudinary from 'cloudinary';
import formidable from 'formidable';

cloudinary.config({ 
  cloud_name: '', 
  api_key: '', 
  api_secret: '' 
});

export const config = {
  api: {
    bodyParser: false,
  },
}

export default async (req, res) => {
  const form = new formidable.IncomingForm();
  form.uploadDir = "./";
  form.keepExtensions = true;
  await form.parse(req, (err, fields, files) => {
    cloudinary.v2.uploader.upload(files.image.path, function(error, result) {
      res.status(200).json({
        success: true,
        data: result.secure_url
      })
    });
  });
}