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;
}
沒有留言:
張貼留言