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