Skip to content

Commit 6b7fed2

Browse files
committed
feat: re-introduce fileSize parameter, set Content-Length
1 parent e5e0b99 commit 6b7fed2

2 files changed

Lines changed: 75 additions & 42 deletions

File tree

lib/CloudConvert.ts

Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { statSync } from 'node:fs';
12
import { basename } from 'node:path';
23
import { Readable } from 'node:stream';
34
import { io, type Socket } from 'socket.io-client';
@@ -18,20 +19,78 @@ export type UploadFileSource =
1819
| AsyncIterable<Uint8Array>
1920
| NodeJS.ReadableStream;
2021

21-
function guessFilename(source: UploadFileSource): string | undefined {
22-
return 'path' in source && typeof source.path === 'string'
23-
? basename(source.path)
24-
: undefined;
22+
async function* unifySources(
23+
data: UploadFileSource
24+
): AsyncIterable<Uint8Array> {
25+
if (data instanceof Uint8Array) {
26+
yield data;
27+
return;
28+
}
29+
30+
if (data instanceof Blob) {
31+
yield data.bytes();
32+
return;
33+
}
34+
35+
if (Symbol.iterator in data) {
36+
yield* data;
37+
return;
38+
}
39+
40+
if (Symbol.asyncIterator in data) {
41+
for await (const chunk of data) {
42+
if (typeof chunk === 'string')
43+
throw new Error(
44+
'bad file data, received string but expected Uint8Array'
45+
);
46+
yield chunk;
47+
}
48+
return;
49+
}
50+
}
51+
52+
function guessNameAndSize(
53+
source: UploadFileSource,
54+
fileName?: string,
55+
fileSize?: number
56+
): { name: string; size: number } {
57+
const path =
58+
'path' in source && typeof source.path === 'string'
59+
? source.path
60+
: undefined;
61+
const name =
62+
fileName ?? (path !== undefined ? basename(path) : undefined) ?? 'file';
63+
const size =
64+
fileSize ??
65+
(source instanceof Uint8Array ? source.byteLength : undefined) ??
66+
(source instanceof Blob ? source.size : undefined) ??
67+
(path !== undefined
68+
? statSync(path, { throwIfNoEntry: false })?.size
69+
: undefined) ??
70+
(fileName !== undefined
71+
? statSync(fileName, { throwIfNoEntry: false })?.size
72+
: undefined);
73+
if (size === undefined) {
74+
throw new Error(
75+
'Could not determine the number of bytes, specify it explicitly when calling `upload`'
76+
);
77+
}
78+
return { name, size };
2579
}
2680

2781
export class UploadFile {
2882
private readonly attributes: Array<[key: string, value: unknown]> = [];
2983
private readonly data: AsyncIterable<Uint8Array>;
30-
constructor(
31-
data: UploadFileSource,
32-
private readonly filename = guessFilename(data)
33-
) {
34-
this.data = UploadFile.unifySources(data);
84+
private readonly filename?: string;
85+
private readonly fileSize: number;
86+
constructor(data: UploadFileSource, filename?: string, fileSize?: number) {
87+
this.data = unifySources(data);
88+
const { name, size } = guessNameAndSize(data, filename, fileSize);
89+
this.filename = name;
90+
this.fileSize = size;
91+
}
92+
byteCount() {
93+
return this.fileSize;
3594
}
3695
add(key: string, value: unknown) {
3796
this.attributes.push([key, value]);
@@ -60,36 +119,6 @@ export class UploadFile {
60119
// End multipart/form-data protocol
61120
yield enc.encode(`\r\n--${boundary}--\r\n`);
62121
}
63-
64-
static async *unifySources(
65-
data: UploadFileSource
66-
): AsyncIterable<Uint8Array> {
67-
if (data instanceof Uint8Array) {
68-
yield data;
69-
return;
70-
}
71-
72-
if (data instanceof Blob) {
73-
yield data.bytes();
74-
return;
75-
}
76-
77-
if (Symbol.iterator in data) {
78-
yield* data;
79-
return;
80-
}
81-
82-
if (Symbol.asyncIterator in data) {
83-
for await (const chunk of data) {
84-
if (typeof chunk === 'string')
85-
throw new Error(
86-
'bad file data, received string but expected Uint8Array'
87-
);
88-
yield chunk;
89-
}
90-
return;
91-
}
92-
}
93122
}
94123

95124
export default class CloudConvert {
@@ -148,7 +177,7 @@ export default class CloudConvert {
148177
const presigned = options?.presigned ?? false;
149178
const flat = options?.flat ?? false;
150179
const url = new URL(route, baseURL);
151-
const { contentType, search, body } = prepareParameters(
180+
const { contentLength, contentType, search, body } = prepareParameters(
152181
method,
153182
parameters
154183
);
@@ -158,6 +187,7 @@ export default class CloudConvert {
158187
const headers = {
159188
'User-Agent': `cloudconvert-node/v${version} (https://github.com/cloudconvert/cloudconvert-node)`,
160189
...(!presigned ? { Authorization: `Bearer ${this.apiKey}` } : {}),
190+
...(contentLength ? { 'Content-Length': contentLength } : {}),
161191
...(contentType ? { 'Content-Type': contentType } : {})
162192
};
163193
const res = await fetch(url, {
@@ -230,6 +260,7 @@ function prepareParameters(
230260
method: 'GET' | 'POST' | 'DELETE',
231261
data?: UploadFile | object
232262
): {
263+
contentLength?: string;
233264
contentType?: string;
234265
body?: string | ReadableStream<Uint8Array>;
235266
search?: string;
@@ -249,6 +280,7 @@ function prepareParameters(
249280
.map(() => Math.random().toString(36)[2] || 0)
250281
.join('')}`;
251282
return {
283+
contentLength: data.byteCount().toString(),
252284
contentType: `multipart/form-data; boundary=${boundary}`,
253285
body: asyncIterableToReadableStream(data.stream(boundary))
254286
};

lib/TasksResource.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,8 @@ export default class TasksResource {
591591
async upload(
592592
task: Task | JobTask,
593593
stream: UploadFileSource,
594-
filename?: string
594+
filename?: string,
595+
fileSize?: number
595596
): Promise<any> {
596597
if (task.operation !== 'import/upload') {
597598
throw new Error('The task operation is not import/upload');
@@ -601,7 +602,7 @@ export default class TasksResource {
601602
throw new Error('The task is not ready for uploading');
602603
}
603604

604-
const uploadFile = new UploadFile(stream, filename);
605+
const uploadFile = new UploadFile(stream, filename, fileSize);
605606
for (const parameter in task.result.form.parameters) {
606607
uploadFile.add(parameter, task.result.form.parameters[parameter]);
607608
}

0 commit comments

Comments
 (0)