文件上传

本指南中涵盖的 API 已在 React Router v7 中移除。请参阅 the React Router guide to file uploads 了解推荐方法。

大多数情况下,你可能需要将文件代理到文件托管服务器。

¥Most of the time, you'll probably want to proxy the file to a file host.

示例:

¥Example:

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 函数接受一些关于文件的参数:

¥The UploadHandler function accepts a number of parameters about the file:

属性 类型 描述
name string 字段名称(来自你的 HTML 表单字段 "name" 的值)
data AsyncIterable 文件字节的可迭代对象
filename string 用户选择上传的文件的名称(如 rickroll.mp4
contentType string 文件的内容类型(例如 videomp4

你的工作是使用 data 执行任何你需要的操作,并返回一个有效的 [FormData][form-data] 值:[File][the-browser-file-api]、stringundefined 可跳过将其添加到生成的 FormData 中。

¥Your job is to do whatever you need with the data and return a value that's a valid [FormData][form-data] value: [File][the-browser-file-api], string, or undefined to skip adding it to the resulting FormData.

上传处理程序组合

¥Upload Handler Composition

我们内置了 unstable_createFileUploadHandlerunstable_createMemoryUploadHandler,并且我们期望未来能开发更多上传处理程序实用程序。如果你的表单需要使用不同的上传处理程序,你可以使用自定义处理程序将它们组合在一起;这里有一个理论示例:

¥We have the built-in unstable_createFileUploadHandler and unstable_createMemoryUploadHandler and we also expect more upload handler utilities to be developed in the future. If you have a form that needs to use different upload handlers, you can compose them together with a custom handler; here's a theoretical example:

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;
};
Remix v2.17 中文网 - 粤ICP备13048890号