express-fileupload
Advanced tools
Comparing version 1.0.0 to 1.1.1-alpha.1
@@ -16,2 +16,3 @@ # express-fileupload Examples | ||
return res.status(400).send('No files were uploaded.'); | ||
} | ||
@@ -18,0 +19,0 @@ // The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file |
const express = require('express'); | ||
const fileUpload = require('../lib/index.js'); | ||
const fileUpload = require('../lib/index'); | ||
const app = express(); | ||
const PORT = 8000; | ||
app.use('/form', express.static(__dirname + '/index.html')); | ||
@@ -38,4 +39,4 @@ | ||
app.listen(8000, function() { | ||
console.log('Express server listening on port 8000'); // eslint-disable-line | ||
app.listen(PORT, function() { | ||
console.log('Express server listening on port ', PORT); // eslint-disable-line | ||
}); |
@@ -9,6 +9,7 @@ 'use strict'; | ||
module.exports = function(options, fileUploadOptions = null) { | ||
return { | ||
const output = { | ||
name: options.name, | ||
data: options.buffer, | ||
encoding: options.encoding, | ||
tempFilePath: options.tempFilePath, | ||
truncated: options.truncated, | ||
@@ -20,18 +21,42 @@ mimetype: options.mimetype, | ||
if (callback) { | ||
doMove( | ||
() => { | ||
callback(null); | ||
}, | ||
(error) => { | ||
callback(error); | ||
} | ||
); | ||
if (options.buffer.length && !options.tempFilePath) { | ||
moveFromBuffer( | ||
() => { | ||
callback(null); | ||
}, | ||
error => { | ||
callback(error); | ||
} | ||
); | ||
} else { | ||
moveFromTemp( | ||
() => { | ||
callback(null); | ||
}, | ||
error => { | ||
callback(error); | ||
} | ||
); | ||
} | ||
// Otherwise, return a promise | ||
// Otherwise, return a promise | ||
} else { | ||
return new Promise((resolve, reject) => { | ||
doMove(resolve, reject); | ||
if (options.buffer) { | ||
moveFromBuffer(resolve, reject); | ||
} else { | ||
moveFromTemp(resolve, reject); | ||
} | ||
}); | ||
} | ||
function checkAndMakeDir(){ | ||
if (fileUploadOptions && fileUploadOptions.createParentPath) { | ||
const parentPath = path.dirname(filePath); | ||
if (!fs.existsSync(parentPath)) { | ||
fs.mkdirSync(parentPath); | ||
} | ||
} | ||
} | ||
/** | ||
@@ -43,10 +68,16 @@ * Local function that moves the file to a different location on the filesystem | ||
*/ | ||
function doMove(successFunc, errorFunc) { | ||
if (fileUploadOptions && fileUploadOptions.createParentPath) { | ||
const parentPath = path.dirname(filePath); | ||
if (!fs.existsSync(parentPath)) { | ||
fs.mkdirSync(parentPath); | ||
function moveFromTemp(successFunc, errorFunc) { | ||
checkAndMakeDir(); | ||
fs.rename(options.tempFilePath, filePath, function(err){ | ||
if (err) { | ||
errorFunc(err); | ||
} else { | ||
successFunc(); | ||
} | ||
} | ||
}); | ||
} | ||
function moveFromBuffer(successFunc, errorFunc) { | ||
checkAndMakeDir(); | ||
const fstream = fs.createWriteStream(filePath); | ||
@@ -66,2 +97,8 @@ | ||
}; | ||
if (options.size) { | ||
output.size = options.size; | ||
} | ||
return output; | ||
}; |
@@ -6,2 +6,3 @@ 'use strict'; | ||
const isEligibleRequest = require('./isEligibleRequest'); | ||
const processNested = require('./processNested'); | ||
@@ -12,3 +13,6 @@ const fileUploadOptionsDefaults = { | ||
abortOnLimit: false, | ||
createParentPath: false | ||
createParentPath: false, | ||
parseNested: false, | ||
useTempFiles: false, | ||
tempFileDir: '/tmp' | ||
}; | ||
@@ -32,4 +36,5 @@ | ||
/** | ||
* Quietly expose fileFactory; useful for testing | ||
* Quietly expose fileFactory and processNested; useful for testing | ||
*/ | ||
module.exports.fileFactory = fileFactory; | ||
module.exports.processNested = processNested; |
const Busboy = require('busboy'); | ||
const fileFactory = require('./fileFactory'); | ||
const { | ||
getTempFilePath, | ||
complete, | ||
cleanupStream, | ||
tempFileHandler | ||
} = require('./tempFileHandler'); | ||
const processNested = require('./processNested'); | ||
@@ -40,3 +47,3 @@ /** | ||
if (!prev) { | ||
return req.body[fieldname] = val; | ||
return (req.body[fieldname] = val); | ||
} | ||
@@ -55,6 +62,15 @@ | ||
let safeFileNameRegex = /[^\w-]/g; | ||
const memHandler = function(data) { | ||
buffers.push(data); | ||
if (options.debug) { | ||
return console.log('Uploading %s -> %s', fieldname, filename); // eslint-disable-line | ||
} | ||
}; | ||
const dataHandler = options.useTempFiles | ||
? tempFileHandler(options, fieldname, filename) | ||
: memHandler; | ||
file.on('limit', () => { | ||
if (options.abortOnLimit) { | ||
res.writeHead(413, {'Connection': 'close'}); | ||
res.writeHead(413, { Connection: 'close' }); | ||
res.end('File size limit has been reached'); | ||
@@ -64,10 +80,4 @@ } | ||
file.on('data', function(data) { | ||
buffers.push(data); | ||
file.on('data', dataHandler); | ||
if (options.debug) { | ||
return console.log('Uploading %s -> %s', fieldname, filename); // eslint-disable-line | ||
} | ||
}); | ||
file.on('end', function() { | ||
@@ -77,8 +87,11 @@ if (!req.files) { | ||
} | ||
if (options.useTempFiles) { | ||
complete(filename); | ||
} | ||
const buf = Buffer.concat(buffers); | ||
const buffer = Buffer.concat(buffers); | ||
// see: https://github.com/richardgirges/express-fileupload/issues/14 | ||
// firefox uploads empty file in case of cache miss when f5ing page. | ||
// resulting in unexpected behavior. if there is no file data, the file is invalid. | ||
if (!buf.length) { | ||
if (!buffer.length && !options.useTempFiles) { | ||
return; | ||
@@ -108,9 +121,15 @@ } | ||
if (extension.length > maxExtensionLength && maxExtensionLength > 0) { | ||
if ( | ||
extension.length > maxExtensionLength && | ||
maxExtensionLength > 0 | ||
) { | ||
filenameParts[filenameParts.length - 1] += | ||
'.' + extension.substr(0, extension.length - maxExtensionLength); | ||
'.' + | ||
extension.substr(0, extension.length - maxExtensionLength); | ||
extension = extension.substr(-maxExtensionLength); | ||
} | ||
extension = maxExtensionLength ? '.' + extension.replace(safeFileNameRegex, '') : ''; | ||
extension = maxExtensionLength | ||
? '.' + extension.replace(safeFileNameRegex, '') | ||
: ''; | ||
filename = filenameParts.join('.'); | ||
@@ -123,9 +142,13 @@ } | ||
const newFile = fileFactory({ | ||
name: filename, | ||
buffer: buf, | ||
encoding: encoding, | ||
truncated: file.truncated, | ||
mimetype: mime | ||
}, options); | ||
const newFile = fileFactory( | ||
{ | ||
name: filename, | ||
buffer, | ||
tempFilePath: getTempFilePath(), | ||
encoding, | ||
truncated: file.truncated, | ||
mimetype: mime | ||
}, | ||
options | ||
); | ||
@@ -145,6 +168,12 @@ // Non-array fields | ||
file.on('error', next); | ||
file.on('error', cleanupStream, next); | ||
}); | ||
busboy.on('finish', next); | ||
busboy.on('finish', () => { | ||
if (options.parseNested) { | ||
req.body = processNested(req.body); | ||
req.files = processNested(req.files); | ||
} | ||
next(); | ||
}); | ||
@@ -154,2 +183,2 @@ busboy.on('error', next); | ||
req.pipe(busboy); | ||
}; | ||
}; |
{ | ||
"name": "express-fileupload", | ||
"version": "1.0.0", | ||
"version": "1.1.1-alpha.1", | ||
"author": "Richard Girges <richardgirges@gmail.com>", | ||
@@ -35,3 +35,3 @@ "description": "Simple express file upload middleware that wraps around Busboy", | ||
"coveralls": "^3.0.2", | ||
"eslint": "^5.6.0", | ||
"eslint": "^5.9.0", | ||
"express": "^4.16.3", | ||
@@ -38,0 +38,0 @@ "istanbul": "^0.4.5", |
@@ -56,2 +56,12 @@ # express-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, | ||
tempFileDir : '/tmp/' | ||
})); | ||
``` | ||
### Available Options | ||
@@ -66,7 +76,10 @@ Pass in non-Busboy options directly to the middleware. These are express-fileupload specific options. | ||
abortOnLimit | <ul><li><code>false</code> **(default)**</li><li><code>true</code></ul> | Returns a HTTP 413 when the file is bigger than the size limit if true. Otherwise, it will add a <code>truncate = true</code> to the resulting file structure. | ||
useTempFiles | <ul><li><code>false</code> **(default)**</li><li><code>true</code></ul> | Will use temporary files at the specified tempDir for managing uploads rather than using buffers in memory. This avoids memory issues when uploading large files. | ||
tempFileDir | <ul><li><code>String</code> **(path)**</li></ul> | Used with the <code>useTempFiles</code> option. Path to the directory where temp files will be stored during the upload process. Add trailing slash. | ||
parseNested | <ul><li><code>false</code> **(default)**</li><li><code>true</code></li></ul> | By default, req.body and req.files are flattened like this: <code>{'name': 'John', 'hobbies[0]': 'Cinema', 'hobbies[1]': 'Bike'}</code><br /><br/>When this option is enabled they are parsed in order to be nested like this: <code>{'name': 'John', 'hobbies': ['Cinema', 'Bike']}</code> | ||
# Help Wanted | ||
Pull Requests are welcomed! | ||
Looking for additional maintainers. Please contact `richardgirges [ at ] gmail.com` if you're interested. Pull Requests are welcomed! | ||
# Thanks & Credit | ||
[Brian White](https://github.com/mscdex) for his stellar work on the [Busboy Package](https://github.com/mscdex/busboy) and the [connect-busboy Package](https://github.com/mscdex/connect-busboy) |
@@ -188,2 +188,33 @@ const fs = require('fs'); | ||
}); | ||
describe('Testing [parseNested] option to ensure:', function() { | ||
it('When [parseNested] is enabled result are nested', function(done){ | ||
const app = server.setup({parseNested: true}); | ||
request(app) | ||
.post('/fields/nested') | ||
.field('name', 'John') | ||
.field('hobbies[0]', 'Cinema') | ||
.field('hobbies[1]', 'Bike') | ||
.expect('Content-Type', /json/) | ||
.expect(200, { | ||
name: 'John', | ||
hobbies: ['Cinema', 'Bike'] | ||
}, done); | ||
}); | ||
it('When [parseNested] is disabled are flattened', function(done){ | ||
const app = server.setup({parseNested: false}); | ||
request(app) | ||
.post('/fields/flattened') | ||
.field('name', 'John') | ||
.field('hobbies[0]', 'Cinema') | ||
.field('hobbies[1]', 'Bike') | ||
.expect('Content-Type', /json/) | ||
.expect(200, { | ||
name: 'John', | ||
'hobbies[0]': 'Cinema', | ||
'hobbies[1]': 'Bike' | ||
}, done); | ||
}); | ||
}); | ||
}); |
@@ -53,7 +53,8 @@ 'use strict'; | ||
testFile.mv(uploadPath) | ||
testFile | ||
.mv(uploadPath) | ||
.then(() => { | ||
res.send('File uploaded to ' + uploadPath); | ||
}) | ||
.catch((err) => { | ||
.catch(err => { | ||
res.status(500).send(err); | ||
@@ -217,2 +218,45 @@ }); | ||
app.all('/fields/nested', function(req, res) { | ||
if (!req.body) { | ||
return res.status(400).send('No request body found'); | ||
} | ||
if (!req.body.name || !req.body.name.trim()) { | ||
return res.status(400).send('Invalid name'); | ||
} | ||
if (!req.body.hobbies || !req.body.hobbies.length == 2) { | ||
return res.status(400).send('Invalid hobbies'); | ||
} | ||
res.json({ | ||
name: req.body.name, | ||
hobbies: req.body.hobbies | ||
}); | ||
}); | ||
app.all('/fields/flattened', function(req, res) { | ||
if (!req.body) { | ||
return res.status(400).send('No request body found'); | ||
} | ||
if (!req.body.name || !req.body.name.trim()) { | ||
return res.status(400).send('Invalid name'); | ||
} | ||
if (!req.body['hobbies[0]'] || !req.body['hobbies[0]'].trim()) { | ||
return res.status(400).send('Invalid hobbies[0]'); | ||
} | ||
if (!req.body['hobbies[1]'] || !req.body['hobbies[1]'].trim()) { | ||
return res.status(400).send('Invalid hobbies[1]'); | ||
} | ||
res.json({ | ||
name: req.body.name, | ||
'hobbies[0]': req.body['hobbies[0]'], | ||
'hobbies[1]': req.body['hobbies[1]'] | ||
}); | ||
}); | ||
app.all('/fields/array', function(req, res) { | ||
@@ -219,0 +263,0 @@ if (!req.body) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1175261
30
1405
84
2
8