Skip to content

Commit c7a6b9c

Browse files
Merge pull request #233 from RomanBurunkov/master
Support empty files
2 parents b6097df + a53b93f commit c7a6b9c

8 files changed

+51
-59
lines changed

lib/fileFactory.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ module.exports = (options, fileUploadOptions = {}) => {
3939
// see: https://github.com/richardgirges/express-fileupload/issues/14
4040
// firefox uploads empty file in case of cache miss when f5ing page.
4141
// resulting in unexpected behavior. if there is no file data, the file is invalid.
42-
if (!fileUploadOptions.useTempFiles && !options.buffer.length) return;
42+
// if (!fileUploadOptions.useTempFiles && !options.buffer.length) return;
4343

4444
// Create and return file object.
4545
return {

lib/processMultipart.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ module.exports = (options, req, res, next) => {
5959
: memHandler(options, field, filename); // Upload into RAM.
6060
// Define upload timer.
6161
const uploadTimer = new UploadTimer(options.uploadTimeout, () => {
62+
file.removeAllListeners('data');
63+
file.resume();
6264
// After destroy an error event will be emitted and file clean up will be done.
6365
file.destroy(new Error(`Upload timeout ${field}->${filename}, bytes:${getFileSize()}`));
6466
});
@@ -88,9 +90,12 @@ module.exports = (options, req, res, next) => {
8890
debugLog(options, `Upload finished ${field}->${filename}, bytes:${size}`);
8991
// Reset upload timer in case of end event.
9092
uploadTimer.clear();
93+
// See https://github.com/richardgirges/express-fileupload/issues/191
9194
// Do not add file instance to the req.files if original name and size are empty.
9295
// Empty name and zero size indicates empty file field in the posted form.
93-
if (!name && size === 0) return;
96+
if (!name && size === 0) {
97+
return debugLog(options, `Don't add file instance if original name and size are empty`);
98+
}
9499
req.files = buildFields(req.files, field, fileFactory({
95100
buffer: complete(),
96101
name: filename,
@@ -122,6 +127,7 @@ module.exports = (options, req, res, next) => {
122127
});
123128

124129
busboy.on('finish', () => {
130+
debugLog(options, `Busboy finished parsing request.`);
125131
if (options.parseNested) {
126132
req.body = processNested(req.body);
127133
req.files = processNested(req.files);
@@ -139,7 +145,10 @@ module.exports = (options, req, res, next) => {
139145
});
140146
});
141147

142-
busboy.on('error', next);
148+
busboy.on('error', (err) => {
149+
debugLog(options, `Busboy error`);
150+
next(err);
151+
});
143152

144153
req.pipe(busboy);
145154
};

lib/tempFileHandler.js

+14-24
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,23 @@ module.exports = (options, fieldname, filename) => {
1818
const hash = crypto.createHash('md5');
1919
let fileSize = 0;
2020
let completed = false;
21-
22-
let writeStream = false;
23-
let writePromise = Promise.resolve();
2421

25-
const createWriteStream = () => {
26-
debugLog(options, `Opening write stream for ${fieldname}->${filename}...`);
27-
writeStream = fs.createWriteStream(tempFilePath);
28-
writePromise = new Promise((resolve, reject) => {
29-
writeStream.on('finish', () => {
30-
resolve();
31-
});
32-
writeStream.on('error', (err) => {
33-
debugLog(options, `Error write temp file: ${err}`);
34-
reject(err);
35-
});
22+
debugLog(options, `Opening write stream for ${fieldname}->${filename}...`);
23+
const writeStream = fs.createWriteStream(tempFilePath);
24+
const writePromise = new Promise((resolve, reject) => {
25+
writeStream.on('finish', () => resolve());
26+
writeStream.on('error', (err) => {
27+
debugLog(options, `Error write temp file: ${err}`);
28+
reject(err);
3629
});
37-
};
30+
});
3831

3932
return {
4033
dataHandler: (data) => {
4134
if (completed === true) {
4235
debugLog(options, `Error: got ${fieldname}->${filename} data chunk for completed upload!`);
4336
return;
4437
}
45-
if (writeStream === false) createWriteStream();
4638
writeStream.write(data);
4739
hash.update(data);
4840
fileSize += data.length;
@@ -60,14 +52,12 @@ module.exports = (options, fieldname, filename) => {
6052
},
6153
cleanup: () => {
6254
completed = true;
63-
if (writeStream !== false) {
64-
debugLog(options, `Cleaning up temporary file ${tempFilePath}...`);
65-
writeStream.end();
66-
deleteFile(tempFilePath, err => (err
67-
? debugLog(options, `Cleaning up temporary file ${tempFilePath} failed: ${err}`)
68-
: debugLog(options, `Cleaning up temporary file ${tempFilePath} done.`)
69-
));
70-
}
55+
debugLog(options, `Cleaning up temporary file ${tempFilePath}...`);
56+
writeStream.end();
57+
deleteFile(tempFilePath, err => (err
58+
? debugLog(options, `Cleaning up temporary file ${tempFilePath} failed: ${err}`)
59+
: debugLog(options, `Cleaning up temporary file ${tempFilePath} done.`)
60+
));
7161
},
7262
getWritePromise: () => writePromise
7363
};

lib/utilities.js

+23-22
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const fs = require('fs');
44
const path = require('path');
5-
const Readable = require('stream').Readable;
5+
const { Readable } = require('stream');
66

77
// Parameters for safe file name parsing.
88
const SAFE_FILE_NAME_REGEX = /[^\w-]/g;
@@ -27,17 +27,17 @@ const debugLog = (options, msg) => {
2727
};
2828

2929
/**
30-
* Generates unique temporary file name like: tmp-5000-156788789789.
30+
* Generates unique temporary file name. e.g. tmp-5000-156788789789.
3131
* @param {string} prefix - a prefix for generated unique file name.
3232
* @returns {string}
3333
*/
34-
const getTempFilename = (prefix) => {
34+
const getTempFilename = (prefix = TEMP_PREFIX) => {
3535
tempCounter = tempCounter >= TEMP_COUNTER_MAX ? 1 : tempCounter + 1;
36-
return `${prefix || TEMP_PREFIX}-${tempCounter}-${Date.now()}`;
36+
return `${prefix}-${tempCounter}-${Date.now()}`;
3737
};
3838

3939
/**
40-
* isFunc- check if argument is a function.
40+
* isFunc: Checks if argument is a function.
4141
* @returns {boolean} - Returns true if argument is a function.
4242
*/
4343
const isFunc = func => func && func.constructor && func.call && func.apply ? true: false;
@@ -60,7 +60,7 @@ const promiseCallback = (resolve, reject) => {
6060
* Builds instance options from arguments objects(can't be arrow function).
6161
* @returns {Object} - result options.
6262
*/
63-
const buildOptions = function(){
63+
const buildOptions = function() {
6464
const result = {};
6565
[...arguments].forEach(options => {
6666
if (!options || typeof options !== 'object') return;
@@ -107,17 +107,18 @@ const checkAndMakeDir = (fileUploadOptions, filePath) => {
107107
// Check whether folder for the file exists.
108108
if (!filePath) return false;
109109
const parentPath = path.dirname(filePath);
110-
// Create folder if it is not exists.
110+
// Create folder if it doesn't exist.
111111
if (!fs.existsSync(parentPath)) fs.mkdirSync(parentPath, { recursive: true });
112112
// Checks folder again and return a results.
113113
return fs.existsSync(parentPath);
114114
};
115115

116116
/**
117-
* Delete file.
117+
* Deletes a file.
118118
* @param {string} file - Path to the file to delete.
119+
* @param {Function} callback
119120
*/
120-
const deleteFile = (file, callback) => fs.unlink(file, err => err ? callback(err) : callback());
121+
const deleteFile = (file, callback) => fs.unlink(file, callback);
121122

122123
/**
123124
* Copy file via streams
@@ -147,15 +148,15 @@ const copyFile = (src, dst, callback) => {
147148
};
148149

149150
/**
150-
* moveFile - moves the file from src to dst.
151+
* moveFile: moves the file from src to dst.
151152
* Firstly trying to rename the file if no luck copying it to dst and then deleteing src.
152153
* @param {string} src - Path to the source file
153154
* @param {string} dst - Path to the destination file.
154155
* @param {Function} callback - A callback function.
155156
*/
156-
const moveFile = (src, dst, callback) => fs.rename(src, dst, err => (!err
157-
? callback()
158-
: copyFile(src, dst, err => err ? callback(err) : deleteFile(src, callback))
157+
const moveFile = (src, dst, callback) => fs.rename(src, dst, err => (err
158+
? copyFile(src, dst, err => err ? callback(err) : deleteFile(src, callback))
159+
: callback()
159160
));
160161

161162
/**
@@ -176,7 +177,7 @@ const saveBufferToFile = (buffer, filePath, callback) => {
176177
};
177178
// Setup file system writable stream.
178179
let fstream = fs.createWriteStream(filePath);
179-
fstream.on('error', error => callback(error));
180+
fstream.on('error', err => callback(err));
180181
fstream.on('close', () => callback());
181182
// Copy file via piping streams.
182183
readStream.pipe(fstream);
@@ -252,18 +253,18 @@ const parseFileName = (opts, fileName) => {
252253
};
253254

254255
module.exports = {
255-
debugLog,
256256
isFunc,
257+
debugLog,
258+
copyFile, // For testing purpose.
259+
moveFile,
257260
errorFunc,
258-
promiseCallback,
259-
buildOptions,
261+
deleteFile, // For testing purpose.
260262
buildFields,
261-
checkAndMakeDir,
262-
deleteFile, // For testing purpose.
263-
copyFile, // For testing purpose.
264-
moveFile,
265-
saveBufferToFile,
263+
buildOptions,
266264
parseFileName,
267265
getTempFilename,
266+
promiseCallback,
267+
checkAndMakeDir,
268+
saveBufferToFile,
268269
uriDecodeFileName
269270
};

test/fileFactory.spec.js

-8
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@ describe('Test of the fileFactory factory', function() {
2626
beforeEach(() => server.clearUploadsDir());
2727

2828
it('return a file object', () => assert.ok(fileFactory(mockFileOpts)));
29-
it('return void if buffer is empty and useTempFiles is false.', () => {
30-
assert.equal(fileFactory({
31-
name: mockFileName,
32-
buffer: Buffer.concat([])
33-
}, {
34-
useTempFiles: false
35-
}), null);
36-
});
3729

3830
describe('Properties', function() {
3931
it('contains the name property', () => {

test/files/emptyfile.txt

Whitespace-only changes.

test/multipartUploads.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const uploadDir = server.uploadDir;
1212
const clearTempDir = server.clearTempDir;
1313
const clearUploadsDir = server.clearUploadsDir;
1414

15-
const mockFiles = ['car.png', 'tree.png', 'basketball.png'];
15+
const mockFiles = ['car.png', 'tree.png', 'basketball.png', 'emptyfile.txt'];
1616

1717
const mockUser = {
1818
firstName: 'Joe',

test/server.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const setup = (fileUploadOptions) => {
4040
const testFile = req.files.testFile;
4141
const fileData = getUploadedFileData(testFile);
4242

43-
testFile.mv(fileData.uploadPath, function(err) {
43+
testFile.mv(fileData.uploadPath, (err) => {
4444
if (err) {
4545
console.log('ERR', err); // eslint-disable-line
4646
return res.status(500).send(err);

0 commit comments

Comments
 (0)