React Router v7 已發布。 查看文件
檔案上傳

此文件為 WIP(正在進行中):它是從檔案上傳的 API 文件中提取的,因此有些脫離上下文。我們打算將其重寫為關於檔案上傳的通用指南。

大多數時候,您可能需要將檔案代理到檔案主機。

範例

import type {
  ActionFunctionArgs,
  UploadHandler,
} from "@remix-run/node"; // or cloudflare/deno
import {
  unstable_composeUploadHandlers,
  unstable_createMemoryUploadHandler,
  unstable_parseMultipartFormData,
} from "@remix-run/node"; // or cloudflare/deno
import { writeAsyncIterableToWritable } from "@remix-run/node"; // `writeAsyncIterableToWritable` is a Node-only utility
import type {
  UploadApiOptions,
  UploadApiResponse,
  UploadStream,
} from "cloudinary";
import cloudinary from "cloudinary";

async function uploadImageToCloudinary(
  data: AsyncIterable<Uint8Array>
) {
  const uploadPromise = new Promise<UploadApiResponse>(
    async (resolve, reject) => {
      const uploadStream =
        cloudinary.v2.uploader.upload_stream(
          {
            folder: "remix",
          },
          (error, result) => {
            if (error) {
              reject(error);
              return;
            }
            resolve(result);
          }
        );
      await writeAsyncIterableToWritable(
        data,
        uploadStream
      );
    }
  );

  return uploadPromise;
}

export const action = async ({
  request,
}: ActionFunctionArgs) => {
  const userId = getUserId(request);

  const uploadHandler = unstable_composeUploadHandlers(
    // our custom upload handler
    async ({ name, contentType, data, filename }) => {
      if (name !== "img") {
        return undefined;
      }
      const uploadedImage = await uploadImageToCloudinary(
        data
      );
      return uploadedImage.secure_url;
    },
    // fallback to memory for everything else
    unstable_createMemoryUploadHandler()
  );

  const formData = await unstable_parseMultipartFormData(
    request,
    uploadHandler
  );

  const imageUrl = formData.get("avatar");

  // because our uploadHandler returns a string, that's what the imageUrl will be.
  // ... etc
};

UploadHandler 函數接受關於檔案的多個參數

屬性 類型 描述
name 字串 欄位名稱(來自您的 HTML 表單欄位的「name」值)
data AsyncIterable 檔案位元組的可迭代物件
filename 字串 使用者選擇上傳的檔案名稱(例如 rickroll.mp4
contentType 字串 檔案的內容類型(例如 videomp4

您的任務是使用 data 執行您需要的任何操作,並傳回一個有效的 [FormData][form-data] 值:[File][the-browser-file-api]、stringundefined,以跳過將其新增至產生的 FormData。

上傳處理程式組合

我們有內建的 unstable_createFileUploadHandlerunstable_createMemoryUploadHandler,並且我們也預期未來會開發更多上傳處理程式工具。如果您的表單需要使用不同的上傳處理程式,您可以使用自訂處理程式將它們組合在一起,以下是一個理論範例

import type { UploadHandler } from "@remix-run/node"; // or cloudflare/deno
import { unstable_createFileUploadHandler } from "@remix-run/node"; // or cloudflare/deno
import { createCloudinaryUploadHandler } from "some-handy-remix-util";

export const standardFileUploadHandler =
  unstable_createFileUploadHandler({
    directory: "public/calendar-events",
  });

export const cloudinaryUploadHandler =
  createCloudinaryUploadHandler({
    folder: "/my-site/avatars",
  });

export const fileUploadHandler: UploadHandler = (args) => {
  if (args.name === "calendarEvent") {
    return standardFileUploadHandler(args);
  } else if (args.name === "eventBanner") {
    return cloudinaryUploadHandler(args);
  }
  return undefined;
};
文件和範例授權於 MIT