82

Managed to store my files in a folder but they store without the file extension.

Does any one know how would I store the file with file extension?

nbro
  • 15,395
  • 32
  • 113
  • 196
user3355603
  • 1,091
  • 2
  • 12
  • 19

18 Answers18

207

I have a workaround for the adding proper extension of files. If you use path node module

var multer = require('multer');
var path = require('path')

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/')
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + path.extname(file.originalname)) //Appending extension
  }
})

var upload = multer({ storage: storage });
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
sree
  • 3,113
  • 2
  • 18
  • 13
  • 2
    There's a problem with this solution, it doesn't account for files that do not have a file extension. You need to use the mime type of the file in that case. – m.e.conroy Dec 17 '20 at 22:37
  • 1
    I agree, this is the best solution. In order to figure out the right extension you can make use of mime-types. https://www.npmjs.com/package/mime-types – Rafael Mejía Dec 31 '20 at 00:53
  • This works for me too. With _path.extname(file.originalname)_, you don't need to provide the dot (.) to build a filename. – yaach Sep 08 '21 at 00:25
58

From the docs: "Multer will not append any file extension for you, your function should return a filename complete with an file extension."

Here's how you can add the extension:

var multer = require('multer');

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/')
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + '.jpg') //Appending .jpg
  }
})

var upload = multer({ storage: storage });

I would recommend using the mimetype property to determine the extension. For example:

filename: function (req, file, cb) {
  console.log(file.mimetype); //Will return something like: image/jpeg

More info: https://github.com/expressjs/multer

Scott
  • 863
  • 9
  • 12
  • 1
    i follow this but my files are uploaded in C: drive of my computer under uploads folder.how to save them in my project directory. – kisor Jun 23 '16 at 11:43
  • 1
    @kisor you're after [_dirname](https://nodejs.org/docs/latest/api/globals.html#globals_dirname) – Scott Jul 10 '16 at 17:03
  • 2
    This answer just points that Multer does not append a file extension, but the OP asks for a way to append the extension. The recommendation is valid but dowsnt answer the main question. – Jean Paul Rumeau May 07 '21 at 02:02
22

I got file the extension from file.mimetype . I split the mimetype and get the file extension from it Please try the below function.

let storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, './uploads')
  },
  filename: function (req, file, cb) {
    let extArray = file.mimetype.split("/");
    let extension = extArray[extArray.length - 1];
    cb(null, file.fieldname + '-' + Date.now()+ '.' +extension)
  }
})
const upload = multer({ storage: storage })
VISHNU
  • 948
  • 8
  • 15
13

It can be done like this:

var storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, config.DIR)
    },
    filename: function (req, file, cb) {
        let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
        cb(null, Date.now() + ext)
    }
});
const upload = multer({
    storage: storage
}).any();
Edric
  • 24,639
  • 13
  • 81
  • 91
Zohaib Aslam
  • 336
  • 3
  • 13
  • This suffers from file names that don't have the extension in the name of the file. You should be using the mimetype in some fashion. – m.e.conroy Jul 30 '19 at 13:12
9
import multer from 'multer';
import * as shortid from 'shortid';
import * as mime from 'mime-types';

const storage = multer.diskStorage({
  destination: function (req,file,cb) {
    cb(null, '/path/to/uploads/');
  },
  filename: function (req,file,cb) {
    /* generates a "unique" name - not collision proof but unique enough for small sized applications */
    let id = shortid.generate();
    /* need to use the file's mimetype because the file name may not have an extension at all */
    let ext = mime.extension(file.mimetype);
    cb(null, `${id}.${ext}`);
  }
});

EDIT

shortid has been deprecated you should use nanoid.

import multer from 'multer';
import * as nanoid from 'nanoid';
import * as mime from 'mime-types';

const storage = multer.diskStorage({
  destination: function (req,file,cb) {
    cb(null, '/path/to/uploads/');
  },
  filename: function (req,file,cb) {
    /* generates a "unique" name - not collision proof but unique enough for small sized applications */
    let id = nanoid();
    /* need to use the file's mimetype because the file name may not have an extension at all */
    let ext = mime.extension(file.mimetype);
    cb(null, `${id}.${ext}`);
  }
});
m.e.conroy
  • 3,508
  • 25
  • 27
4

There may be some issues in the already answered codes.

  • There may be some cases of files with no extension.
  • There should not be an upload.any() usage. Its vulnerable to the attackers
  • The upload function should not be global .

I have written the below codes for better security.

var storage = multer.diskStorage({
    destination: function (req, file, cb) {

        cb(null, 'temp/')
    },
    filename: function (req, file, cb) {
        let ext = ''; // set default extension (if any)
        if (file.originalname.split(".").length>1) // checking if there is an extension or not.
            ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
        cb(null, Date.now() + ext)
    }
})
var upload = multer({ storage: storage });

