I had a doubt about Multer Filename.
When a multer stores the file with a random fileName then, can there be a case where two files have the same name in multer?
Basically what I want to say is If I am storing file from large numbers of users, can the filename get repeated? Can I trust multer on this or do I have to write a separate function to give a unique filename to each file?
I had a doubt about Multer Filename.
When a multer stores the file with a random fileName then, can there be a case where two files have the same name in multer?
Basically what I want to say is If I am storing file from large numbers of users, can the filename get repeated? Can I trust multer on this or do I have to write a separate function to give a unique filename to each file?
Share Improve this question edited May 11, 2021 at 14:27 Đăng Khoa Đinh 5,4113 gold badges17 silver badges36 bronze badges asked May 10, 2021 at 13:21 Tejas ShirnalkarTejas Shirnalkar 1531 silver badge8 bronze badges 3- Yes, you can trust it – user15388024 Commented May 10, 2021 at 13:22
- thanks ! does it use date.now() only cause that can be reapeted? – Tejas Shirnalkar Commented May 10, 2021 at 13:27
- It's open source. Here is the code you're interested in. – user15388024 Commented May 10, 2021 at 13:30
3 Answers
Reset to default 9From the npm page here, we can see Multer's example code to store a file on disk :
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })
The filename is concatenated with '-' + Date.now()
. Date.now()
give you the number of milliseconds from 1st January 1970. So, if 2 files are saved at different times, they have different names. And theoretically, if 2 files are saved on the system at exactly the same time (milliseconds unit) they will have the same name.
So, it does not depend on how many files you store but how fast requests e to your system. If you have more than 1000 requests per second, there will be a chance for duplication. In this case, you should use something like UUID. Otherwise, Date.now()
does the work.
- You can read more about
Date.now()
here. - Wikipedia page about UUID here, in case you need it. It also has a npm package.
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '../media/');
}
,
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now()+'.'+file.mimetype.split('/')[1]);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(null, false);
}
}
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
}).single('profile_picture');
The uniqueness of each uploaded file is achieved by generating a filename that incorporates the current timestamp (in milliseconds) using Date.now() and appending it to the original fieldname of the uploaded file. This bination ensures that each file uploaded, even if they have the same original name, will have a different filename because the timestamp will be different at the time of upload. const multer = require('multer');
Timestamps should not, in general, be used to generate unique identifiers.
While for a single threaded application, where a low number of requests are made to an endpoint per second, the probability of a name collision is essentially zero, it is 2023 and we have been living in a world of big data, big tech platforms, and horizontal scalability for at least a decade now, so we should do things in a robust way.
Here's how to do it using the uuidv4
module:
const storage = multer.diskStorage({
destination: (request, file, cb) => {
cb(null, "./uploads/");
},
filename: (request, file, cb) => {
// get a uuid
const fileuuid = uuid();
// get the filename extension from the original file name
const uuidFilenameExtension = file.originalname.split(".").pop();
// construct a new filename based on the uuid and preserve the
// original extension
const uuidFilename = fileuuid + "." + uuidFilenameExtension;
cb(null, uuidFilename);
},
});
Why is this important?
Consider horizontal scalability. Our application may one day be used to serve multiple millions of clients with many thousands of requests per second. With a resolution of milliseconds from the Date.now()
function call, it is easy to see how if we were to use this to generate filenames we will encounter name conflicts. (Probably surprisingly often depending on user activity at peak times.)
We might scale our frontend but keep all uploaded images on the same storage server or filesystem - so duplications in names are not an unrealistic concern.
In regards to security: Do you really want a situation where two users upload a profile picture and one user is surprised to find their picture is of someone else? (You might consider how to write your application logic to ensure this cannot happen.)
When using UUIDs, it is not totally impossible to get name collisions, but the uncorrelated nature of sequential calls to a uuid creation function ensures the probability of this is low. If UUIDs are 128 bits in entropy, (they very often are) then this is double what most timestamps are. Timestamps are usually 64 bits long.
When calling a timestamp function, the results will be highly correlated because they are sequential in time. With cryptographic UUIDs this is not the case.
See also: https://www.npmjs./package/uuidv4
Addition - Deprecation Warning use v4
:
DeprecationWarning: uuidv4() is deprecated. Use v4() from the uuid module instead.
If you get a warning about deprication use this module instead:
const { v4 } = require('uuid')
then
const fileuuid = v4();