Skip to content

Commit b8b5cfa

Browse files
committed
fix: support S3 uploads in client
1 parent f2d3a42 commit b8b5cfa

2 files changed

Lines changed: 37 additions & 19 deletions

File tree

lib/CloudConvert.ts

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { io, type Socket } from 'socket.io-client';
1+
import path from 'node:path';
22
import { Readable } from 'node:stream';
3+
import { io, type Socket } from 'socket.io-client';
34
import { version } from '../package.json';
45
import JobsResource, { type JobEventData } from './JobsResource';
56
import SignedUrlResource from './SignedUrlResource';
@@ -17,23 +18,26 @@ export type UploadFileSource =
1718
| AsyncIterable<Uint8Array>
1819
| NodeJS.ReadableStream;
1920

21+
function guessFilename(source: UploadFileSource): string | undefined {
22+
return 'path' in source && typeof source.path === 'string'
23+
? path.basename(source.path)
24+
: undefined;
25+
}
26+
2027
export class UploadFile {
2128
private readonly attributes: Array<[key: string, value: unknown]> = [];
2229
private readonly data: AsyncIterable<Uint8Array>;
2330
constructor(
2431
data: UploadFileSource,
25-
private readonly filename?: string
32+
private readonly filename = guessFilename(data)
2633
) {
2734
this.data = UploadFile.unifySources(data);
2835
}
2936
add(key: string, value: unknown) {
3037
this.attributes.push([key, value]);
3138
}
32-
async *stream() {
39+
async *stream(boundary: string) {
3340
const enc = new TextEncoder();
34-
const boundary = `----------${Array.from(Array(32))
35-
.map(() => Math.random().toString(36)[2] || 0)
36-
.join('')}`;
3741
// Start multipart/form-data protocol
3842
yield enc.encode(`--${boundary}\r\n`);
3943
// Send all attributes
@@ -117,22 +121,32 @@ export default class CloudConvert {
117121
async call(
118122
method: 'GET' | 'POST' | 'DELETE',
119123
route: string,
120-
parameters?: UploadFile | object
124+
parameters?: UploadFile | object,
125+
options?: { presigned?: boolean; flat?: boolean }
121126
) {
122127
const baseURL = this.useSandbox
123128
? 'https://api.sandbox.cloudconvert.com/v2/'
124129
: `https://${
125130
this.region ? this.region + '.' : ''
126131
}api.cloudconvert.com/v2/`;
127-
return await this.callWithBase(baseURL, method, route, parameters);
132+
return await this.callWithBase(
133+
baseURL,
134+
method,
135+
route,
136+
parameters,
137+
options
138+
);
128139
}
129140

130141
async callWithBase(
131142
baseURL: string,
132143
method: 'GET' | 'POST' | 'DELETE',
133144
route: string,
134-
parameters?: UploadFile | object
145+
parameters?: UploadFile | object,
146+
options?: { presigned?: boolean; flat?: boolean }
135147
) {
148+
const presigned = options?.presigned ?? false;
149+
const flat = options?.flat ?? false;
136150
const url = new URL(route, baseURL);
137151
const { contentType, search, body } = prepareParameters(
138152
method,
@@ -142,8 +156,8 @@ export default class CloudConvert {
142156
url.search = search;
143157
}
144158
const headers = {
145-
Authorization: `Bearer ${this.apiKey}`,
146159
'User-Agent': `cloudconvert-node/v${version} (https://github.com/cloudconvert/cloudconvert-node)`,
160+
...(!presigned ? { Authorization: `Bearer ${this.apiKey}` } : {}),
147161
...(contentType ? { 'Content-Type': contentType } : {})
148162
};
149163
const res = await fetch(url, {
@@ -154,20 +168,20 @@ export default class CloudConvert {
154168
duplex: 'half'
155169
});
156170
if (!res.ok) {
157-
console.error('SND:', url, method, headers);
158-
console.error('RCV:', res, await res.text());
159171
// @ts-expect-error cause not present in types yet
160172
throw new Error(res.statusText, { cause: res });
161173
}
162174

163175
if (
164-
res.headers.get('Content-Type')?.toLowerCase() !==
165-
'application/json'
176+
!res.headers
177+
.get('content-type')
178+
?.toLowerCase()
179+
.includes('application/json')
166180
) {
167181
return undefined;
168182
}
169-
const { data } = await res.json();
170-
return data;
183+
const json = await res.json();
184+
return flat ? json : json.data;
171185
}
172186

173187
subscribe(
@@ -231,9 +245,12 @@ function prepareParameters(
231245
}
232246

233247
if (data instanceof UploadFile) {
248+
const boundary = `----------${Array.from(Array(32))
249+
.map(() => Math.random().toString(36)[2] || 0)
250+
.join('')}`;
234251
return {
235-
contentType: 'multipart/form-data',
236-
body: asyncIterableToReadableStream(data.stream())
252+
contentType: `multipart/form-data; boundary=${boundary}`,
253+
body: asyncIterableToReadableStream(data.stream(boundary))
237254
};
238255
}
239256

lib/TasksResource.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,8 @@ export default class TasksResource {
609609
return await this.cloudConvert.call(
610610
'POST',
611611
task.result.form.url,
612-
uploadFile
612+
uploadFile,
613+
{ presigned: true, flat: true }
613614
);
614615
}
615616

0 commit comments

Comments
 (0)