Using it for upload

// using only single file object name (HTML name attribute)
// May use upload.array(["file1","file2"]) for more than one
app.post('/file_upload', upload.single("file"), function (req,res) {
    //console.log(req.body, 'Body');
    console.log(req.file, 'file');
    res.send("cool");
})
  • 1
    Thanks for your input here, but this doesn't answer the question. Please consider posting a new question and self-answer it if you'd like to store this knowledge on StackOverflow. Also read the help section before you do. – Graham Feb 15 '18 at 01:56
4

I used this little trick to get file extension, and as a workaround to circumvent issues that might occur when someone uploads a file with similar file name twice, or that exists in the server.

const path = require('path');
const crypto = require('crypto');

let upload = multer({
storage: multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, path.join(__dirname, '../uploads'))
    },
    filename: (req, file, cb) => {
        // randomBytes function will generate a random name
        let customFileName = crypto.randomBytes(18).toString('hex')
        // get file extension from original file name
        let fileExtension = path.extname(file.originalname).split('.')[1];
        cb(null, customFileName + '.' + fileExtension)
    }
  })
})
Danny Sofftie
  • 1,031
  • 1
  • 8
  • 16
2

const multer = require('multer'); const uuid = require('uuid/v1');

const MIME_TYPE_MAP = {
  'image/png': 'png',
  'image/jpeg': 'jpeg',
  'image/jpg': 'jpg'
};

const fileUpload = multer({
  limits: 500000,
  storage: multer.diskStorage({
    destination: (req, file, cb) => {
      cb(null, 'uploads/images');
    },
    filename: (req, file, cb) => {
      const ext = MIME_TYPE_MAP[file.mimetype];
      cb(null, uuid() + '.' + ext);
    }
  }),
  fileFilter: (req, file, cb) => {
    const isValid = !!MIME_TYPE_MAP[file.mimetype];
    let error = isValid ? null : new Error('Invalid mime type!');
    cb(error, isValid);
  }
});

module.exports = fileUpload;
rahulyadav
  • 73
  • 2
1

The file extension can be dynamic. here is the solution

const path = require('path'); // path for cut the file extension
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
      cb(null, 'uploads')
    },
    filename: function (req, file, cb) {
      cb(null, 'upload_at_' + Date.now() + path.extname(file.originalname))
    }
  })
Abid
  • 181
  • 3
  • 8
1

I use this method and it works.

I store the file in this format: FieldName+Date+Extension => Profile1621416613594.jpg

var multer = require('multer');
    
var storage = multer.diskStorage({
    destination: function (req,file,cb){
        cb(null, './uploads')
    },
    filename: function (req,file,cb){
        cb(null,file.fieldname+'-'+Date.now()+'.'+file.mimetype.split('/').reverse()[0]);
    },
});

var upload = multer({storage: storage}); 
Reza Hadipour
  • 52
  • 1
  • 9
1
let fileext = file.originalname.split('.').pop();

cb(null,'profile_' + Date.now() + file.originalname.substring(0, 10).split(' ').join('-') + '.' + fileext)
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
  • 1
    While this code may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply – jmoerdyk May 10 '23 at 16:29
0

I am doing like this

var multer  = require('multer');

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, './public/uploads/img/')
  },
  filename: function (req, file, cb) {
    let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
    cb(null, Date.now() + ext);
  }
})

var upload = multer({ storage: storage }).single('eventimage');
NewUser
  • 12,713
  • 39
  • 142
  • 236
0

An object oriented way to store image with unique name

// image.service.ts
import { diskStorage, StorageEngine } from "multer";

class ImageStorageService {

    storage: StorageEngine
    constructor() {
        const MIME_TYPE_MAP = {
            'image/png': 'png',
            'image/jpeg': 'jpg',
            'image/jpg': 'jpg'
        }

        this.storage = diskStorage({
            destination: (req, file, callback) => {
                const isValid = MIME_TYPE_MAP[file.mimetype]
                let error = new Error(`Invalid mime type`)
                if (isValid)
                    error = null

                //app.use(express.static(path.join(`${__dirname}/assets`)))
                callback(error, 'assets/images')
            },
            filename: (req, file, callback) => {
                let currentFileName: string = file.originalname.substr(0, file.originalname.lastIndexOf('.'))
                const name = currentFileName.toLowerCase().split(' ').join('-')
                const ext = MIME_TYPE_MAP[file.mimetype]
                callback(null, `${name}-${Date.now()}.${ext}`)
            }
        })
    }
}

export const ImageStorage = new ImageStorageService().storage

then in one of your routes

import { ImageStorage } from "./services/image-storage.service";

this.router.post('/signup', multer({ storage: ImageStorage }).single('image'), async (req, res, next) => {
    let img_url: string
    if (req.file) {
        const url: string = `${req.protocol}:\/\/${req.get('host')}`
        img_url = url + '/images/' + req.file.filename
        //http://localhost:3000/images/penguins-1548339248380.jpg
    }
})
WasiF
  • 26,101
  • 16
  • 120
  • 128
