Skip to content

Commit

Permalink
feat: support custom filename when file is Buffer or Readable (#508)
Browse files Browse the repository at this point in the history
> Uploading files with Buffer defaults the filename to the key or path,
which can conflict with middleware checks.

* 🤖 files as an object defaults filename to the key.
~~* 🚨 File paths or streams also override the filename.~~
* ♻️ only work for buffer scence.
----------

> 当直接通过 Buffer 来上传文件时,无法自定义文件名,这在某些中间件会校验 filename,导致无法上传。

* 🤖 files 参数为 object 时,默认将 key 作为文件名传输
~~* 🚨 传入文件路径或 Readable 流时也同样覆盖~~
* ♻️ 仅处理 buffer 场景


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features**
- Enhanced file upload functionality to support optional custom file
names, allowing users to specify filenames when uploading files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
elrrrrrrr committed May 7, 2024
1 parent bc21ec3 commit 032f439
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 6 deletions.
11 changes: 6 additions & 5 deletions src/HttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ export class HttpClient extends EventEmitter {
requestOptions.method = 'POST';
}
const formData = new FormData();
const uploadFiles: [string, string | Readable | Buffer][] = [];
const uploadFiles: [string, string | Readable | Buffer, string?][] = [];
if (Array.isArray(args.files)) {
for (const [ index, file ] of args.files.entries()) {
const field = index === 0 ? 'file' : `file${index}`;
Expand All @@ -452,7 +452,8 @@ export class HttpClient extends EventEmitter {
uploadFiles.push([ 'file', args.files ]);
} else if (typeof args.files === 'object') {
for (const field in args.files) {
uploadFiles.push([ field, args.files[field] ]);
// set custom fileName
uploadFiles.push([ field, args.files[field], field ]);
}
}
// set normal fields first
Expand All @@ -461,7 +462,7 @@ export class HttpClient extends EventEmitter {
formData.append(field, args.data[field]);
}
}
for (const [ index, [ field, file ]] of uploadFiles.entries()) {
for (const [ index, [ field, file, customFileName ]] of uploadFiles.entries()) {
if (typeof file === 'string') {
// FIXME: support non-ascii filename
// const fileName = encodeURIComponent(basename(file));
Expand All @@ -470,9 +471,9 @@ export class HttpClient extends EventEmitter {
const fileReadable = createReadStream(file);
formData.append(field, new BlobFromStream(fileReadable, mime.lookup(fileName) || ''), fileName);
} else if (Buffer.isBuffer(file)) {
formData.append(field, new Blob([ file ]), `bufferfile${index}`);
formData.append(field, new Blob([ file ]), customFileName || `bufferfile${index}`);
} else if (file instanceof Readable || isReadable(file as any)) {
const fileName = getFileName(file) || `streamfile${index}`;
const fileName = getFileName(file) || customFileName || `streamfile${index}`;
formData.append(field, new BlobFromStream(file, mime.lookup(fileName) || ''), fileName);
isStreamingRequest = true;
}
Expand Down
32 changes: 31 additions & 1 deletion test/options.files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ describe('options.files.test.ts', () => {
// console.log(response.data);
assert.equal(response.data.method, 'POST');
assert.match(response.data.headers['content-type'], /^multipart\/form-data;/);
assert.equal(response.data.files.hello.filename, 'bufferfile0');
assert.equal(response.data.files.hello.filename, 'hello');
assert.equal(response.data.files.hello.mimeType, 'application/octet-stream');
assert.equal(response.data.files.hello.encoding, '7bit');
assert.equal(response.data.files.hello.size, 11);
Expand Down Expand Up @@ -268,4 +268,34 @@ describe('options.files.test.ts', () => {
assert.equal(response.data.form.hello, 'hello world,😄😓');
assert.equal(response.data.form.foo, 'bar');
});

it('should support custom fileName when use files:object', async () => {
const rawData = JSON.stringify({ a: 1 });
const response = await urllib.request(`${_url}multipart`, {
files: {
'buffer.js': Buffer.from(rawData),
'readable.js': Readable.from([ rawData ]),
},
data: {
hello: 'hello world,😄😓',
foo: 'bar',
},
dataType: 'json',
});
assert.equal(response.status, 200);
// console.log(response.data);
assert.equal(response.data.method, 'POST');
assert.match(response.data.headers['content-type'], /^multipart\/form-data;/);

assert.equal(response.data.files['readable.js'].filename, 'readable.js');
// set mimeType by filename
assert.equal(response.data.files['readable.js'].mimeType, 'application/javascript');

assert.equal(response.data.files['buffer.js'].filename, 'buffer.js');
assert.equal(response.data.files['buffer.js'].mimeType, 'application/octet-stream');
assert.equal(response.data.files['buffer.js'].size, rawData.length);

assert.equal(response.data.form.hello, 'hello world,😄😓');
assert.equal(response.data.form.foo, 'bar');
});
});

0 comments on commit 032f439

Please sign in to comment.