Next.js 实战 (六):如何实现文件本地上传

前言

在我们的日常工作中,上传文件、导入 Excel 表格数据这些是不可避免的,那在 Next.js 该如何实现上传文件到本地呢?

Next.js 的官方文档并没有相应的实例代码,需要开发者自行实现,一般来说有两种思路:

  1. 使用 Node.js 原生上传
  2. 使用第三方插件,如:multer

本文将以第一种方式实现:使用 Node.js 原生上传

业务设计

  1. 上传的文件使用哈希值命名,也可自己拼接上原文件名
  2. 文件上传到指定目录,这里我们指令上传的目录为:public/uploads,因为上传到这个目录,我们就能直接通过 /uploads/xxx.jpg 访问文件
  3. 上传目录的文件夹将以 YYYYMM 年月的格式分类,可以自定义

具体步骤

新建 app/api/upload/route.ts 文件,写入代码:

ts 复制代码
import crypto from 'crypto';
import dayjs from 'dayjs';
import { existsSync } from 'fs';
import fs from 'fs/promises';
import { NextRequest, NextResponse } from 'next/server';
import path from 'path';

import { RESPONSE_MSG } from '@/enums';
import { responseMessage } from '@/lib/utils';

export async function POST(req: NextRequest) {
  try {
    // 获取二进制文件数据
    const formData = await req.formData();

    const f = formData.get('file');

    if (!f) {
      return NextResponse.json({}, { status: 400 });
    }

    const file = f as File;

    const yearMonth = dayjs().format('YYYYMM');

    // 获取当前年月并创建对应的文件夹
    const uploadDir = path.join(process.cwd(), 'public/uploads', yearMonth);

    // 如果文件夹不存在,则创建
    if (!existsSync(uploadDir)) {
      await fs.mkdir(uploadDir, { recursive: true });
    }

    // 将文件保存到服务器的文件系统中
    const fileArrayBuffer = await file.arrayBuffer();

    // 生成哈希值作为文件名
    const hash = crypto.randomBytes(16).toString('hex');

    // 生成文件名
    const fileName = `${hash}.${file.name.split('.')[1]}`;

    // 将文件上传到 uploads 文件夹
    await fs.writeFile(path.join(uploadDir, fileName), Buffer.from(fileArrayBuffer));

    return NextResponse.json(
      responseMessage({
        fileName,
        size: file.size,
        url: `/uploads/${yearMonth}/${fileName}`,
      }),
    );
  } catch (error) {
    return NextResponse.json(responseMessage(error, RESPONSE_MSG.ERROR, -1));
  }
}

代码都有注释,我感觉还是比较好容易理解的

前端使用

前端可以通过 FormData 格式提交数据:

html 复制代码
// 上传头像
  const { loading: uploadLoading, run: runUploadAvatar } = useRequest(uploadFile, {
    manual: true,
    onSuccess: ({ code, data }) => {
      if (isSuccess(code)) {
        setAvatar(data.url);
      }
    },
  });

  // 图片上传回调
  const handleFileChange: ChangeEventHandler<HTMLInputElement> = (event) => {
    const file = event.target.files?.[0];
    if (file) {
      // 创建一个 FormData 对象
      const formData = new FormData();
      formData.append('file', file);
      runUploadAvatar(formData);
    }
  };

<Input
  name="avatar"
  type="file"
  accept="image/*"
  className="w-20"
  onChange={handleFileChange}
  size="sm"
  />

效果演示

我们通过 postman 模拟上传:

上传后的文件夹结构:

总结

这里只实现了单个文件上传,批量上传或者文件数组的需要自行实现

现在很多公司文件存储业务都已经使用第三方平台,比如:

  1. 阿里云 OSS
  2. 腾讯云 COS
  3. 七牛云 KODO
  4. 又拍云 USS

很少有上传文件到服务器本地的,业务量大的话会对服务器造成压力,一般这种适合个人站点、博客使用,这里我们当做学习就行。

Github:next-admin

相关推荐
陈随易7 小时前
bm2,Node.js 与 Bun 项目部署新选择
前端·后端·程序员
福兮说8 小时前
canvas 旋转图片的六个坑:四角被裁、Math.ceil 多出 1px 黑边、JPG 角发黑、翻转方向反了
前端·javascript·图像处理·canvas
FungLeo10 小时前
成为全栈·Next.js 网站前台篇·站点设置如何驱动页头、页脚与 SEO
react·isr·next.js·metadata·成为全栈·站点配置
传人once10 小时前
多图片上传预览功能如何写
前端·css·html·jquery·html5
Eric.4610 小时前
Wan‑Animate+MiniMax-H3 本地部署 AI 漫剧流水线:8G 显存动作迁移与动态分镜工程实战
前端·人工智能·comfyui·ai漫剧
库拉镜像AI牛牛12 小时前
短剧内容自动化生产:知漫剧工作室落地教程
大数据·服务器·前端·人工智能·语音识别
律宏阔12 小时前
Flutter 列表图片内存优化:CachedNetworkImage 与 ClipRRect 的取舍
前端·flutter
行者全栈架构师12 小时前
【鸿蒙心迹】相册文搜图工程化落地:HarmonyOS 7.0 端侧 AI 检索全链路实战与 5 个踩坑解法
前端
律宏阔12 小时前
Dart 3 Record:解决 Future.wait 异构返回值的类型问题
前端·flutter
Seraphina3612 小时前
DVWA(XSS Reflected/Stored High,CSRF Low)
前端·xss·csrf