0

I have the following piece of code: an endpoint getting the URL of an image, downloading it, resize it, saving it locally, then uploading it to a S3 bucket. Every piece of code works fine on it's own.

// Post a URL of a pic and get a URL of its thumb on AWS S3
router.post("/", (req, res) => {

  const url = req.body.url;
  const tempPath = './public/uploads/';
  const tempFileName = uuidv1();

  const createThumbPic = async () => {
    const tempURL = tempPath + tempFileName;
    jimp.read(url, async (err, img) => {
      if (err) {
        res.status(400).json({
          error: `${err})`,
        });
        return;
      }
      img.resize(120, 120)
        .quality(60)
        .write(tempURL);
    });
    return tempURL;
  }

  const uploadThumbnailToS3 = async () => {
    const tempURLThumb = await createThumbPic();
    const file = await fs.readFileSync(tempURLThumb);
    const params = {
      Bucket: process.env.S3_BUCKET_ID,
      Key: tempFileName,
      Body: file
    };
    // Uploading files to the bucket
    const fileUrlOns3 = await s3.upload(params, function (err, data) {
      if (err) {
        res.status(400).json({ error: err });
      }
      res.status(201).json({ thumbUrl: resData });
      return data.Location;
    });
    return fileUrlOns3;
  }

  // Execute Code and respond to client
  uploadThumbnailToS3().catch((err) => {
    res.status(400).json({ error: err });
  });

});

the Error I am getting back from this endpoint:

{
    "error": {
        "errno": -2,
        "syscall": "open",
        "code": "ENOENT",
        "path": "./public/uploads/imagefiletemp.jpg"
    }
}

The problem comes from the follwing to lines: It seems that fs.readFileSyncruns before that createThumbPic() is totally finished. Hence that the readFileSync doesn't find the file it should read, and throw that error back.

const tempURLThumb = await createThumbPic();
const file = await fs.readFileSync(tempURLThumb);

Somehow I know that createThumbPic return tempURLThumb before that the fiel is finished, as I get the response in postman, than 1 sec later I see the file appearing in my directory.

const createThumbPic = async () => {
    const tempURL = tempPath + tempFileName;
    jimp.read(url, async (err, img) => {
      if (err) {
        res.status(400).json({
          error: `${err})`,
        });
        return;
      }
      img.resize(120, 120)
        .quality(60)
        .write(tempURL);
    });
    return tempURL;
  }

How to make createThumbPicwait until img.resize is completely finished before returning tempURL?

I tried my best learning ASYNC/AWAIT, and thought I had figured, but it seems like something is still off in my code. Any tip?

  • that is because you declared `uploadFileS3` as `async`. Under the hood, js will create a promise and then you should use `await` as Aaron pointed out – CarlosCarucce Nov 23 '20 at 19:47
  • I added a second duplicate question that explains your updated problem. – str Nov 23 '20 at 20:16
  • 1
    Regarding your update: `createThumbPic` does not wait for `jimp.read` to complete before it returns. You might want to read [How do I convert an existing callback API to promises?](https://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises). – str Nov 25 '20 at 08:49
  • 1
    Don't pass callbacks to something that you expect to get a promise from, both the `jimp.read` and `s3.upload` calls. – Bergi Nov 25 '20 at 12:05

1 Answers1

1

You need to await your promise:

        // upload to S3
        const thumbOnS3 = await uploadFileS3(tempURL, tempFileName);
        console.log(thumbOnS3)
Aaron Stuyvenberg
  • 3,437
  • 1
  • 9
  • 21
  • That's what I thought, but If I add await, I get `SyntaxError: await is only valid in async function`, because jimp.read is not async. –  Nov 23 '20 at 19:46
  • 1
    or uploadFileS3(tempURL, tempFileName).then(thumbOnS3 => console.log(thumbOnS3); – bmacnaughton Nov 23 '20 at 19:46