-
Notifications
You must be signed in to change notification settings - Fork 0
/
FileResolver.ts
143 lines (112 loc) · 4.13 KB
/
FileResolver.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import type { BinaryLike } from "crypto";
import { createReadStream, existsSync, type PathLike } from "fs";
import { Stream, type Readable, type Writable } from "stream";
export const fileNamePattern = /.*\/+([^?#]+)(?:[?#].*)?/;
export interface RawFile {
/**
* The name of the file
*/
name: string
/**
* An explicit key to use for key of the formdata field for this file.
*/
key?: string
/**
* The actual data for the file
*/
data: Blob | BinaryLike | Buffer | DataView | File
/**
* Content-Type of the file
*/
contentType?: string
}
/**
* This parameter could be {@link Blob}, {@link Buffer}, {@link File}, {@link PathLike}, {@link RawFile}, {@link Readable}, {@link String}, {@link URL} or {@link Writable}
*/
export type FileResolvable =
| Blob
| File
| PathLike
| RawFile
| Readable
| Writable;
/**
* A function that converts {@link FileResolvable} to {@link File}
*
* @param file - The file as {@link FileResolvable} to resolve
* @param fileName - The name of the file to upload
*/
export async function resolveFile(file: FileResolvable, fileName?: string): Promise<File> {
if (file instanceof File) return file;
if (file instanceof URL || typeof file === "string") {
file = file.toString();
fileName ??= file.match(fileNamePattern)?.pop();
if (/^(?:s?ftp|https?):\/\//.test(file)) {
const response = await fetch(file);
if (response.status > 399) throw response;
return response.blob()
.then(blob => new File([blob], fileName ?? `file.${resolveBlobFileType(blob)}`, { type: blob.type }));
}
if (existsSync(file))
return streamToFile(createReadStream(file), fileName ?? "file.zip");
return new File([file], fileName ?? "file.zip");
}
if (file instanceof Blob) return new File([file], fileName ?? `file.${resolveBlobFileType(file)}`, { type: file.type });
fileName ??= "file.zip";
if (Buffer.isBuffer(file)) return new File([file], fileName);
if ("data" in file) {
if (file.data instanceof File) return file.data;
return new File([file.data], file.name, { type: file.contentType });
}
if (!Stream.isErrored(file)) return streamToFile(file, fileName);
throw new TypeError("Invalid file type was provided.");
}
export type FileResolvableSync = Exclude<FileResolvable, URL | Readable | Writable>
export function resolveFileSync(file: FileResolvableSync, fileName?: string): File {
if (file instanceof File) return file;
fileName ??= "file.zip";
if (typeof file === "string") {
return new File([file], fileName);
}
if (file instanceof Blob) return new File([file], fileName);
if (Buffer.isBuffer(file)) return new File([file], fileName);
if ("data" in file) {
if (file.data instanceof File) return file.data;
return new File([file.data], file.name);
}
throw new TypeError("Invalid file type was provided.");
}
/**
* A function that converts a like {@link Stream} parameter to {@link File}
*
* @param stream - A parameter like {@link Readable} or {@link Writable}
* @param fileName - A file name, if you wish
* @param mimeType - A mimeType parameter
*/
export function streamToFile(stream: Stream, fileName?: string | null, mimeType?: string) {
return new Promise<File>((resolve, reject) => {
const chunks: any[] = [];
stream.on("data", chunk => chunks.push(chunk))
.once("end", () => resolve(new File(chunks, fileName ?? "file.zip", { type: mimeType })))
.once("error", reject);
});
}
/**
* A function that converts a like {@link Stream} parameter to {@link Blob}
*
* @param stream - A parameter like {@link Readable} or {@link Writable}
* @param mimeType - A mimeType parameter
*/
export function streamToBlob(stream: Stream, mimeType?: string) {
return new Promise<Blob>((resolve, reject) => {
const chunks: any[] = [];
stream.on("data", chunk => chunks.push(chunk))
.once("end", () => resolve(new Blob(chunks, { type: mimeType })))
.once("error", reject);
});
}
export default resolveFile;
function resolveBlobFileType(b: Blob | Blob["type"]) {
if (typeof b === "string") return b.split("/")[1].split(/\W/)[0];
return resolveBlobFileType(b.type);
}