2023-10-22

RemixJS 2 處理從用戶端來的資料

RemixJS 2 處理從用戶端來的資料

handle-request-data.ts 包含了幾個處理要求端來的資料,後來改用 qs 套件比較符合 expressjs 的風格。 其中處理上傳檔案的部份為存成檔案,目前建議存到雲端服務,例如 AWS S3。 這些函式同樣可以使用在 NextJS app router 上。

// *** handle-request-data.ts import qs from "qs"; import fs from "node:fs/promises"; import { v4 as uuidV4 } from "uuid"; export const getQueryStringObject:any = (request: Request) => { const q = request.url.indexOf("?"); if (q < 0) { return {}; } else { return qs.parse(request.url.slice(q+1)); } // *** 以下為使用 Object 的版本 // const url = new URL(request.url); // return Object.fromEntries(url.searchParams); }; export const getBodyObject = async (request: Request) => { const contentType = request.headers.get("Content-Type")?.split(";")[0]; switch (contentType) { case "multipart/form-data": const formData:any = await request.formData(); return qs.parse(new URLSearchParams(formData).toString()); // *** 以下為使用 Object 的版本 // return Object.fromEntries(formData); case "application/x-www-form-urlencoded": const txt = await request.text(); return qs.parse(txt); // *** 以下為使用 Object 的版本 // const usp = new URLSearchParams(txt); // return Object.fromEntries(usp); case "application/json": const json = await request.json(); return json; } }; type FileDataType = { size: number; type: string; lastModified: number; originalName: string; filename: string; path: string; }; type MultipartDataResultType = { fields: { [index: string]: string | string[] }; files: { [index: string]: FileDataType | FileDataType[] }; error: any; }; // *** 處理檔案上傳 export async function getMultipartData( request: Request, acceptedMimeTypes = ["image/jpeg", "image/png"], // 篩選類型設定 useUuidFilename = true, // 使用隨機 uuid 為主檔名 uploadDir = "./tmp" // 上傳的資料夾 ) { let result: MultipartDataResultType = { fields: {}, files: {}, error: {}, }; const method = request.method.toUpperCase(); let type = request.headers.get("Content-Type"); if (!type) return result; type = type.split(";")[0]; // 取得 mimetype if (method !== "GET") { if (type === "multipart/form-data") { const formData = await request.formData(); try { await fs.access(uploadDir, fs.constants.F_OK); } catch (ex) { // 建立上傳的資料夾 await fs.mkdir(uploadDir, { recursive: true }); } for (const [k, v] of formData.entries()) { console.log({ k, v }); if (typeof v === "string") { // 處理文字欄位 if (!result.fields[k]) { result.fields[k] = v; } else { if (result.fields[k] instanceof Array) { const strArray = result.fields[k] as string[]; strArray.push(v); } else { const val = result.fields[k] as string; result.fields[k] = [val, v]; } } } else if (v instanceof Blob) { // 處理檔案欄位 result.files = result.files || {}; const { size, type, name, lastModified } = v; if (acceptedMimeTypes.length) { if (!acceptedMimeTypes.includes(type)) { continue; } let filename = name; if (useUuidFilename) { let tmpName = name.toLowerCase(); let mainName = uuidV4(); let extName = ""; if (tmpName.indexOf(".") !== -1) { const frs = tmpName.split("."); extName = "." + frs[frs.length - 1]; } filename = mainName + extName; } const path = uploadDir + "/" + filename; const fileData: FileDataType = { size, type, lastModified, originalName: name, filename, path, }; if (!result.files[k]) { result.files[k] = fileData; } else { if (result.files[k] instanceof Array) { const fdArray = result.files[k] as FileDataType[]; fdArray.push(fileData); } else { const val = result.files[k] as FileDataType; result.files[k] = [val, fileData]; } } await fs.writeFile(path, v.stream() as any); } } } } } return result; }

沒有留言:

FB 留言