Skip to content

Commit dec6b12

Browse files
authored
fix: export WebFormData (#559)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced `WebFormData` as an alternative export for `FormData`, enhancing compatibility and clarity. - Added a new `BufferStream` class to manage data chunk buffering efficiently. - **Tests** - Implemented a new test suite for validating data posting using `BufferStream` with `WebFormData`. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent d2b64c1 commit dec6b12

3 files changed

Lines changed: 116 additions & 0 deletions

File tree

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export {
6868
} from './IncomingHttpHeaders.js';
6969
export * from './HttpClientError.js';
7070
export { FetchFactory, fetch } from './fetch.js';
71+
export { FormData as WebFormData } from './FormData.js';
7172

7273
export default {
7374
request,

test/fixtures/BufferStream.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Transform } from 'node:stream';
2+
3+
const BUF_SIZE = 1024 * 1024;
4+
5+
export class BufferStream extends Transform {
6+
private buf: Buffer;
7+
private offset: number;
8+
9+
constructor(options?: any) {
10+
super(options);
11+
this.realloc();
12+
}
13+
14+
realloc() {
15+
this.buf = Buffer.alloc(BUF_SIZE);
16+
this.offset = 0;
17+
}
18+
19+
_transform(chunk: Buffer, _: any, callback: any) {
20+
const currentLength = this.offset;
21+
const chunkSize = chunk.length;
22+
const newSize = currentLength + chunkSize;
23+
// 缓冲区未满
24+
// - 向缓冲区写入
25+
if (newSize < BUF_SIZE) {
26+
chunk.copy(this.buf, currentLength);
27+
this.offset += chunkSize;
28+
return callback();
29+
}
30+
31+
// 缓冲区正好满
32+
// - 拷贝到缓冲区以后, 将 chunk 返回
33+
// - 刷新缓冲区
34+
if (newSize === BUF_SIZE) {
35+
chunk.copy(this.buf, currentLength);
36+
const writeChunk = this.buf;
37+
this.realloc();
38+
return callback(null, writeChunk);
39+
}
40+
41+
// 超过缓冲区大小
42+
// - 拷贝到缓冲区以后, 将 chunk 返回
43+
// - 刷新缓冲区
44+
// - 将超出的部分拷贝到新的缓冲区中
45+
const copyLength = BUF_SIZE - currentLength;
46+
const remainLength = chunkSize - copyLength;
47+
chunk.copy(this.buf, currentLength, 0, copyLength);
48+
const writeChunk = this.buf;
49+
this.push(writeChunk);
50+
this.realloc();
51+
52+
if (remainLength > BUF_SIZE) {
53+
// 特殊情况: 给了一个超大 chunk
54+
// 直接将这个 chunk 返回,没必要缓冲了
55+
this.push(chunk.slice(copyLength));
56+
} else {
57+
chunk.copy(this.buf, 0, copyLength);
58+
this.offset = remainLength;
59+
}
60+
return callback(null);
61+
}
62+
63+
_flush(callback: any) {
64+
if (this.offset) {
65+
const chunk = Buffer.alloc(this.offset);
66+
this.buf.copy(chunk);
67+
this.push(chunk);
68+
this.offset = 0;
69+
}
70+
callback();
71+
}
72+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { strict as assert } from 'node:assert';
2+
import { createReadStream } from 'node:fs';
3+
import { basename } from 'node:path';
4+
import { describe, it, beforeAll, afterAll } from 'vitest';
5+
import { HttpClient, WebFormData } from '../src/index.js';
6+
import { startServer } from './fixtures/server.js';
7+
import { BufferStream } from './fixtures/BufferStream.js';
8+
9+
describe('formData-with-BufferStream.test.ts', () => {
10+
let close: any;
11+
let _url: string;
12+
beforeAll(async () => {
13+
const { closeServer, url } = await startServer();
14+
close = closeServer;
15+
_url = url;
16+
});
17+
18+
afterAll(async () => {
19+
await close();
20+
});
21+
22+
it('should post with BufferStream', async () => {
23+
const fileStream = createReadStream(__filename);
24+
const bufferStream = new BufferStream();
25+
fileStream.pipe(bufferStream);
26+
const formData = new WebFormData();
27+
const fileName = basename(__filename);
28+
formData.append('fileBufferStream', bufferStream, fileName);
29+
formData.append('foo', 'bar');
30+
31+
const httpClient = new HttpClient();
32+
const response = await httpClient.request(`${_url}multipart`, {
33+
method: 'POST',
34+
content: formData,
35+
headers: formData.getHeaders(),
36+
dataType: 'json',
37+
});
38+
assert.equal(response.status, 200);
39+
// console.log(response.data);
40+
assert.equal(response.data.files.fileBufferStream.filename, 'formData-with-BufferStream.test.ts');
41+
assert.deepEqual(response.data.form, { foo: 'bar' });
42+
});
43+
});

0 commit comments

Comments
 (0)