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 } -
准备好目录和文件名,目录用当前年月作文件夹,随机生成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、前端封装上传组件
- 封装好自定义上传组件,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>
);
},
},
]}
/>