Search code examples
node.jsreactjsaxiosmulter

how do I fix that axios sends jpeg instead of jpg to back end?


SO I'm trying to send an image from the react front end to the node.js back end but when I send a .jpg to the back end and I ask there what tpye it is sais it's a jpeg while multer still thinks its a jpg so it the sharp functions I do on it don't work anymore and give an error because they can't find the file.

I tried using by both instances where I need the file type (where multer saves it and where I declaire the imput by for sharp) but that didn't work. here is the code for the front end:

function App() {
  const [image, setImage] = useState("");
  function handleImage(e) {
    console.log(e.target.files);
    setImage(e.target.files[0]);
  }
  function handleApi() {
    const formData = new FormData();
    formData.append("image", image);
    formData.append("pfp", "yes");
    formData.append("filetype", image.type )
    console.log('hello')
    console.log(image)
    console.log(formData)
    axios
      .post("http://localhost:3001/users/editprofile/1", formData, {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      })
      .then((res) => {
        console.log(res);
      });
  }
  return (
    <div>
      <input type="file" name="file" onChange={handleImage} />
      <button onClick={handleApi}>submit</button>
    </div>
  );
}`

and here is the code for the back end:

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "images/temp");
  },
  filename: function (req, file, cb) {
    const id = req.params.id;
    let extention = path.extname(file.originalname);
    cb(null, `pfp_${id}` + extention);
  },
});

router.post("/editprofile/:id", upload.single("image"), async (req, res) => {
  const id = req.params.id;
  const { pfp, bio, filetype } = req.body;
  console.log(bio);
  console.log(req.body);
  if (pfp) {
    const typetemp = filetype.split("/");
    const type = typetemp[typetemp.length - 1];

    var imput = path.join(
      __dirname,
      "..",
      "images/temp",
      "pfp_" + id + "." + type
    );
    var output = path.join(__dirname, "..", "images/temp", id + "png.png");
    var newimage = path.join(
      __dirname,
      "..",
      "images/profile_pictures",
      "pfp_" + id + ".png"
    );

    try {
      const metadata = await sharp(imput).metadata();
      if (metadata.format != "png") {
        await sharp(imput).toFormat("png", { palette: true }).toFile(output);
      } else {
        await sharp(imput).toFile(output);
      }
      if (metadata.height > metadata.width) {
        var topcut = Math.round((metadata.height - metadata.width) / 2);
        await sharp(imput)
          .extract({
            width: metadata.width,
            height: metadata.width,
            left: 0,
            top: topcut,
          })
          .toFile(newimage);
      } else if (metadata.height < metadata.width) {
        var leftcut = Math.round((metadata.width - metadata.height) / 2);
        console.log(leftcut);
        await sharp(imput)
          .extract({
            width: metadata.height,
            height: metadata.height,
            left: leftcut,
            top: 0,
          })
          .toFile(newimage);
      }
      const metadatanew = await sharp(newimage).metadata();
      console.log(metadatanew);
      var newpfp = id + ".png";
    } catch (error) {
      console.log(error);
    }
  }

I get the following error in the console when I try to upload a jpg:

[Object: null prototype] { pfp: 'yes', filetype: 'image/jpeg' }
[Error: Input file is missing: D:\programmeerstuff\javascript\pws\version 0.0.1\server\images\temp\pfp_1.jpeg]

and this is the picture I tried to upload: (don't mind what's on it it was just the first image I saw when I clicked choose file XD) photo I tried to upload


Solution

  • The problem is there is no image/jpg mime type, and you're creating file extension with the mime type, which is always image/jpeg, so you'll always end up with .jpeg file extension.

    Solution would be to send file extension from the frontend, instead of mime type:

    // get file extension, maybe also add extra check to ensure there is one   
    const filetype = image.name.split('.').pop();
    
    formData.append("filetype", filetype )
    

    And on the server you should check if file exists (and add extra checks, if needed).

    Try this, add in on the server, where you set type and imput (no need to set type, now fileType contains extension from the frontend:

    const fs = require('fs');
    
    let imput = path.join(
              __dirname,
              "..",
              "images/temp",
              "pfp_" + id + "." + filetype
            );
    
    
    if(!fs.existsSync(imput)) {
    
            
            // no file, return error
    
    
    }
    

    EDIT

    or, simply read the file from the req.file.path, instead of passing and constructing extension, and to make sure the file is uploaded, read req.file, to make sure it exists before passing it to sharp:

    if (pfp && req.file) {
    
        const imput = path.join(__dirname, "..", req.file.path);