1 | import * as Minio from "minio@7"; |
2 | import mime from "mime-types@2"; |
3 |
|
4 | const MESSAGES_I9 = { |
5 | en: { |
6 | error_mimetype: "File type could not be identified", |
7 | error_file_upload: "Error uploading file (S3)", |
8 | }, |
9 | pt_br: { |
10 | error_mimetype: "Tipo do arquivo não pôde ser identificado", |
11 | error_file_upload: "Erro ao enviar arquivo (S3)", |
12 | }, |
13 | }; |
14 | const MESSAGES = MESSAGES_I9.en; |
15 |
|
16 | |
17 | * Returns the mimetype and extension of a base64 encoded file |
18 | * @param encoded Base64 encoded file |
19 | * @returns Object with mimetype and extension |
20 | */ |
21 | function base64MimeType(encoded: string) { |
22 | let result = null; |
23 |
|
24 | if (typeof encoded !== "string") { |
25 | return result; |
26 | } |
27 |
|
28 | const match = encoded.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/); |
29 | if (match && match.length) { |
30 | result = { |
31 | mimetype: match[1], |
32 | extension: mime.extension(match[1]), |
33 | }; |
34 | } |
35 |
|
36 | return result; |
37 | } |
38 |
|
39 | |
40 | * Uploads a list of files to S3 |
41 | * @param s3 S3 resource |
42 | * @param files Array of base64 encoded files |
43 | * @returns Object with error_count and array of results (files URLs or error messages) |
44 | * |
45 | * Detects file type/extension using base64 header (e.g. "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAA..."). |
46 | * The output of File Input component can be used as parameter for this function. |
47 | */ |
48 | type S3 = { |
49 | endPoint: string; |
50 | port: number; |
51 | useSSL: boolean; |
52 | pathStyle: boolean; |
53 | bucket: string; |
54 | accessKey: string; |
55 | secretKey: string; |
56 | region: string; |
57 | }; |
58 | export async function main(s3: S3, files: string[]) { |
59 | if (files.length <= 0) { |
60 | return; |
61 | } |
62 |
|
63 | const s3client = new Minio.Client(s3); |
64 |
|
65 | const out: any[] = []; |
66 | let error_count = 0; |
67 |
|
68 | await Promise.all( |
69 | files.map(async (file: string) => { |
70 | try { |
71 | const mime_data = base64MimeType(file)!; |
72 | if (mime_data) { |
73 | const now = new Date(); |
74 | const pad = (n: number) => String(n).padStart(2, "0"); |
75 | const timestamp = |
76 | `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}` + |
77 | `-${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`; |
78 | const file_name = |
79 | timestamp + |
80 | "-" + |
81 | Math.random().toString(36).substring(2, 8) + |
82 | "." + |
83 | mime_data.extension; |
84 | const base64Data = file.replace(/^data:\w+\/\w+;base64,/, ""); |
85 | const bytes = Buffer.from(base64Data, "base64"); |
86 |
|
87 | await s3client.putObject(s3.bucket, file_name, bytes, bytes.length, { |
88 | "Content-Type": mime_data.mimetype, |
89 | }); |
90 |
|
91 | const url = `https://${s3["endPoint"]}/${s3["bucket"]}/${file_name}`; |
92 | out.push({ |
93 | success: true, |
94 | url, |
95 | }); |
96 | } else { |
97 | out.push({ |
98 | success: false, |
99 | message: MESSAGES.error_mimetype, |
100 | }); |
101 | error_count++; |
102 | } |
103 | } catch (error) { |
104 | out.push({ |
105 | success: false, |
106 | message: `${MESSAGES.error_file_upload} (S3): ${error.message}`, |
107 | }); |
108 | error_count++; |
109 | } |
110 | }), |
111 | ); |
112 |
|
113 | return { error_count, files: out }; |
114 | } |
115 |
|