Mike K
Mike K

Reputation: 6473

Unable to upload images with multer

I'm trying to upload images. It's reaching the backend, but the request body and req.image are coming out empty.

I have the submission:

const handleSubmit = async () => {
  try {
    const data = createFormData();
    console.log(data); // prints the correct request object

    const response = await axios.post(
      `http://${GATEWAY}:5000/api/uploads/single`,
      JSON.stringify(data)
    );

    alert("Upload success!");
    console.log("response.data", response.data);
  } catch (err) {
    console.log("err caught --> ", err);
  }
};

const createFormData = () => {
  const data = new FormData();

  data.append("title", title); // coming from
  data.append("body", body); // react hooks state (useState)
  data.append("image", {
    height: image.height,
    width: image.width,
    type: image.type,
    uri:
      Platform.OS === "android" ? image.uri : image.uri.replace("file:/", "")
  });

  return data;
};

My endpoint:

const express = require("express");
const multer = require("multer");
const bodyParser = require("body-parser");

express().use(bodyParser.json());

const router = express.Router();

// middleware
const auth = require("../../middleware/auth");

const storage = multer.diskStorage({
  destination(req, file, callback) {
    callback(null, "./images");
  },
  filename(req, file, callback) {
    callback(null, `${file.fieldname}_${Date.now()}_${file.originalname}`);
  }
});

const upload = multer({ storage });

// @route  POST api/uploads/single
// @desc   Upload single image
// @access Private
router.post(
  "/single",
  // upload.array("photo", 3),
  auth,
  upload.single("image"),
  (req, res) => {
    console.log("req", req.body); // {}
    console.log("req", req.image); // undefined

    return res.status(200).json({
      message: "Response from backend"
    });
  }
);

module.exports = router;

And my server.js

 const express = require("express");
 const connectDb = require("./config/db");

 const app = express();

 // connect to db
 connectDb();

 // Define routes (some omitted for brevity)
 app.use("/api/uploads", require("./routes/api/uploads"));

 const PORT = process.env.PORT || 5000;

 app.listen(PORT, () => console.log(`Listening on port ${PORT}`));

For some reason, in the first snippet, if I do not stringify the data: FormData object that is built from createFormData(), my backend is never even reached.

I've tried so many things, and I'm not starting to think that maybe my backend isn't setup properly. The line where I'm doing express().use(bodyParser.json()); exists because I can't do app.use(bodyParser.json()); (or at least I think), because the app object is in the main server.js file. I'm including other API routes in other files.

For example, my server.js has these, amongst others:

// Define routes
app.use("/api/auth", require("./routes/api/auth"));
app.use("/api/users", require("./routes/api/users"));
app.use("/api/profile", require("./routes/api/profile"));
app.use("/api/uploads", require("./routes/api/uploads"));

And I was following this tutorial to use multer with react-native. A little lost at this point, not sure what I'm doing wrong.


Edit:

I'm making the request like this now,

  const config = {
    headers: {
      "Content-Type": "multipart/form-data"
    }
  };

  const response = await axios.post(
    `http://${GATEWAY}:5000/api/uploads/single`,
    data,
    config
  );

But It's failing with a

img

If I stringify it, it hits the backend but not in the way I need it to:

img2


Edit:

I got it working by specifying the image type, as per the suggestion here

Upvotes: 0

Views: 889

Answers (4)

Raj Gohel
Raj Gohel

Reputation: 1102

I used the following line to get images:-

concole.log(req.files);

Upvotes: 0

Mike K
Mike K

Reputation: 6473

Got it working by specifying the image type as per the suggestion here

Upvotes: 0

Hamza
Hamza

Reputation: 469

You're trying to access the file from req.image and req.body, but as mentioned in the https://www.npmjs.com/package/multer, you can access it from :

req.file

req.body will hold the text fields only, on the other hand, if you only uploaded a single file you can find it in req.file, but if you uploaded multiple files you will find them in req.files.

Upvotes: 0

Aric Peters
Aric Peters

Reputation: 111

I think this is because you are not setting your content-type to multipart/form-data. Try adding this to your request options:

const response = await axios.post(
  `http://${GATEWAY}:5000/api/uploads/single`,
  data,
  headers: {
    `Content-Type`: `multipart/form-data`
  }
);

Because of multipart/form-data, do not stringify the data you are sending. Stringifying the data will cause it to only be read as text by the server but is expecting a file to be attached.

Upvotes: 1

Related Questions