0

I like to use the original filename for SEO purposes. This requires a bit more checking if the file with the same name already exists. Moreover, extension resolving is done in a few steps to provide maximum flexibility.

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/')
  },
  filename: function (req, file, cb) {
    // try to get extension from original file name
    var lioDot = file.originalname.lastIndexOf('.');
    if (lioDot !== -1) {
      // I like to use original upload filename for SEO but first lets clean it 
      var newName = file.originalname.substring(0, lioDot).replace(/([^a-z0-9]+)/gi, '-');
      var ext = file.originalname.substring(lioDot, file.originalname.length);
    } else {
      var newName = file.originalname.replace(/([^a-z0-9]+)/gi, '-');
      // try to get extension from mime type string
      var extArray = file.mimetype.split("/");
      var ext = extArray[extArray.length - 1];
      // mime type extension resolving by pure string extraction is not accurate for a lot of types
      // https://www.freeformatter.com/mime-types-list.html
      // it's usually fine for ext strings up to 4 characters, png, jpeg, gif, bmp, tiff ..
      if (ext > 4) {
        // other mime types you would like to support
        var mimetypes = { 'vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx' };
        if (mimetypes.hasOwnProperty(ext)) ext = mimetypes[ext];
      }
    }

    var newFullName = newName + ext;
    var i = 0;
    // we need to check if the file with the same name already exists
    // if it exists then we're adding something to make it unique 
    while (fs.existsSync(process.env.PWD + '/uploads/' + newFullName)) {
      newFullName = newName + '-' + ++i + ext;
    }
    cb(null, newFullName);
  }
})

const upload = multer({ storage: storage });
maxxx
  • 657
  • 7
  • 5
0

It can be done like this...simple to grasp

// validate uploaded files
const FILE_TYPE_MAP = {
  // mime type
  "image/png": "png",
  "image/jpeg": "jpeg",
  "image/jpg": "jpg",
};
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "public/uploads");
  },
  filename: function (req, file, cb) {
    const filename = file.originalname.replace(" ", "-");
    const extension = FILE_TYPE_MAP[file.mimetype]
    cb(null, `${filename}-${Date.now()}.${extension}`);
  },
});
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-ask). – Community Sep 11 '21 at 07:12
  • This works. However it don't matter if I upload .jpeg or .jpg. I always ends up with the extension .jpeg. – mikael1000 Jan 17 '22 at 16:06
0

Simple helpler function that maintains the unique filename generated by multer and adds the extension parsed from mimetype:

Just pass the object returned by multer


const fs = require('fs');

function renameWithExt(file) {
  const ext = file.mimetype.split('/')[1]; // parse the extension type

  fs.rename(`${file.path}`, `${file.path}.${ext}`, () => {
    console.log(`File: ${file.filename} renamed with extension '.${ext}'`);
  });
}

renameWithExt(req.file);

Ryan
  • 1
  • 2
  • 3
0
const storageAttach = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/attachFile')
  },
  filename: function(req, file, cb) {
    let file_name_string = String(file.originalname).split('.')
    let ext = file_name_string[file_name_string.length - 1]
    console.log('file_storage', file.originalname.split('.')[file.origin]);
      
    cb(null, file.fieldname + '-' + Date.now() + `.${ext}`) // set the extension to .jpg
  }
})
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
Rakhmadi
  • 1
  • 1
  • 1
  • 1
    While this code may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply – jmoerdyk May 10 '23 at 16:29
  • Welcome to SO, thank you for participating. However, I am not sure what this answer adds to the already provided ones. It seems to be the exact same answer as [this one](https://stackoverflow.com/a/48798870/4883195), except that it lacks the check if there is a `.` in filename. – Moritz Ringler May 13 '23 at 12:03
-1

const multer = require('multer');
const uuid = require('uuid/v1');

const MIME_TYPE_MAP = {
  'image/png': 'png',
  'image/jpeg': 'jpeg',
  'image/jpg': 'jpg'
};

const fileUpload = multer({
  limits: 500000,
  storage: multer.diskStorage({
    destination: (req, file, cb) => {
      cb(null, 'uploads/images');
    },
    filename: (req, file, cb) => {
      const ext = MIME_TYPE_MAP[file.mimetype];
      cb(null, uuid() + '.' + ext);
    }
  }),
  fileFilter: (req, file, cb) => {
    const isValid = !!MIME_TYPE_MAP[file.mimetype];
    let error = isValid ? null : new Error('Invalid mime type!');
    cb(error, isValid);
  }
});

module.exports = fileUpload;
Asif vora
  • 3,163
  • 3
  • 15
  • 31
rahulyadav
  • 73
  • 2