Merge pull request #126 from RomanBurunkov/master

Adds file size and md5 hash support
dev v1.1.1-alpha.4
Richard Girges 5 years ago committed by GitHub
commit 9f0eed23f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      README.md
  2. 3
      lib/fileFactory.js
  3. 6
      lib/memHandler.js
  4. 3
      lib/processMultipart.js
  5. 15
      lib/tempFileHandler.js
  6. 2
      package.json
  7. 14
      test/fileFactory.spec.js
  8. 168
      test/multipartUploads.spec.js
  9. 63
      test/server.js
  10. 38
      test/tempFile.spec.js

@ -6,6 +6,11 @@ Simple express middleware for uploading files.
[![downloads per month](http://img.shields.io/npm/dm/express-fileupload.svg)](https://www.npmjs.org/package/express-fileupload)
[![Coverage Status](https://img.shields.io/coveralls/richardgirges/express-fileupload.svg)](https://coveralls.io/r/richardgirges/express-fileupload)
# Version 1.1.1 Breaking Changes
Breaking change to `md5` handling.
md5 again returns a hash value instead of function which compute the hash.
md5 hashes now can be used with tempFiles.
# Version 1.0.0 Breaking Changes
Breaking change to `md5` handling. [Read about it here.](https://github.com/richardgirges/express-fileupload/releases/tag/v1.0.0-alpha.1)
@ -37,7 +42,8 @@ The **req.files.foo** object will contain the following:
* `req.files.foo.mimetype`: The mimetype of your file
* `req.files.foo.data`: A buffer representation of your file
* `req.files.foo.truncated`: A boolean that represents if the file is over the size limit
* `req.files.foo.md5`: A function that returns an MD5 checksum of the uploaded file
* `req.files.foo.size`: Uploaded size in bytes
* `req.files.foo.md5`: MD5 checksum of the uploaded file
### Examples
* [Example Project](https://github.com/richardgirges/express-fileupload/tree/master/example)
@ -55,7 +61,7 @@ app.use(fileUpload({
### Using useTempFile Options
Use temp files instead of memory for managing the upload process.
Please note: md5 hashes will not be generated when using tempFiles
```javascript
app.use(fileUpload({
useTempFiles : true,

@ -3,7 +3,6 @@
const fs = require('fs');
const path = require('path');
const streamifier = require('streamifier');
const md5 = require('md5');
/**
* Returns true if argument is function.
@ -74,7 +73,7 @@ module.exports = function(options, fileUploadOptions = null) {
tempFilePath: options.tempFilePath,
truncated: options.truncated,
mimetype: options.mimetype,
md5: () => md5(options.buffer),
md5: options.hash,
mv: function(filePath, callback) {
// Determine propper move function.
let moveFunc = (options.buffer.length && !options.tempFilePath)

@ -1,3 +1,4 @@
const crypto = require('crypto');
/**
* memHandler - In memory upload handler
@ -8,10 +9,12 @@
module.exports = function(options, fieldname, filename) {
let buffers = [];
let fileSize = 0; // eslint-disable-line
let hash = crypto.createHash('md5');
return {
dataHandler: function(data) {
buffers.push(data);
hash.update(data);
fileSize += data.length;
if (options.debug) {
return console.log('Uploading %s -> %s, bytes: %d', fieldname, filename, fileSize); // eslint-disable-line
@ -26,6 +29,9 @@ module.exports = function(options, fieldname, filename) {
getFileSize: function(){
return fileSize;
},
getHash: function(){
return hash.digest('hex');
},
complete: function(){
return Buffer.concat(buffers);
},

@ -53,7 +53,7 @@ module.exports = function processMultipart(options, req, res, next) {
// Build req.files fields
busboy.on('file', function(fieldname, file, filename, encoding, mime) {
const {dataHandler, getFilePath, getFileSize, complete, cleanup} = options.useTempFiles
const {dataHandler, getFilePath, getFileSize, getHash, complete, cleanup} = options.useTempFiles
? tempFileHandler(options, fieldname, filename)
: memHandler(options, fieldname, filename);
@ -127,6 +127,7 @@ module.exports = function processMultipart(options, req, res, next) {
buffer,
tempFilePath: getFilePath(),
size: getFileSize(),
hash: getHash(),
encoding,
truncated: file.truncated,
mimetype: mime

@ -1,8 +1,10 @@
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
module.exports = function(options, fieldname, filename) {
const dir = path.normalize(options.tempFileDir || process.cwd() + '/tmp/');
let hash = crypto.createHash('md5');
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
@ -14,6 +16,7 @@ module.exports = function(options, fieldname, filename) {
return {
dataHandler: function(data) {
writeStream.write(data);
hash.update(data);
fileSize += data.length;
if (options.debug) {
return console.log( // eslint-disable-line
@ -29,16 +32,20 @@ module.exports = function(options, fieldname, filename) {
getFileSize: function(){
return fileSize;
},
getHash: function(){
return hash.digest('hex');
},
complete: function(){
writeStream.end();
//return empty buffer since data has been uploaded to the temporary file.
return Buffer.concat([]);
},
cleanup: function(){
writeStream.end();
fs.unlink(tempFilePath, function(err) {
if (err) throw err;
});
},
complete: function(){
writeStream.end();
return Buffer.concat([]); //return empty buffer since uploaded to the temporary file.
}
};
};

@ -11,7 +11,6 @@
},
"dependencies": {
"busboy": "^0.2.14",
"md5": "^2.2.1",
"streamifier": "^0.1.1"
},
"engines": {
@ -30,6 +29,7 @@
"license": "MIT",
"repository": "richardgirges/express-fileupload",
"devDependencies": {
"md5": "^2.2.1",
"body-parser": "^1.18.3",
"coveralls": "^3.0.2",
"eslint": "^5.9.0",

@ -9,6 +9,11 @@ const server = require('./server');
const mockBuffer = fs.readFileSync(path.join(server.fileDir, 'basketball.png'));
/**
* Returns true if argument is function.
*/
const isFunc = func => (func && func.constructor && func.call && func.apply) ? true : false;
describe('Test of the fileFactory factory', function() {
beforeEach(function() {
server.clearUploadsDir();
@ -70,9 +75,16 @@ describe('Test of the fileFactory factory', function() {
it('contains the md5 property', function() {
const mockMd5 = md5(mockBuffer);
assert.equal(fileFactory({
name: 'basketball.png',
buffer: mockBuffer,
hash: mockMd5
}).md5, mockMd5);
});
it('contains the mv method', function() {
assert.equal(isFunc(fileFactory({
name: 'basketball.png',
buffer: mockBuffer
}).md5(), mockMd5);
}).mv), true);
});
});
});

@ -1,6 +1,7 @@
'use strict';
const fs = require('fs');
const md5 = require('md5');
const path = require('path');
const request = require('supertest');
const server = require('./server');
@ -37,6 +38,9 @@ describe('Test Single File Upload', function() {
it(`upload ${fileName} with POST`, function(done) {
let filePath = path.join(fileDir, fileName);
let fileBuffer = fs.readFileSync(filePath);
let fileHash = md5(fileBuffer);
let fileStat = fs.statSync(filePath);
let uploadedFilePath = path.join(uploadDir, fileName);
clearUploadsDir();
@ -44,7 +48,17 @@ describe('Test Single File Upload', function() {
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(200)
.expect((res)=>{
res.body.uploadDir = '';
res.body.uploadPath = '';
})
.expect(200, {
name: fileName,
md5: fileHash,
size: fileStat.size,
uploadDir: '',
uploadPath: ''
})
.end(function(err) {
if (err) {
return done(err);
@ -56,6 +70,9 @@ describe('Test Single File Upload', function() {
it(`upload ${fileName} with PUT`, function(done) {
let filePath = path.join(fileDir, fileName);
let fileBuffer = fs.readFileSync(filePath);
let fileHash = md5(fileBuffer);
let fileStat = fs.statSync(filePath);
let uploadedFilePath = path.join(uploadDir, fileName);
clearUploadsDir();
@ -63,7 +80,17 @@ describe('Test Single File Upload', function() {
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(200)
.expect((res)=>{
res.body.uploadDir = '';
res.body.uploadPath = '';
})
.expect(200, {
name: fileName,
md5: fileHash,
size: fileStat.size,
uploadDir: '',
uploadPath: ''
})
.end(function(err) {
if (err) {
return done(err);
@ -108,6 +135,9 @@ describe('Test Single File Upload w/ .mv() Promise', function() {
it(`upload ${fileName} with POST w/ .mv() Promise`, function(done) {
let filePath = path.join(fileDir, fileName);
let fileBuffer = fs.readFileSync(filePath);
let fileHash = md5(fileBuffer);
let fileStat = fs.statSync(filePath);
let uploadedFilePath = path.join(uploadDir, fileName);
clearUploadsDir();
@ -115,7 +145,17 @@ describe('Test Single File Upload w/ .mv() Promise', function() {
request(app)
.post('/upload/single/promise')
.attach('testFile', filePath)
.expect(200)
.expect((res)=>{
res.body.uploadDir = '';
res.body.uploadPath = '';
})
.expect(200, {
name: fileName,
md5: fileHash,
size: fileStat.size,
uploadDir: '',
uploadPath: ''
})
.end(function(err) {
if (err) {
return done(err);
@ -127,6 +167,9 @@ describe('Test Single File Upload w/ .mv() Promise', function() {
it(`upload ${fileName} with PUT w/ .mv() Promise`, function(done) {
let filePath = path.join(fileDir, fileName);
let fileBuffer = fs.readFileSync(filePath);
let fileHash = md5(fileBuffer);
let fileStat = fs.statSync(filePath);
let uploadedFilePath = path.join(uploadDir, fileName);
clearUploadsDir();
@ -134,7 +177,17 @@ describe('Test Single File Upload w/ .mv() Promise', function() {
request(app)
.post('/upload/single/promise')
.attach('testFile', filePath)
.expect(200)
.expect((res)=>{
res.body.uploadDir = '';
res.body.uploadPath = '';
})
.expect(200, {
name: fileName,
md5: fileHash,
size: fileStat.size,
uploadDir: '',
uploadPath: ''
})
.end(function(err) {
if (err) {
return done(err);
@ -175,34 +228,53 @@ describe('Test Single File Upload w/ .mv() Promise', function() {
describe('Test Multi-File Upload', function() {
it('upload multiple files with POST', function(done) {
let upload1 = path.join(fileDir, mockFiles[0]);
let upload2 = path.join(fileDir, mockFiles[1]);
let upload3 = path.join(fileDir, mockFiles[2]);
let req = request(app).post('/upload/multiple');
clearUploadsDir();
request(app)
.post('/upload/multiple')
.attach('testFile1', upload1)
.attach('testFile2', upload2)
.attach('testFile3', upload3)
.expect(200)
let expectedResult = [];
let expectedResultSorted = [];
let uploadedFilesPath = [];
mockFiles.forEach((fileName, index) => {
let filePath = path.join(fileDir, fileName);
let fileStat = fs.statSync(filePath);
uploadedFilesPath.push(path.join(uploadDir, fileName));
expectedResult.push({
name:fileName,
md5: md5(fs.readFileSync(filePath)),
size: fileStat.size,
uploadDir: '',
uploadPath: ''
});
req.attach(`testFile${index+1}`, filePath);
});
req
.expect((res)=>{
res.body.forEach(fileInfo => {
fileInfo.uploadDir = '';
fileInfo.uploadPath = '';
let index = mockFiles.indexOf(fileInfo.name);
expectedResultSorted.push(expectedResult[index]);
});
})
.expect(200, expectedResultSorted)
.end(function(err) {
if (err) {
return done(err);
}
fs.stat(upload1, function(err) {
fs.stat(uploadedFilesPath[0], function(err) {
if (err) {
return done(err);
}
fs.stat(upload2, function(err) {
fs.stat(uploadedFilesPath[1], function(err) {
if (err) {
return done(err);
}
fs.stat(upload3, done);
fs.stat(uploadedFilesPath[2], done);
});
});
});
@ -215,20 +287,41 @@ describe('Test File Array Upload', function() {
clearUploadsDir();
for (let i = 0; i < mockFiles.length; i++) {
req.attach('testFiles', path.join(fileDir, mockFiles[i]));
}
let expectedResult = [];
let expectedResultSorted = [];
let uploadedFilesPath = [];
mockFiles.forEach((fileName) => {
let filePath = path.join(fileDir, fileName);
let fileStat = fs.statSync(filePath);
uploadedFilesPath.push(path.join(uploadDir, fileName));
expectedResult.push({
name:fileName,
md5: md5(fs.readFileSync(filePath)),
size: fileStat.size,
uploadDir: '',
uploadPath: ''
});
req.attach('testFiles', filePath);
});
req
.expect(200)
.expect((res)=>{
res.body.forEach(fileInfo => {
fileInfo.uploadDir = '';
fileInfo.uploadPath = '';
let index = mockFiles.indexOf(fileInfo.name);
expectedResultSorted.push(expectedResult[index]);
});
})
.expect(200, expectedResultSorted)
.end(function(err) {
if (err) {
return done(err);
}
for (let i = 0; i < mockFiles.length; i++) {
fs.statSync(path.join(uploadDir, mockFiles[i]));
}
uploadedFilesPath.forEach(function(uploadedFilePath){
fs.statSync(uploadedFilePath);
});
done();
});
@ -241,6 +334,9 @@ describe('Test Upload With Fields', function() {
it(`upload ${fileName} and submit fields at the same time with POST`, function(done) {
let filePath = path.join(fileDir, fileName);
let fileBuffer = fs.readFileSync(filePath);
let fileHash = md5(fileBuffer);
let fileStat = fs.statSync(filePath);
let uploadedFilePath = path.join(uploadDir, fileName);
clearUploadsDir();
@ -251,10 +347,19 @@ describe('Test Upload With Fields', function() {
.field('firstName', mockUser.firstName)
.field('lastName', mockUser.lastName)
.field('email', mockUser.email)
.expect((res)=>{
res.body.uploadDir = '';
res.body.uploadPath = '';
})
.expect(200, {
firstName: mockUser.firstName,
lastName: mockUser.lastName,
email: mockUser.email
email: mockUser.email,
name: fileName,
md5: fileHash,
size: fileStat.size,
uploadDir: '',
uploadPath: ''
},
function(err) {
if (err) {
@ -267,6 +372,10 @@ describe('Test Upload With Fields', function() {
it(`upload ${fileName} and submit fields at the same time with PUT`, function(done) {
let filePath = path.join(fileDir, fileName);
let fileBuffer = fs.readFileSync(filePath);
let fileStat = fs.statSync(filePath);
let fileHash = md5(fileBuffer);
let uploadedFilePath = path.join(uploadDir, fileName);
clearUploadsDir();
@ -277,10 +386,19 @@ describe('Test Upload With Fields', function() {
.field('firstName', mockUser.firstName)
.field('lastName', mockUser.lastName)
.field('email', mockUser.email)
.expect((res)=>{
res.body.uploadDir = '';
res.body.uploadPath = '';
})
.expect(200, {
firstName: mockUser.firstName,
lastName: mockUser.lastName,
email: mockUser.email
email: mockUser.email,
name: fileName,
md5: fileHash,
size: fileStat.size,
uploadDir: '',
uploadPath: ''
},
function(err) {
if (err) {

@ -38,7 +38,14 @@ const setup = function(fileUploadOptions) {
return res.status(500).send(err);
}
res.send('File uploaded to ' + uploadPath);
//res.send('File uploaded to ' + uploadPath);
res.json({
name: testFile.name,
uploadDir: uploadDir,
uploadPath: uploadPath,
md5: testFile.md5,
size: testFile.size
});
});
});
@ -53,7 +60,13 @@ const setup = function(fileUploadOptions) {
testFile
.mv(uploadPath)
.then(() => {
res.send('File uploaded to ' + uploadPath);
res.json({
name: testFile.name,
uploadDir: uploadDir,
uploadPath: uploadPath,
md5: testFile.md5,
size: testFile.size
});
})
.catch(err => {
res.status(500).send(err);
@ -92,7 +105,12 @@ const setup = function(fileUploadOptions) {
res.json({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email
email: req.body.email,
name: testFile.name,
uploadDir: uploadDir,
uploadPath: uploadPath,
md5: testFile.md5,
size: testFile.size
});
});
});
@ -149,7 +167,30 @@ const setup = function(fileUploadOptions) {
return res.status(500).send(err);
}
res.send('Files uploaded to ' + uploadDir);
//res.send('Files uploaded to ' + uploadDir);
res.json([
{
name: testFile1.name,
uploadDir: uploadDir,
uploadPath: uploadPath1,
md5: testFile1.md5,
size: testFile1.size
},
{
name: testFile2.name,
uploadDir: uploadDir,
uploadPath: uploadPath2,
md5: testFile2.md5,
size: testFile2.size
},
{
name: testFile2.name,
uploadDir: uploadDir,
uploadPath: uploadPath2,
md5: testFile2.md5,
size: testFile2.size
}
]);
});
});
});
@ -174,7 +215,7 @@ const setup = function(fileUploadOptions) {
return res.status(400).send('Files array is empty');
}
let filesUploaded = 0;
let uploadResults = [];
for (let i = 0; i < testFiles.length; i++) {
let uploadPath = path.join(uploadDir, testFiles[i].name);
@ -183,8 +224,16 @@ const setup = function(fileUploadOptions) {
return res.status(500).send(err);
}
if (++filesUploaded === testFiles.length) {
res.send('File uploaded to ' + uploadPath);
uploadResults.push({
name: testFiles[i].name,
uploadDir: uploadDir,
uploadPath: uploadPath,
md5: testFiles[i].md5,
size: testFiles[i].size
});
if (uploadResults.length === testFiles.length) {
res.json(uploadResults);
}
});
}

@ -1,4 +1,5 @@
const fs = require('fs');
const md5 = require('md5');
const path = require('path');
const request = require('supertest');
const server = require('./server');
@ -26,30 +27,35 @@ describe('File Upload Options Tests', function() {
expectedFileNameOnFileSystem,
done
) {
let filePath = path.join(fileDir, actualFileNameToUpload);
let fileBuffer = fs.readFileSync(filePath);
let fileHash = md5(fileBuffer);
let fileStat = fs.statSync(filePath);
let uploadedFilePath = path.join(uploadDir, expectedFileNameOnFileSystem);
request(
server.setup(options)
)
.post('/upload/single')
.attach(
'testFile',
path.join(
fileDir,
actualFileNameToUpload
)
)
.expect(200)
.attach('testFile', filePath)
.expect((res)=>{
res.body.uploadDir = '';
res.body.uploadPath = '';
})
.expect(200, {
name: expectedFileNameOnFileSystem,
md5: fileHash,
size: fileStat.size,
uploadDir: '',
uploadPath: ''
})
.end(function(err) {
if (err) {
return done(err);
}
const uploadedFilePath = path.join(
uploadDir,
expectedFileNameOnFileSystem
);
fs.stat(
uploadedFilePath,
done
);
fs.stat(uploadedFilePath, done);
});
}
describe('Testing [safeFileNames with useTempFiles] option to ensure:', function() {

Loading…
Cancel
Save