phoebus
phoebus

Reputation: 1329

NodeJS - multer - change filename depending on request attributes

I know that I can change the filename with multer by means of the storage object like following:

const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, process.env.UPLOAD_DIR);
    },
    filename: (req, file, cb) => {
        cb(null, 'bla.png');
    }
});
const upload = multer({ storage: storage } );

My request, besides having the file, also contains some text attributes such as name: myPic.png.

Is it possible to dynamically change the filename dependent on other request attributes or within the controller like following:

filename: (req, file, cb) => {
     cb(null, `${req.body.name}.png`);
}

or

router.post('/upload', upload.single('pic'), myController.upload);

/* in controller */
upload = async (req: Request, res: Response) => {
    try {

        /* change the filename of multer here? */

    } catch (err) {
        winston.error(`Error while uploading: ${err.message}`);
        winston.error(`Stack trace: ${err.stack}`);
        sendJSONResponse(res, err, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Upvotes: 3

Views: 1669

Answers (2)

Vladimir Salguero
Vladimir Salguero

Reputation: 5947

According to the multer documentation it does not have access to req.body for other additional fields, if you test it it receives an undefined value, then a not so perfect but functional solution is the following, once the file is uploaded you can rename it as follows.

  1. Add the native class fs for access to files option

    const fs = require('fs');
    
  2. In diskStorage configuration add the name you want, for example bla.png

    var storage = multer.diskStorage({
        destination: path.join('public/images/'),
        filename: function ( req, file, cb ) {          
            cb(null, 'bla.png');          
        }
    });
    
  3. The form with the text field for the custom name

    <form action="/upload" enctype="multipart/form-data" method="POST">
        <input type="file" accept="image/*" name="photo" >
        <br><!--here is the custom file name-->
        <input type="text" name="file_name">
        <br> 
        <button type="submit">Send</button>
    </form>
    
  4. Within the post path, once you have sent the file whose name will be bla.png, you can replace that name with the one in a field of the form by accessing req.body.field_name

    router.post('/upload', upload.single('photo'), (req, res) => {
        //Here change the file name bla.png for the new value in req.body.field_name + original ext of file
        fs.renameSync(req.file.path, req.file.path.replace('bla.png', 
        req.body.field_name + path.extname(req.file.originalname)));
        if(req.file) { 
            res.json(req.file);
        }
        else throw 'error';
    });
    

Upvotes: 2

Vasan
Vasan

Reputation: 4956

Multer is the middleware which both populates req.body AND stores the file.

Also, when it reaches the filename() function, there is no guarantee that the text fields will be populated in req.body because it depends on which order the client sends them in (see last note).

From what I see, you have two options:

1) Rename the uploaded file after the multer upload middleware does its thing and populates req.body as well as req.file. So in your controller upload middleware, you'd do something like:

if (req.file) {
    fs.renameSync(req.file.path, req.file.destination + req.body.name);
}

2) Change the request body text field into a query parameter. Then, inside filename() you can do a req.query.name.

Con: Not a very RESTful design, but maybe that is not so important to you.

Upvotes: 1

Related Questions