Skip to content

Commit

Permalink
fix: export WebFormData (#559)
Browse files Browse the repository at this point in the history
<!-- 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 -->
  • Loading branch information
fengmk2 authored Dec 6, 2024
1 parent d2b64c1 commit dec6b12
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export {
} from './IncomingHttpHeaders.js';
export * from './HttpClientError.js';
export { FetchFactory, fetch } from './fetch.js';
export { FormData as WebFormData } from './FormData.js';

export default {
request,
Expand Down
72 changes: 72 additions & 0 deletions test/fixtures/BufferStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Transform } from 'node:stream';

const BUF_SIZE = 1024 * 1024;

export class BufferStream extends Transform {
private buf: Buffer;
private offset: number;

constructor(options?: any) {
super(options);
this.realloc();
}

realloc() {
this.buf = Buffer.alloc(BUF_SIZE);
this.offset = 0;
}

_transform(chunk: Buffer, _: any, callback: any) {
const currentLength = this.offset;
const chunkSize = chunk.length;
const newSize = currentLength + chunkSize;
// 缓冲区未满
// - 向缓冲区写入
if (newSize < BUF_SIZE) {
chunk.copy(this.buf, currentLength);
this.offset += chunkSize;
return callback();
}

// 缓冲区正好满
// - 拷贝到缓冲区以后, 将 chunk 返回
// - 刷新缓冲区
if (newSize === BUF_SIZE) {
chunk.copy(this.buf, currentLength);
const writeChunk = this.buf;
this.realloc();
return callback(null, writeChunk);
}

// 超过缓冲区大小
// - 拷贝到缓冲区以后, 将 chunk 返回
// - 刷新缓冲区
// - 将超出的部分拷贝到新的缓冲区中
const copyLength = BUF_SIZE - currentLength;
const remainLength = chunkSize - copyLength;
chunk.copy(this.buf, currentLength, 0, copyLength);
const writeChunk = this.buf;
this.push(writeChunk);
this.realloc();

if (remainLength > BUF_SIZE) {
// 特殊情况: 给了一个超大 chunk
// 直接将这个 chunk 返回,没必要缓冲了
this.push(chunk.slice(copyLength));
} else {
chunk.copy(this.buf, 0, copyLength);
this.offset = remainLength;
}
return callback(null);
}

_flush(callback: any) {
if (this.offset) {
const chunk = Buffer.alloc(this.offset);
this.buf.copy(chunk);
this.push(chunk);
this.offset = 0;
}
callback();
}
}
43 changes: 43 additions & 0 deletions test/formData-with-BufferStream.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { strict as assert } from 'node:assert';
import { createReadStream } from 'node:fs';
import { basename } from 'node:path';
import { describe, it, beforeAll, afterAll } from 'vitest';
import { HttpClient, WebFormData } from '../src/index.js';
import { startServer } from './fixtures/server.js';
import { BufferStream } from './fixtures/BufferStream.js';

describe('formData-with-BufferStream.test.ts', () => {
let close: any;
let _url: string;
beforeAll(async () => {
const { closeServer, url } = await startServer();
close = closeServer;
_url = url;
});

afterAll(async () => {
await close();
});

it('should post with BufferStream', async () => {
const fileStream = createReadStream(__filename);
const bufferStream = new BufferStream();
fileStream.pipe(bufferStream);
const formData = new WebFormData();
const fileName = basename(__filename);
formData.append('fileBufferStream', bufferStream, fileName);
formData.append('foo', 'bar');

const httpClient = new HttpClient();
const response = await httpClient.request(`${_url}multipart`, {
method: 'POST',
content: formData,
headers: formData.getHeaders(),
dataType: 'json',
});
assert.equal(response.status, 200);
// console.log(response.data);
assert.equal(response.data.files.fileBufferStream.filename, 'formData-with-BufferStream.test.ts');
assert.deepEqual(response.data.form, { foo: 'bar' });
});
});

0 comments on commit dec6b12

Please sign in to comment.