Skip to content

Commit

Permalink
refactor(rest): add content-type(s) to uploads (#8290)
Browse files Browse the repository at this point in the history
  • Loading branch information
didinele committed Jul 17, 2022
1 parent bf65b37 commit 103a358
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 13 deletions.
4 changes: 2 additions & 2 deletions packages/discord.js/src/structures/MessagePayload.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ class MessagePayload {
name = fileLike.name ?? findName(attachment);
}

const data = await DataResolver.resolveFile(attachment);
return { data, name };
const { data, contentType } = await DataResolver.resolveFile(attachment);
return { data, name, contentType };
}

/**
Expand Down
17 changes: 11 additions & 6 deletions packages/discord.js/src/util/DataResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,32 +100,37 @@ class DataResolver extends null {
* @see {@link https://nodejs.org/api/stream.html}
*/

/**
* @typedef {Object} ResolvedFile
* @property {Buffer} data Buffer containing the file data
* @property {string} [contentType] Content type of the file
*/

/**
* Resolves a BufferResolvable to a Buffer.
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
* @returns {Promise<Buffer>}
* @returns {Promise<ResolvedFile>}
*/
static async resolveFile(resource) {
if (!resource) return null;
if (Buffer.isBuffer(resource)) return resource;
if (Buffer.isBuffer(resource)) return { data: resource };

if (typeof resource[Symbol.asyncIterator] === 'function') {
const buffers = [];
for await (const data of resource) buffers.push(data);
return Buffer.concat(buffers);
return { data: Buffer.concat(buffers) };
}

if (typeof resource === 'string') {
if (/^https?:\/\//.test(resource)) {
const res = await fetch(resource);
return Buffer.from(await res.arrayBuffer());
return { data: Buffer.from(await res.arrayBuffer()), contentType: res.headers.get('content-type') };
}

const file = path.resolve(resource);

const stats = await fs.stat(file);
if (!stats.isFile()) throw new DiscordError(ErrorCodes.FileNotFound, file);
return fs.readFile(file);
return { data: await fs.readFile(file) };
}

throw new TypeError(ErrorCodes.ReqResourceType);
Expand Down
7 changes: 6 additions & 1 deletion packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1039,11 +1039,16 @@ export class ContextMenuCommandInteraction<Cached extends CacheType = CacheType>
private resolveContextMenuOptions(data: APIApplicationCommandInteractionData): CommandInteractionOption<Cached>[];
}

export interface ResolvedFile {
data: Buffer;
contentType?: string;
}

export class DataResolver extends null {
private constructor();
public static resolveBase64(data: Base64Resolvable): string;
public static resolveCode(data: string, regex: RegExp): string;
public static resolveFile(resource: BufferResolvable | Stream): Promise<Buffer>;
public static resolveFile(resource: BufferResolvable | Stream): Promise<ResolvedFile>;
public static resolveImage(resource: BufferResolvable | Base64Resolvable): Promise<string | null>;
public static resolveInviteCode(data: InviteResolvable): string;
public static resolveGuildTemplateCode(data: GuildTemplateResolvable): string;
Expand Down
1 change: 1 addition & 0 deletions packages/rest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@sapphire/async-queue": "^1.3.2",
"@sapphire/snowflake": "^3.2.2",
"discord-api-types": "^0.36.1",
"file-type": "^17.1.2",
"tslib": "^2.4.0",
"undici": "^5.6.0"
},
Expand Down
19 changes: 16 additions & 3 deletions packages/rest/src/lib/RequestManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import { SequentialHandler } from './handlers/SequentialHandler';
import { DefaultRestOptions, DefaultUserAgent, RESTEvents } from './utils/constants';
import { resolveBody } from './utils/utils';

// Make this a lazy dynamic import as file-type is a pure ESM package
const getFileType = (): Promise<typeof import('file-type')> => {
let cached: Promise<typeof import('file-type')>;
return (cached ??= import('file-type'));
};

/**
* Represents a file to be added to the request
*/
Expand All @@ -27,6 +33,10 @@ export interface RawFile {
* The actual data for the file
*/
data: string | number | boolean | Buffer;
/**
* Content-Type of the file
*/
contentType?: string;
}

