Skip to content

Commit a8f6303

Browse files
committed
Filename can be a nested path
1 parent d7398c3 commit a8f6303

4 files changed

Lines changed: 32 additions & 15 deletions

File tree

Readme.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ someModule.stream(function(err, stdout, stderr) {
132132
var form = new FormData();
133133

134134
form.append('file', stdout, {
135-
filename: 'unicycle.jpg',
135+
filename: 'unicycle.jpg', // ... or:
136+
filepath: 'photos/toys/unicycle.jpg',
136137
contentType: 'image/jpg',
137138
knownLength: 19806
138139
});
@@ -144,6 +145,8 @@ someModule.stream(function(err, stdout, stderr) {
144145
});
145146
```
146147

148+
The `filepath` property overrides `filename` and may contain a relative path. This is typically used when uploading [multiple files from a directory](https://wicg.github.io/entries-api/#dom-htmlinputelement-webkitdirectory).
149+
147150
For edge cases, like POST request to URL with query string or to pass HTTP auth credentials, object can be passed to `form.submit()` as first parameter:
148151

149152
``` javascript

lib/form_data.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var fs = require('fs');
88
var mime = require('mime-types');
99
var asynckit = require('asynckit');
1010
var populate = require('./populate.js');
11+
var normalize = require('normalize-path');
1112

1213
// Public API
1314
module.exports = FormData;
@@ -209,20 +210,25 @@ FormData.prototype._multiPartHeader = function(field, value, options) {
209210

210211
FormData.prototype._getContentDisposition = function(value, options) {
211212

212-
var contentDisposition;
213-
214-
// custom filename takes precedence
215-
// fs- and request- streams have path property
216-
// formidable and the browser add a name property.
217-
var filename = options.filename || value.name || value.path;
213+
var filename
214+
, contentDisposition
215+
;
218216

219-
// or try http response
220-
if (!filename && value.readable && value.hasOwnProperty('httpVersion')) {
221-
filename = value.client._httpMessage.path;
217+
if (options.filepath) {
218+
// custom filepath for relative paths
219+
filename = normalize(options.filepath);
220+
} else if (options.filename || value.name || value.path) {
221+
// custom filename take precedence
222+
// formidable and the browser add a name property
223+
// fs- and request- streams have path property
224+
filename = path.basename(options.filename || value.name || value.path);
225+
} else if (value.readable && value.hasOwnProperty('httpVersion')) {
226+
// or try http response
227+
filename = path.basename(value.client._httpMessage.path);
222228
}
223229

224230
if (filename) {
225-
contentDisposition = 'filename="' + path.basename(filename) + '"';
231+
contentDisposition = 'filename="' + filename + '"';
226232
}
227233

228234
return contentDisposition;
@@ -248,9 +254,9 @@ FormData.prototype._getContentType = function(value, options) {
248254
contentType = value.headers['content-type'];
249255
}
250256

251-
// or guess it from the filename
252-
if (!contentType && options.filename) {
253-
contentType = mime.lookup(options.filename);
257+
// or guess it from the filepath or filename
258+
if (!contentType && (options.filepath || options.filename)) {
259+
contentType = mime.lookup(options.filepath || options.filename);
254260
}
255261

256262
// fallback to the default content type if `value` is not simple value

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
"dependencies": {
4040
"asynckit": "^0.4.0",
4141
"combined-stream": "^1.0.5",
42-
"mime-types": "^2.1.12"
42+
"mime-types": "^2.1.12",
43+
"normalize-path": "^2.1.1"
4344
},
4445
"devDependencies": {
4546
"browserify": "^13.1.1",

test/integration/test-custom-filename.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var assert = common.assert;
88
var mime = require('mime-types');
99
var http = require('http');
1010
var fs = require('fs');
11+
var normalize = require('normalize-path');
1112

1213
var FormData = require(common.dir.lib + '/form_data');
1314
var IncomingForm = require('formidable').IncomingForm;
@@ -35,6 +36,10 @@ var server = http.createServer(function(req, res) {
3536
assert.strictEqual(files['custom_filename'].name, options.filename, 'Expects custom filename');
3637
assert.strictEqual(files['custom_filename'].type, mime.lookup(knownFile), 'Expects original content-type');
3738

39+
assert('custom_filepath' in files);
40+
assert.strictEqual(files['custom_filepath'].name, normalize(knownFile), 'Expects custom filename');
41+
assert.strictEqual(files['custom_filepath'].type, mime.lookup(knownFile), 'Expects original content-type');
42+
3843
assert('unknown_with_filename' in files);
3944
assert.strictEqual(files['unknown_with_filename'].name, options.filename, 'Expects custom filename');
4045
assert.strictEqual(files['unknown_with_filename'].type, mime.lookup(options.filename), 'Expects filename-derived content-type');
@@ -67,6 +72,8 @@ server.listen(common.port, function() {
6772
form.append('unknown_with_filename', fs.createReadStream(unknownFile), options.filename);
6873
// Filename only with unknown file
6974
form.append('unknown_with_filename_as_object', fs.createReadStream(unknownFile), {filename: options.filename});
75+
// Filename with nested path
76+
form.append('custom_filepath', fs.createReadStream(knownFile), {filepath: knownFile});
7077
// No options or implicit file type from extension on name property.
7178
var customNameStream = fs.createReadStream(unknownFile);
7279
customNameStream.name = options.filename;

0 commit comments

Comments
 (0)