How to pass a unique uuid to each callback?

Tags:



I’m using multer-s3-transform, which allows me to manipulate the image coming in, before uploading it to my bucket. Here’s what I have:

const singleImageUploadJpg = multer({
  storage: multerS3({
    s3: s3,
    bucket: "muh-bucket",
    acl: "public-read",
    key: function(req, file, cb) {
      const fileName = uuid.v4();
      cb(null, fileName);
    },
    shouldTransform: function(req, file, cb) {
      cb(null, true);
    },
    transforms: [
      {
        id: "original",
        key: function(req, file, cb) {
          cb(null, `${uuid.v4()}.jpg`);
        },
        transform: function(req, file, cb) {
          cb(
            null,
            sharp()
              .resize()
              .jpeg({ quality: 50 })
          );
        }
      },
      {
        id: "small",
        key: function(req, file, cb) {
          cb(null, `${uuid.v4()}_small.jpg`);
        },
        transform: function(req, file, cb) {
          cb(
            null,
            sharp()
              .resize()
              .jpeg({ quality: 50 })
          );
        }
      }
    ]
  }),
  limits: { fileSize: 50 * 1024 * 1024 }
}).single("image");

The problem is that the uuid will always be different for the small and the original versions. How can I make const fileName = uuid.v4() passed down to each callback, so that they’d have the same name, with _small being the only difference?

Answer

I assume that multer calls the functions provided repeatedly, which is why you don’t do the obvious thing Jim Nilsson suggested. Also, sadly, you’ve said that the file you receive in the transform callback doesn’t have the name you specify earlier.

Two possibilities, both assuming that either the file object or the req object you receive is the same in both callbacks:

  1. Your own expando property
  2. A WeakMap

Expando property

You could try to piggyback it on the file/req (I use file below), like so (see *** comments):

const singleImageUploadJpg = multer({
  storage: multerS3({
    s3: s3,
    bucket: "muh-bucket",
    acl: "public-read",
    key: function(req, file, cb) {
      file.__uuid__ = uuid.v4();                   // ***
      cb(null, file.__uuid__);
    },
    shouldTransform: function(req, file, cb) {
      cb(null, true);
    },
    transforms: [
      {
        id: "original",
        key: function(req, file, cb) {
          cb(null, `${uuid.v4()}.jpg`);
        },
        transform: function(req, file, cb) {
          cb(
            null,
            sharp()
              .resize()
              .jpeg({ quality: 50 })
          );
        }
      },
      {
        id: "small",
        key: function(req, file, cb) {
          cb(null, `${file.__uuid__}_small.jpg`);  // ***
        },
        transform: function(req, file, cb) {
          cb(
            null,
            sharp()
              .resize()
              .jpeg({ quality: 50 })
          );
        }
      }
    ]
  }),
  limits: { fileSize: 50 * 1024 * 1024 }
}).single("image");

That would probably be doing something undocumented, though, which means you need to be careful to test with every “dot release” of the library you upgrade to.

WeakMap:

Alternatively, you could use a WeakMap keyed by the file or req (I use file below):

const nameMap = new WeakMap();
const singleImageUploadJpg = multer({
  storage: multerS3({
    s3: s3,
    bucket: "muh-bucket",
    acl: "public-read",
    key: function(req, file, cb) {
      const fileName = uuid.v4();
      nameMap.set(file, fileName);                  // ***
      cb(null, fileName);
    },
    shouldTransform: function(req, file, cb) {
      cb(null, true);
    },
    transforms: [
      {
        id: "original",
        key: function(req, file, cb) {
          cb(null, `${uuid.v4()}.jpg`);
        },
        transform: function(req, file, cb) {
          cb(
            null,
            sharp()
              .resize()
              .jpeg({ quality: 50 })
          );
        }
      },
      {
        id: "small",
        key: function(req, file, cb) {
          const fileName = nameMap.get(file); // ***
          nameMap.delete(file);               // *** (optional, presumably `file` will be released at some point, which would remove it automatically)
          cb(null, `${fileName}_small.jpg`);  // ***
        },
        transform: function(req, file, cb) {
          cb(
            null,
            sharp()
              .resize()
              .jpeg({ quality: 50 })
          );
        }
      }
    ]
  }),
  limits: { fileSize: 50 * 1024 * 1024 }
}).single("image");


Source: stackoverflow