/**
Expand Down Expand Up @@ -401,11 +411,14 @@ export class RequestManager extends EventEmitter {
// FormData.append only accepts a string or Blob.
// https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#parameters
// The Blob constructor accepts TypedArray/ArrayBuffer, strings, and Blobs.
if (Buffer.isBuffer(file.data) || typeof file.data === 'string') {
formData.append(fileKey, new Blob([file.data]), file.name);
if (Buffer.isBuffer(file.data)) {
// Try to infer the content type from the buffer if one isn't passed
const { fileTypeFromBuffer } = await getFileType();
const contentType = file.contentType ?? (await fileTypeFromBuffer(file.data))?.mime;
formData.append(fileKey, new Blob([file.data], { type: contentType }), file.name);
} else {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
formData.append(fileKey, new Blob([`${file.data}`]), file.name);
formData.append(fileKey, new Blob([`${file.data}`], { type: file.contentType }), file.name);
}
}

Expand Down
57 changes: 56 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3003,6 +3003,7 @@ __metadata:
c8: ^7.11.3
discord-api-types: ^0.36.1
eslint: ^8.19.0
file-type: ^17.1.2
prettier: ^2.7.1
tslib: ^2.4.0
tsup: ^6.1.3
Expand Down Expand Up @@ -4188,6 +4189,13 @@ __metadata:
languageName: node
linkType: hard

"@tokenizer/token@npm:^0.3.0":
version: 0.3.0
resolution: "@tokenizer/token@npm:0.3.0"
checksum: 1d575d02d2a9f0c5a4ca5180635ebd2ad59e0f18b42a65f3d04844148b49b3db35cf00b6012a1af2d59c2ab3caca59451c5689f747ba8667ee586ad717ee58e1
languageName: node
linkType: hard

"@tootallnate/once@npm:1":
version: 1.1.2
resolution: "@tootallnate/once@npm:1.1.2"
Expand Down Expand Up @@ -9702,6 +9710,17 @@ dts-critic@latest:
languageName: node
linkType: hard

"file-type@npm:^17.1.2":
version: 17.1.2
resolution: "file-type@npm:17.1.2"
dependencies:
readable-web-to-node-stream: ^3.0.2
strtok3: ^7.0.0-alpha.7
token-types: ^5.0.0-alpha.2
checksum: 22103084b47d1fdc82e84b979512a2e9e488643f975b04cfd39acb2a9ab212438274a4f06039061631ca01be030f174c387c4a3ab9fe3417a1a199cb59079cb8
languageName: node
linkType: hard

"file-uri-to-path@npm:1.0.0":
version: 1.0.0
resolution: "file-uri-to-path@npm:1.0.0"
Expand Down Expand Up @@ -10997,7 +11016,7 @@ dts-critic@latest:
languageName: node
linkType: hard

"ieee754@npm:^1.1.13":
"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1":
version: 1.2.1
resolution: "ieee754@npm:1.2.1"
checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e
Expand Down Expand Up @@ -15009,6 +15028,13 @@ dts-critic@latest:
languageName: node
linkType: hard

"peek-readable@npm:^5.0.0-alpha.5":
version: 5.0.0-alpha.5
resolution: "peek-readable@npm:5.0.0-alpha.5"
checksum: cab949ed457dac95ae191dd412c6a0ba05e8db4842fd51704ccf2c8c16d6f3ceeefc997e8caea584a0395f229e468c0203a38a8d0ec68cfef8bacc157a006dcb
languageName: node
linkType: hard

"peek-stream@npm:^1.1.0":
version: 1.1.3
resolution: "peek-stream@npm:1.1.3"
Expand Down Expand Up @@ -15671,6 +15697,15 @@ dts-critic@latest:
languageName: node
linkType: hard

"readable-web-to-node-stream@npm:^3.0.2":
version: 3.0.2
resolution: "readable-web-to-node-stream@npm:3.0.2"
dependencies:
readable-stream: ^3.6.0
checksum: 8c56cc62c68513425ddfa721954875b382768f83fa20e6b31e365ee00cbe7a3d6296f66f7f1107b16cd3416d33aa9f1680475376400d62a081a88f81f0ea7f9c
languageName: node
linkType: hard

"readdirp@npm:~3.6.0":
version: 3.6.0
resolution: "readdirp@npm:3.6.0"
Expand Down Expand Up @@ -17239,6 +17274,16 @@ dts-critic@latest:
languageName: node
linkType: hard

"strtok3@npm:^7.0.0-alpha.7":
version: 7.0.0-alpha.8
resolution: "strtok3@npm:7.0.0-alpha.8"
dependencies:
"@tokenizer/token": ^0.3.0
peek-readable: ^5.0.0-alpha.5
checksum: 00e5c9ed0c5de537839cf443d5628f0ae88d2956ca1fdcbd45cd97372045d7179a40ec99f6d06b02c59ec2141e362142ad0a87c59506d401dbd3bd1ee242abaa
languageName: node
linkType: hard

"style-to-object@npm:^0.3.0":
version: 0.3.0
resolution: "style-to-object@npm:0.3.0"
Expand Down Expand Up @@ -17737,6 +17782,16 @@ dts-critic@latest:
languageName: node
linkType: hard

"token-types@npm:^5.0.0-alpha.2":
version: 5.0.0-alpha.2
resolution: "token-types@npm:5.0.0-alpha.2"
dependencies:
"@tokenizer/token": ^0.3.0
ieee754: ^1.2.1
checksum: ee23eeed6f383b1072d99781d62fc7840f1296a96d47e636e36fca757debd7eb4274d31fcd2d56997606eede00b12b1e61a64610fe0ed7807d6b1c4dcf5ccc6b
languageName: node
linkType: hard

"toml@npm:^3.0.0":
version: 3.0.0
resolution: "toml@npm:3.0.0"
Expand Down

0 comments on commit 103a358

Please sign in to comment.