文件上传

此文档尚未完善:它从文件上传的 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 字符串 文件的 MIME 类型(例如 video/mp4

您的工作是用 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