记录一次用nextjs作文件上传

418 阅读1分钟

1、首先写好接口代码

  1. /api/common/upload 新建route.ts,然后现封装保存文件函数

    import path from "path"; import fs from 'fs' import { randomUUID } from "crypto"; // 文件保存 const saveFile = async (blob: File) => { const dirName = /upload/${new Date().getFullYear()}-${new Date().getMonth() + 1} const uploadDir = path.join(process.cwd(), 'public' + dirName) // 上传路径 fs.mkdirSync(uploadDir,{recursive: true}) // 创建目录,有则创建 const fileName = randomUUID() + '.png' // 文件名 const arrayBuffer = await blob.arrayBuffer() fs.writeFileSync(uploadDir + '/' + fileName,new DataView(arrayBuffer)) // 将数据写入文件 return dirName + '/' + fileName }

  2. 准备好目录和文件名,目录用当前年月作文件夹,随机生成uuid来作文件名,然后接收formData中的file

    export const POST = async (req: NextRequest) => { const formData = await req.formData() // 获取formData的数据 const fileName = await saveFile(formData.get('file') as File) // 将文件数据存储 return NextResponse.json({ success: true, errorMessage: '文件上传成功', data: fileName }) }

2、前端封装上传组件

  1. 封装好自定义上传组件,props传入imageUrl和setImageUrl,setImageUrl在文件上传成功后执行,imageUrl可用于显示图片

import React from "react";
import { UploadOutlined } from "@ant-design/icons";
import { Button, message, Upload } from "antd";

type Props = {
  imageUrl: string;
  setImageUrl: any;
};

const EasyUpload = ({ imageUrl, setImageUrl }: Props) => (
  <Upload
    name="file"
    action="/api/common/upload"
    onChange={(info) => {
      if (info.file.status === "done") {
        console.log(info.file.response.data);
        setImageUrl(info.file.response.data);
        message.success(`${info.file.name} file uploaded successfully`);
      } else if (info.file.status === "error") {
        message.error(`${info.file.name} file upload failed.`);
      }
    }}
  >
    <Button icon={<UploadOutlined />}>Click to Upload</Button>
  </Upload>
);

export default EasyUpload;

2. 然后外部接收imageUrl,并顺带入网络请求,发送给后端接口,后端进行数据库存储

<Form
  layout="vertical"
  form={myForm}
  onFinish={async (v) => {
    console.log(currentId);
    // 若有id则修改
    if (currentId) {
      const res = await fetch("/api/articles/" + currentId, {
        method: "PUT",
        body: JSON.stringify({ ...v, imageUrl }),
      });
    } else {
      // 若无则新增
      const res = await fetch("/api/articles", {
        body: JSON.stringify({ ...v, imageUrl }),
        method: "POST",
      });
    }
    setQuery({ ...query });
    setOpen(false);
  }}
>
  <Form.Item
    label="标题"
    name="title"
    rules={[{ required: true, message: "标题不能为空" }]}
  >
    <Input placeholder="请输入标题" />
  </Form.Item>
  <Form.Item label="简介" name="desc">
    <Input.TextArea placeholder="请输入简介" />
  </Form.Item>
  <Form.Item label="封面" name="file">
    <EasyUpload imageUrl={imageUrl} setImageUrl={setImageUrl} />
  </Form.Item>
</Form>

3、前端在表格中显示

      <Table
        dataSource={list}
        rowKey="id"
        className="mt-4"
        pagination={{
          total,
          pageSize: query.per,
          onChange(page) {
            setQuery({ ...query, page });
          },
        }}
        columns={[
          {
            title: "序号",
            width: 100,
            render(v, r, i) {
              return i + 1;
            },
          },
          {
            title: "标题",
            dataIndex: "title",
          },
          {
            title: "简介",
            dataIndex: "desc",
          },
          {
            title: "封面",
            render(v, r: any) {
              return (
                <img
                  src={r.imageUrl}
                  alt={r.title}
                  style={{ width: 80, objectFit: "contain" }}
                />
              );
            },
          },
          {
            title: "操作",
            render(v, r) {
              return (
                <Space>
                  <Button
                    type="primary"
                    onClick={() => {
                      setCurrentId(v.id);
                      setOpen(true);
                      myForm.setFieldsValue(r);
                    }}
                  >
                    编辑
                  </Button>
                  <Popconfirm
                    title="删除"
                    description="确定要删除吗?"
                    onConfirm={async () => {
                      setCurrentId(v.id);
                      const res = await fetch("/api/articles/" + currentId, {
                        method: "DELETE",
                      }).then((res) => res.json());
                      setQuery({ ...query });
                    }}
                    okText="Yes"
                    cancelText="No"
                  >
                    <Button danger>删除</Button>
                  </Popconfirm>
                </Space>
              );
            },
          },
        ]}
      />