- Published on
在 Next.js 中轻松实现文件上传
- Authors
- Name
- Zhanxin
本文共有 1051 字 · 预计阅读时长≈ 6 min
在 Web 开发中,文件上传是一个常见且重要的功能。Next.js 为我们提供了多种便捷的方式来实现文件上传,本文将详细介绍其中两种常用的方法。
使用 Server Action 上传文件
Server Action 是 Next.js 中处理文件上传的一种简单而直接的方式。 首先,假设我们有一个简单的图像上传应用程序,它需要获取用户上传的图像并将它们存储在 /public/uploads 目录中。为此,我们创建一个接收上传文件的 Server Action 函数:
"use server";
import fs from "node:fs/promises";
import { revalidatePath } from "next/cache";
export async function uploadFile(formData: FormData) {
const file = formData.get("file") as File;
const arrayBuffer = await file.arrayBuffer();
const buffer = new Uint8Array(arrayBuffer);
await fs.writeFile(`./public/uploads/${file.name}`, buffer);
revalidatePath("/");
}
这里的 uploadFile 函数从 FormData 对象中获取文件,并将其写入指定目录。需要注意的是,在实际项目中,我们通常不会将文件直接写入本地的 /public/uploads 目录,而是会选择像 S3 这样的云存储服务。此处只是为了提供一个简单的示例,所以采用了本地文件系统。 接下来,我们创建一个用于上传文件的表单页面:
import Image from 'next/image'
import fs from 'node:fs/promises'
import UploadForm from './UploadForm'
export default async function Home() {
const files = await fs.readdir('./public/uploads')
const images = files.filter((file) => file.endsWith('.jpg')).map((file) => `/uploads/${file}`)
return (
<main>
<UploadForm />
<div className="flex flex-wrap">
{images.map((image) => (
<div key={image} className="h-auto w-1/2 px-2">
<Image
key={image}
src={image}
width={400}
height={400}
alt={image}
className="w-full object-cover"
/>
</div>
))}
</div>
</main>
)
}
这是一个 React Server 组件,它能够使用 fs 读取 /public/uploads 目录中的文件,并筛选出图像文件进行展示。同样,这里为了简化,我们使用了本地文件系统,您在实际开发中可以将 fs 调用替换为对云存储服务的调用,或者从与用户关联的数据库中获取图像。 然后,我们还需要创建 UploadForm 组件:
该页面还引入了我们接下来要创建的 UploadForm 组件。
'use client'
import { uploadFile } from './upload-action'
export default function UploadForm() {
return (
<form action={uploadFile} className="flex flex-col gap-4">
<label>
<span>Upload a file</span>
<input type="file" name="file" ref={fileInput} />
</label>
<button type="submit">Submit</button>
</form>
)
}
这是一个简单的表单,包含文件输入和提交按钮。action 属性设置为我们之前定义的 uploadFile 函数,这是 Next.js 的一项特色功能,允许表单直接调用服务器操作。 当您点击“提交”按钮时,文件上传操作就会被触发。如果您查看开发人员控制台,会发现 Next.js 在客户端自动生成 POST 请求,并将其路由到 Server Action 进行处理。
使用 API Route
除了 Server Action,使用 API Route 也是在 Web 应用程序中处理文件上传的常见方法。首先,在 /api/uploadImage 目录下创建 route.ts 文件:
import { NextResponse } from "next/server";
import { revalidatePath } from "next/cache";
import fs from "node:fs/promises";
export async function POST(req: Request) {
try {
const formData = await req.formData();
const file = formData.get("file") as File;
const arrayBuffer = await file.arrayBuffer();
const buffer = new Uint8Array(arrayBuffer);
await fs.writeFile(`./public/uploads/${file.name}`, buffer);
revalidatePath("/");
return NextResponse.json({ status: "success" });
} catch (e) {
console.error(e);
return NextResponse.json({ status: "fail", error: e });
}
}
这段代码的主体部分与 Server Action 中的类似,只是在获取请求中的 formData 时有所不同。并且,我们还返回了一个 JSON 响应。 相应地,UploadForm 的代码也会有所变化:
"use client";
import { useRef } from "react";
export default function UploadForm() {
const fileInput = useRef<HTMLInputElement>(null);
async function uploadFile(
evt: React.MouseEvent<HTMLButtonElement, MouseEvent>
) {
evt.preventDefault();
const formData = new FormData();
formData.append("file", fileInput?.current?.files?.[0]!);
const response = await fetch("/api/uploadImage", {
method: "POST",
body: formData,
});
const result = await response.json();
console.log(result);
}
return (
<form className="flex flex-col gap-4">
<label>
<span>Upload a file</span>
<input type="file" name="file" ref={fileInput} />
</label>
<button type="submit" onClick={uploadFile}>
Submit
</button>
</form>
);
}
在这种方式下,我们不再通过 action 属性将表单提交给服务器,而是在提交按钮的 onClick 事件中调用 uploadFile 函数,通过 fetch 发送文件到 API Route 进行处理。
总结
Next.js 的 Server Action 为文件上传提供了简洁高效的解决方案。当您希望更直接、简单地处理文件上传时,它是一个不错的选择。然而,如果您需要对文件上传的机制有更多的控制权和定制化需求,使用 API Route 自行管理上传过程则更加灵活。