不得不说上传比我想象中复杂一点,也是我比较害怕的,不过现在却基本实现了!下面我来写一下我是怎么做的,不一定对哈哈哈,不过流程大概是懂了的。
做的比较粗糙,有些地方写的比较随意了
vue页面
首先看一下页面吧,有些多余的东西我就不展示了
<template>
<RouterView></RouterView>
书名: <input type=”text” v-model=”name” /><br />
价格: <input type=”text” v-model=”price” /><br />
最低价格: <input type=”text” v-model=”lowPrice” />~ 最高价格:
<input type=”text” v-model=”highPrice” />之间<br />
<button
@click=”handleAddUser”
:style=”{ marginTop: ’20px’, marginBottom: ’20px’ }”
>
新增图书
</button>
<button
@click=”handleQuery”
:style=”{ marginTop: ’20px’, marginBottom: ’20px’ }”
>
查询图书
</button>
<div :style=”{ width: ‘1280px’ }”>
<a-table height=”300″ :dataSource=”showData” :columns=”columns”>
<template #bodyCell=”{ column, text, record }”>
<template v-if=”column.dataIndex === ‘uploadImg'”>
<a-upload
:action=”uploadAction(record)”
name=”file”
:headers=”headers”
@change=”handleChange”
>
<a-button>
<upload-outlined></upload-outlined>
点击上传
</a-button>
<template #itemRender=”{ file, actions }”>
<a-space>
<span
:style=”{
color: file.status === ‘done’ ? ‘#1890ff’ : ‘red’,
}”
>{{ file.status === ‘done’ ? ‘上传成功’ : ‘上传失败’ }}</span
>
</a-space>
</template>
</a-upload>
</template>
<template v-if=”column.dataIndex === ‘operation'”>
<span
:style=”{ color: ‘#0090d8’, cursor: ‘pointer’ }”
@click=”handleDelete(record)”
>删除</span
>
</template>
<template v-if=”column.dataIndex === ‘filePath'”>
<template v-if=”record.filePath.length > 0″>
<div
v-for=”item in record.filePath”
:style=”{
position: ‘relative’,
}”
>
<img
:style=”{ height: ’90px’, width: ‘120px’ }”
:src=”‘http://localhost:4000/uploads/’ + item”
/>
<div
:style=”{
cursor: ‘pointer’,
position: ‘absolute’,
…略
}”
@click=”handleDeleteImg(record, item)”
>
x
</div>
</div></template
>
</template>
</template>
</a-table>
</div>
</template>
<script setup>
import instance from ‘./utils/request’;
import { onMounted, ref } from ‘vue’;
import { UploadOutlined } from ‘@ant-design/icons-vue’;
import { message, Upload as AUpload } from ‘ant-design-vue’;
const showData = ref([]);
const columns = [
{
title: ‘id’,
dataIndex: ‘_id’,
key: ‘_id’,
align: ‘center’,
width: 200,
},
{
title: ‘书名’,
dataIndex: ‘bookName’,
key: ‘bookName’,
align: ‘center’,
width: 300,
},
{
title: ‘价格’,
dataIndex: ‘price’,
key: ‘price’,
align: ‘center’,
width: 250,
},
{
title: ‘上传’,
dataIndex: ‘uploadImg’,
key: ‘uploadImg’,
align: ‘center’,
width: 200,
},
{
title: ”,
dataIndex: ‘filePath’,
key: ‘filePath’,
align: ‘center’,
width: 200,
},
{
title: ‘操作’,
dataIndex: ‘operation’,
align: ‘center’,
width: 250,
},
];
const name = ref(”);
const price = ref(”);
const lowPrice = ref(”);
const highPrice = ref(”);
const headers = {
authorization: ‘authorization-text’,
};
const uploadAction = (record) => {
return `http://localhost:4000/upload?id=${record?._id}`;
// 这个是本地服务器的地址
};
async function handleChange({ file }) {
if (file.status === ‘done’) {
await handleQuery();
}
}
const handleDeleteImg = async (record, url) => {
const { _id } = record;
await instance.post(‘/deleteImg’, {
id: _id,
imgUrl: url,
});
// 每次删除完/上传完都要记得重新查询
await handleQuery();
};
我将antd自带的fileList给去掉,渲染的内容也选择不展示的列表
nodejs部分
这里比较重要了
const express = require(‘express’);
// const multiparty = require(‘multiparty’);
const multer = require(‘multer’);
const path = require(‘path’);
const fs = require(‘fs’);
const mongoose = require(‘mongoose’);
const { useHandleDbHooks } = require(‘./handle’);
const app = express();
const cors = require(‘cors’);
const jsonParser = express.json();
const stringParser = express.urlencoded();
app.use(cors());
// 设置静态文件目录(重要)
app.use(express.static(‘./public’));
app.use(jsonParser);
app.use(stringParser);
mongoose.connect(‘mongodb://localhost:27017/newDb’);
mongoose.connection.on(‘open’, () => {
console.log(‘数据库连接成功’);
});
const Schema = mongoose.Schema;
// 每个schema 都会映射到一个collection,并定义这个collection里的文档的构成。
const bookSchema = new Schema({
bookName: String,
price: Number,
fileName: String,
filePath: { type: [String], default: [] },
file: Object,
});
// 创建Model,就是集合了(主要这里创建出来的book集合会自动变为复数形式-books,所以看到了不用觉得奇怪)
const BookModel = mongoose.model(‘book’, bookSchema);
// 把操作数据库的方法都写在另一个文件里了
const { addBook, getBooksList, deleteBook, queryBooks, addPicUrl, deleteImg } =
useHandleDbHooks(BookModel);
// 我们定义了一个 diskStorage 存储引擎,它指定了文件的保存位置和文件名。
const storage = multer.diskStorage({
// 将存储在./public/uploads下
destination: (req, file, cb) => {
cb(null, path.join(__dirname, ‘public’, ‘uploads’));
},
filename: (req, file, cb) => {
const ext = path.extname(file.originalname);
cb(null, Date.now() + ext); // 生成唯一文件名
},
});
// 创建 multer 实例
const upload = multer({ storage });
const deleteLocalFile = (fileName) => {
// 删除本地的uploads文件夹下的
const filePath = path.join(__dirname, ‘public’, ‘uploads’, fileName);
fs.unlink(filePath, (err) => {
//这个方法用于删除指定路径的文件
if (err) {
console.error(‘删除本地文件失败:’, err);
} else {
console.log(‘删除本地文件成功’);
}
});
};
app.post(‘/upload’, upload.single(‘file’), async (req, res) => {
// upload.single(‘file’) 告诉 Multer 期待一个名为 ‘file’ 的字段包含上传的文件。
try {
const id = req.query.id;
await addPicUrl({ id, filePath: req.file.filename });
res.send({
code: 200,
message: ‘文件上传成功’,
filePath: req.file.filename,
});
} catch (error) {
res.status(500).send({
code: 500,
message: ‘文件上传失败’,
error: error.message,
});
}
});
app.post(‘/deleteImg’, async (req, res) => {
const { imgUrl } = req.body;
try {
deleteLocalFile(imgUrl);
await deleteImg(req.body);
res.send({ code: 1, message: ‘删除成功’ });
} catch (error) {
console.log(‘删除失败’);
}
});
最后就是操作数据库的handle.js 这些mongoose语法都是gpt的
exports.useHandleDbHooks = function useHandleDbHooks(Model) {
const addPicUrl = async ({ id, filePath }) => {
try {
await Model.findById(id).then(async (doc) => {
if (!doc) {
throw new Error(‘没有找到对应的图书’);
}
//默认给数组
const updatedFilePaths = Array.isArray(doc.filePath)
? doc.filePath
: [];
updatedFilePaths.push(filePath);
await Model.findByIdAndUpdate(id, { filePath: updatedFilePaths });
});
} catch (error) {
return Promise.reject(new Error(‘添加失败’));
}
};
const deleteImg = async ({ id, imgUrl }) => {
await Model.updateOne(
{ _id: id }, // 根据 id 查找文档
{ $pull: { filePath: imgUrl } } // 从 filePath 数组中移除 imgUrl
);
};
return {
addBook,
getBooksList,
deleteBook,
queryBooks,
addPicUrl,
deleteImg,
};
};
有点多了,简单说一下我的上传逻辑,首先,上传的时候,上传组件中的action会向我们给定的地址发送请求,这个请求通常使用 FormData 对象来包装文件数据(file: (binary)),然后我们可以在请求体中拿到这个file,另外,我们顺便把这条数据的id传过去,方便数据库的操作
随后我们在nodej中设置一下multer中间件,将这个存在我们的本地文件夹中,(我一开始居然想把整个存在数据库里hhhh,人傻了,其实就是把我们的电脑作为一个服务器,存储,发请求的时候向这个服务器请求资源就可以拿到了)
然后我们还要把这个路径存在数据库中,就用请求中带过来的id去匹配。
这样,当我们查询的时候,就可以拿到返回的路径,我们再向这个路径去请求资源,就能拿到了
噢忘记展示了 上传
上传完以后,会在我们的本地存着
删除
同时,本地存的也会删掉
其实就这么多hhh不知道有没有人会认真看哈哈哈随便写写 后面再写一点我遇到的难点吧
前端展示失败
一开始我不知道为什么我访问的的时候是直接访问uploads而不是public下面的uploads,因为我的uploads是在public下的
其实是因为在express中使用了中间件
app.use(express.static(‘./public’));
这行代码告诉 Express 将 ./public 目录中的所有文件作为静态资源提供。也就是说,任何请求到 / 路径的静态文件都会从 ./public 目录中寻找。 因为配置了 express.static(‘./public’),当你请求 /uploads/your-image.jpg 这个路径时,Express 会自动在 ./public/uploads 目录中寻找 your-image.jpg 文件。
URL 路径:http://localhost:4000/uploads/your-image.jpg
实际文件位置:./public/uploads/your-image.jpg
声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/421386.html