1

When a user updates their profile image I want to delete the old one and update the image name in the database. The newly uploaded image is uploaded using react filepond and kept being deleted as well whenever a new image was uploaded. Upon investigating, I found that the request was being sent twice so it was deleting the file after it was uploaded. After I removed the last line of code it stopped doing that and worked fine. But why?

Controller:

const multerStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "public/img");
  },
  filename: (req, file, cb) => {
    const ext = file.mimetype.split("/")[1];
    cb(null, `hero-${Date.now()}.${ext}`);
  }
});

// test if uploaded file is an image
const multerFilter = (req, file, cb) => {
  if (file.mimetype.startsWith("image")) {
    cb(null, true);
  } else {
    // throw error here
    cb(console.log("not an image"), false);
  }
};

const upload = multer({
  storage: multerStorage,
  fileFilter: multerFilter
});

exports.uploadUserPhoto = upload.single("bgImg");

exports.updateHeroImg = async (req, res) => {
  const updateId = "5d13b3c6dd57c43828ed5a7a";
  const hero = await Hero.findById(updateId);
  if (!hero) return;

  const oldImg = hero.bgImg;
  fileHelper.deleteFile("public/img/" + oldImg);

  if (req.file) hero.bgImg = req.file.filename;

  const result = await hero.save();
  res.status(200).send("image uploaded");
};

As soon as I remove this line it works fine. Any ideas?

res.status(200).send("image uploaded");

This is the route:

router.post("/", heroController.uploadUserPhoto, heroController.updateHeroImg);

In React I have:

            <FilePond
              ref={ref => (this.pond = ref)}
              files={this.state.files}
              allowMultiple={false}
              maxFiles={1}
              instantUpload={false}
              maxFileSize={5000000}
              name="bgImg"
              server={{
                process: "http://localhost:8000/api/hero/",
                load: "http://localhost:8000/img/"
              }}
              oninit={() => this.handleInit()}
              onupdatefiles={fileItems => {
                // Set currently active file objects to this.state
                this.setState({
                  files: fileItems.map(fileItem => fileItem.file)
                });
              }}
              //onprocessfile={() => this.uploadComplete()}
            />

  uploadComplete = () => {
    putImg(this.state.files);
    toast.success("Image successfully updated");
  };
user8463989
  • 2,275
  • 4
  • 20
  • 48
  • Is this function called from some express route? – Matt Kuhns Aug 08 '19 at 18:54
  • I am sending the request from react. So, I upload the file using filepond in react and send it to the server which is using express, yes. It goes to the route which goes to the controller. I updated my question to show the route – user8463989 Aug 08 '19 at 18:56
  • `router.post("/", heroController.uploadUserPhoto, heroController.updateHeroImg);` aren't you calling 2 handlers for the same route? – Zee Aug 08 '19 at 19:12
  • Router allows multiple callbacks, think passport. Are you sending status in heroController.uploadUserPhoto? – Matt Kuhns Aug 08 '19 at 19:14
  • @Zee, yes sorry, you are right. I have updated my question with the code for that. – user8463989 Aug 08 '19 at 19:14
  • @MattKuhns, I have updated my question with that code. I am not sending a status there. – user8463989 Aug 08 '19 at 19:15
  • @user8463989 Shouldn't you be calling `next()` in your middleware `heroController.uploadUserPhoto` and sending response via final callback `heroController.updateHeroImg`. – ambianBeing Aug 08 '19 at 19:16
  • @ambianBeing, not 100% sure but I don't think so. – user8463989 Aug 08 '19 at 19:22
  • @ambianBeing is on the right track. Here is something similar. Each middleware has to call next(). https://stackoverflow.com/questions/22285677/in-express-how-multiple-callback-works-in-app-get – Matt Kuhns Aug 08 '19 at 19:28
  • 1
    @user8463989 So since I have used multer, dug around its code and it does indeed call the `next()` function so your routes seem okay from the server end. You said request was being sent twice.. so checking client code what does `putImg(this.state.files);` looks like? – ambianBeing Aug 08 '19 at 19:46
  • @ambianBeing, that is pretty simple, it just looks like this: `export function putImg(img) { return axios.post(apiEndPointTwo, img); }` – user8463989 Aug 08 '19 at 19:48
  • So, I just found what was causing the problem. I used this callback for filepond: `onprocessfile={() => this.uploadComplete()}` https://pqina.nl/filepond/docs/patterns/api/filepond-instance/ but after removing that line my issue is fixed. But I don't know how it now still runs `uploadComplete` if that isn't called. I updated my question... – user8463989 Aug 08 '19 at 19:54
  • I am guessing this line is firing off the request which I didn't realise. `process: "http://localhost:8000/api/hero/",` and then I was sending it again with `onprocessfile={() => this.uploadComplete()}`. Mystery solved! (I think) – user8463989 Aug 08 '19 at 20:00
  • Yep that should be it. I was gonna dug around filepond now which I haven't used. Saved by your comment :D – ambianBeing Aug 08 '19 at 20:03
  • 1
    @ambianBeing, thanks for all your help. Just talking it through with someone helped me solve it. – user8463989 Aug 08 '19 at 20:04

0 Answers0