文件上传与存储
处理文件上传与对象存储。
模板包含一个已鉴权的图片上传路由。它将文件写入本地文件系统并返回公开 URL——这是一个可用的起点,但并非持久化存储。请把它当作接入 S3、R2 或 Vercel Blob 的集成点。
提供的能力
- 一个已鉴权的
POST /api/storage/upload-image路由。 - 仅限图片的校验与无冲突的文件名。
- 为已保存文件返回的
/uploads/...URL。
重要文件
| 文件 | 作用 |
|---|---|
src/app/api/storage/upload-image/route.ts | 上传处理器 |
public/uploads/ | 文件写入位置(运行时创建) |
路由如何工作
export async function POST(request: Request) {
const session = await auth.api.getSession({ headers: await headers() });
if (!session?.user) return /* 401 未授权 */;
const formData = await request.formData();
const files = formData.getAll("files");
// …写入每个文件,返回其 URL
}逐步说明:
- 需要鉴权——路由读取 Better Auth 会话,无用户时返回
401。 - 多部分表单——读取
files字段(用getAll,因此支持多文件),空上传 返回400。 - 仅限图片——每一项必须是类型以
image/开头的File,其余一律拒绝。 - 安全文件名——扩展名由 MIME 类型映射而来(jpg/png/webp/gif),文件名为
crypto.randomUUID(),因此上传不会冲突也不会暴露原始名称。 - 写入并响应——文件写入
public/uploads/,路由返回{ success: true, data: { urls: ["/uploads/<uuid>.<ext>"] } }。
持久化问题
本地磁盘在生产环境中不持久。 文件写入 public/uploads,本地开发可用,
但在 Vercel、Serverless 或容器化部署中不会保留——文件系统是临时的、按
实例隔离的。重新部署/扩容时上传会丢失,且无法跨实例共享。请勿原样在生产中
依赖此路由。
接入对象存储
将文件系统写入替换为对象存储 SDK(AWS S3、Cloudflare R2、Vercel Blob 等)。 保留路由的鉴权检查与校验,只替换“写文件 + 构造 URL”这部分:
添加 SDK + 凭据
安装 SDK,并将其凭据作为仅服务端的环境变量添加(见 环境变量)。
替换写入
不再 writeFile(...) 到 public/uploads,而是把缓冲区上传到你的存储桶,
并使用返回的对象 URL。
返回真实 URL
把存储桶/CDN 的 URL 放进路由已有的 urls 数组,这样调用方无需改动。
生产要求
在向真实用户开放上传前,请补充:
- 大小限制——拒绝过大文件(该路由未限制大小)。
- 更强校验——校验真实内容类型而非仅凭声明的 MIME,并考虑重新编码图片。
- 访问策略——按对象决定公开还是签名/私有 URL。
- 清理——记录删除时清除孤立文件。
- CDN——通过 CDN 提供以提升性能。
- 滥用防护——对路由限流并限定到合适的用户范围。
