Moritz
Moritz

Reputation: 426

multer filter files with multiple fields and different file types in each field

I'm writing an application for a client, and they should be able to upload images and documents for a product.

Pug/ Jade/ Html

The html form/ fieldset has two file inputs. one for the images and another one for the documents.

Example

input.product-upload-images(
  type="file", 
  multiple="multiple",
  value="images",
  accept="image/png, image/jpg ,image/jpeg")

input.product-upload-documents(
  type="file",
  multiple="multiple",
  value="Documents",
  accept="application/pdf")

Router

In my POST/ PUT controller I want to check with a middleware if all files in the field images[] are MIME-type image/... and if all files in the field documents[] are MIME-type image/pdf.

Example

I removed some middlewares, like the passport middleware, or the data validation midddleware, to provide an easy to understand example.

const uploadPath = path.join('some', 'path');
const upload = multer({
    dest: uploadPath,
    fileFilter: (req, file, cb) => {
        // filter filetype by field
    }
});
const uploadMiddleware = upload.fields([
    {name: 'documents[]', maxCount: 10},
    {name: 'images[]', maxCount: 10}
]);

// ...

router.post(
    '/',
    uploadMiddleware,
    async (req, res, next) => {
        // ...

Question

How can I accomplish the implementation of a file filter, which can filter for a certain field a file type?

Upvotes: 0

Views: 541

Answers (2)

ajimae
ajimae

Reputation: 107

I will go ahead and modify your code to include the implementation for a fileFilter callback function for multiple fields upload.

...
const upload = multer({
    dest: uploadPath,
    fileFilter: (req, file, cb) => {
      // filter filetype by field - `file` will all be a single File object
      if (['image/png', 'image/jpeg', 'application/pdf'].include(file.mimetype)) {
        cb(null, true);
      } else {
        cb(new Error('unknown file type.'));
      }
});
...

It is important to note that in as much as the uploads are fields - where each field can contain multiple file types, multer will dump all fields in the fileFilter callback as individual file object and not as an array, it does this by calling the fileFilter callback for each file. However, in your controller you will have the fields sorted and properly added into an array.

For instance, assuming the documents field and images field contains 3 and 2 files respectively then multer will list 5 objects in the fileFilter callback function and in the controller (final middleware handler) create an array of objects with the key documents and images: req.files will print the following object.

[Object: null prototype] {
  documents: [
    {
      fieldname: 'documents',
      originalname: 'doc.pdf',
      encoding: '7bit',
      mimetype: 'document/pdf',
      ...
    },
    {...},
    {...}
  ],
  images: [
    {
      fieldname: 'image1',
      originalname: 'avatar.jpg',
      encoding: '7bit',
      mimetype: 'image/jpeg',
      ...
    },
    {...}
  ]
}

Note In the below snippet from your code:

const uploadMiddleware = upload.fields([
  {name: 'documents[]', maxCount: 10},
  {name: 'images[]', maxCount: 10}
]);

I assumed the { name: 'documents[]', maxCount: 10 } and { name: 'images[]', maxCount: 10 } to be { name: 'documents', maxCount: 10 } and { name: 'images', maxCount: 10 }

Upvotes: 0

scrutycs
scrutycs

Reputation: 29

You can also specify the destination via a Callback.

const exampleMimeType = 'image'
const storage = multer.destination({
  destination: (req, file, cb) => {
    if(file.mimeType === exmpleMimeType){
      cb(null, `/files/${exampleMimeType}`)
    }
  }


})

Upvotes: -1

Related Questions