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

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

Githubnext-admin

相关推荐
screct_demo3 小时前
通俗易懂的讲一下Vue的双向绑定和React的单向绑定
前端·javascript·html
有心还是可以做到的嘛3 小时前
ref() 和 reactive() 区别
前端·javascript·vue.js
xcLeigh5 小时前
HTML5实现好看的博客网站、通用大作业网页模板源码
前端·课程设计·html5
mit6.8245 小时前
[Qt] 输入控件 | Line | Text | Combo | Spin | Date | Dial | Slider
前端·qt·学习·ubuntu
狗狗显卡6 小时前
一些计算机零碎知识随写(25年1月)
前端
Burt7 小时前
【unibest】可以去掉hbx模版了,base模板一统天下
前端·微信小程序·uni-app
拖孩7 小时前
💥大家好,我是拖孩🎤
前端·javascript·后端
CHANG_THE_WORLD7 小时前
Linux 基础七 内存
linux·服务器·前端
Z3r4y7 小时前
【Web】极简&快速入门Vue 3
前端·javascript·vue.js·vue3
cxr8287 小时前
Windows 11 系统中npm-cache优化
前端·windows·npm