大多数情况下,你可能需要将文件代理到文件托管服务器。
¥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]、string
或 undefined
可跳过将其添加到生成的 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_createFileUploadHandler
和 unstable_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;
};