addImage = async function({
  imageUrl,
  width,
  height,
  fit,
  duration,
  filename,
  force
}) {
  const key = getMediaKey({ imageUrl, width, height, fit });
  const expire = new Date().getTime() + duration;
  const oldImageEntry = await getImage({ imageUrl, width, height, fit });

  // download image to a tmp file
  const tmpFilename = getMediaTempFilename({ imageUrl, width, height, fit });
  const tmpFilePath = path.join(mediaDir, tmpFilename);
  await _downloadImageUrl(imageUrl, tmpFilePath);

  try {
    // check the image size
    const newSize = fs.statSync(tmpFilePath).size;
    if (oldImageEntry && !force) {
      const oldSize = oldImageEntry.size;
      if (oldSize == newSize) {
        // update the expire time
        await redis.client.hset(key, "expire", expire);
        logger.info(
          { imageUrl, width, height, fit },
          "_cacheImage size remains the same, only update the expire time"
        );
        return;
      }
    }

    // process the image
    if (!filename)
      filename = getMediaFilename({
        imageUrl,
        width,
        height,
        fit,
        size: newSize
      });
    const filePath = path.join(mediaDir, filename);
    await _processImage({
      sourcePath: tmpFilePath,
      destLocalPath: filePath,
      destS3Path: getMediaS3Path(filename),
      width,
      height,
      fit
    });

    // update redis
    await redis.client.hmset(key, {
      size: newSize,
      expire,
      filename
    });
  } finally {
    // remove the tmp file
    fs.unlinkSync(tmpFilePath);
  }
}
Download, process a image and upload to S3.
@param {object} param0