近期在做跟畢業設計相關的數據后臺管理系統,其中的列表項展示有圖片展示,添加/編輯功能有文件上傳。
“文件上傳/刪除”也是我們平時開發會遇到的一個功能,這里分享個人的實現過程,與大家交流談論~
一、準備工作
- 本次案例使用的node版本是18.16.1
- 前端vue2+element-ui
- 后端使用node+express
- 安裝對應庫:element-ui、axios、multer
注意:本篇重點介紹文件上傳的相關功能,后端express的具體使用可以看我的express專欄~
express專欄
二、前端
文件上傳組件,來自element-ui組件庫
注意:自行安裝組件庫
Element - The world's most popular Vue UI framework
關于組件,有些展示和功能需要對應設置(比如文件上傳的服務器地址,刪除后端文件等等)才可以正常使用,所以我對代碼進行了修改補充。
組件代碼
<template><div><el-uploadref="upload"action="http://localhost:4000/api/upload" //文件上傳的接口list-type="picture-card":on-preview="handlePictureCardPreview":on-remove="handleRemove":file-list="fileList":on-change="handleFileChange":on-success="handleUploadSuccess":on-error="handleUploadError"name="file"><i class="el-icon-plus" /></el-upload><el-dialog :visible.sync="dialogVisible"><img width="100%" :src="dialogImageUrl" alt=""></el-dialog></div>
</template>
?變量和相關函數
注意:需要安裝axios(若安裝則跳過)
npm install axios
<script>
import Axios from 'axios'
export default {data() {return {dialogImageUrl: '', //預覽時展示的圖片dialogVisible: false,fileList: [] // 用于存儲文件列表}},methods: {// 生成文件預覽 URLhandleFileChange(file, fileList) {file.url = URL.createObjectURL(file.raw)this.fileList = fileList},// 刪除文件handleRemove(file, fileList) {this.fileList = fileList// 調用后端刪除接口if (file.response && file.response.data) {this.deleteFile(file.response.data)} else {this.$message.warning('文件尚未上傳成功,無法刪除')}},// 預覽圖片handlePictureCardPreview(file) {this.dialogImageUrl = file.urlthis.dialogVisible = true},// 文件上傳成功handleUploadSuccess(response, file) {console.log('文件上傳成功', response)if (response.code === 0) {// 從后端響應中獲取圖片的 URLconst imageUrl = response.data// 更新 fileList 中的文件 URLconst index = this.fileList.findIndex(f => f.uid === file.uid)if (index !== -1) {this.fileList[index].url = imageUrlthis.fileList[index].response = response // 保存后端返回的響應}this.$message.success('文件上傳成功')} else {this.$message.error('文件上傳失敗: ' + response.msg)}},// 文件上傳失敗handleUploadError(err, file) {console.error('文件上傳失敗', err)this.$message.error('文件上傳失敗')},deleteFile(filename) {// 去掉前綴 /public/static/upload/const pureFilename = filename.replace('/public/static/upload/', '')// 調用后端刪除接口Axios.delete(`http://localhost:4000/api/upload/${pureFilename}`).then(response => {if (response.data.code === 0) {this.$message.success('文件刪除成功')} else {this.$message.error('文件刪除失敗: ' + response.data.msg)}}).catch(err => {console.error('文件刪除失敗', err)this.$message.error('文件刪除失敗')})}}
}
</script>
完整代碼
?注意看:''localhost:4000''相關字眼(關于后端接口的,下文會作介紹)
<template><div><el-uploadref="upload"action="http://localhost:4000/api/upload"list-type="picture-card":on-preview="handlePictureCardPreview":on-remove="handleRemove":file-list="fileList":on-change="handleFileChange":on-success="handleUploadSuccess":on-error="handleUploadError"name="file"><i class="el-icon-plus" /></el-upload><el-dialog :visible.sync="dialogVisible"><img width="100%" :src="dialogImageUrl" alt=""></el-dialog></div>
</template><script>
import Axios from 'axios'
export default {data() {return {dialogImageUrl: '',dialogVisible: false,fileList: [] // 用于存儲文件列表}},methods: {// 生成文件預覽 URLhandleFileChange(file, fileList) {file.url = URL.createObjectURL(file.raw)this.fileList = fileList},// 刪除文件handleRemove(file, fileList) {this.fileList = fileList// 調用后端刪除接口if (file.response && file.response.data) {this.deleteFile(file.response.data)} else {this.$message.warning('文件尚未上傳成功,無法刪除')}},// 預覽圖片handlePictureCardPreview(file) {this.dialogImageUrl = file.urlthis.dialogVisible = true},// 文件上傳成功handleUploadSuccess(response, file) {console.log('文件上傳成功', response)if (response.code === 0) {// 從后端響應中獲取圖片的 URLconst imageUrl = response.data// 更新 fileList 中的文件 URLconst index = this.fileList.findIndex(f => f.uid === file.uid)if (index !== -1) {this.fileList[index].url = imageUrlthis.fileList[index].response = response // 保存后端返回的響應}this.$message.success('文件上傳成功')} else {this.$message.error('文件上傳失敗: ' + response.msg)}},// 文件上傳失敗handleUploadError(err, file) {console.error('文件上傳失敗', err)this.$message.error('文件上傳失敗')},// 刪除后端文件,參數傳遞的是文件名(經前端上傳過后,返回給前端的文件名)deleteFile(filename) {// 去掉前綴 /public/static/upload/const pureFilename = filename.replace('/public/static/upload/', '')// 調用后端刪除接口Axios.delete(`http://localhost:4000/api/upload/${pureFilename}`).then(response => {if (response.data.code === 0) {this.$message.success('文件刪除成功')} else {this.$message.error('文件刪除失敗: ' + response.data.msg)}}).catch(err => {console.error('文件刪除失敗', err)this.$message.error('文件刪除失敗')})}}
}
</script><style>
.el-upload-list__item-thumbnail {width: 100%;height: 100%;object-fit: cover;
}
</style>
三、后端
1、搭建express應用
【express-generator】01-安裝和基本使用
如果按照文章步驟(默認端口是3000,我這里設置成4000端口),則對應更換端口為4000,在創建的express項目的bin/www中進行修改。
2、新建文件夾以及文件
2.1、新建public/static/upload
這是存儲上傳文件的位置。
?2.2、新建routes/upload.js,用于撰寫路由方法。
安裝multer,這是處理文件上傳的相關庫。
npm install multer
var express = require("express");
const multer = require("multer");
const { uploading } = require("../utils/tool");
const fs = require('fs');
const path = require('path');
var router = express.Router();// 文件上傳接口
router.post("/", async function (req, res, next) {// single 方法里面書寫上傳控件的 name 值uploading.single("file")(req, res, function (err) {if (err instanceof multer.MulterError) {next("上傳文件失敗,請檢查文件的大小,控制在 6MB 以內");} else {console.log("req.file>>>", req.file);const filePath = "/public/static/upload/" + req.file.filename;res.send({ code: 0, msg: "文件上傳成功", data: filePath });}})
});// 文件刪除接口
router.delete("/:filename", function (req, res, next) {const filename = req.params.filename;const filePath = path.join(__dirname, '../public/static/upload',filename);console.log("后端接收到的文件參數>>>",filename,"完整基本路徑是>>>>",filePath);fs.unlink(filePath, (err) => {if (err) {console.error("刪除文件失敗", err);return res.status(500).send({ code: 1, msg: "刪除文件失敗" });}res.send({ code: 0, msg: "文件刪除成功" });});
});module.exports = router;
2.3、新增utlis/tool.js,撰寫工具類函數。
const multer = require("multer");
const path = require("path");// 設置上傳文件的引擎
const storage = multer.diskStorage({// 文件存儲的位置destination: function (req, file, cb) {cb(null, __dirname + '/../public/static/upload');},// 上傳到服務器的文件,文件名要做單獨處理filename: function (req, file, cb) {// 獲取文件名const basename = path.basename(file.originalname, path.extname(file.originalname));// 獲取后綴名const extname = path.extname(file.originalname);// 構建新的名字const newName = basename + new Date().getTime() + Math.floor(Math.random() * 9000 + 1000) + extname;cb(null, newName);}
})module.exports.uploading = multer({storage: storage,limits: {fileSize: 6000000,files: 1}
})
?3、在app.js中引入和使用
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const cors = require('cors');
// 文件上傳
const multer = require('multer');
const fs = require('fs');
// const path = require('path');var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var uploadRouter = require('./routes/upload');
var app = express();// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');// 允許所有來源訪問
app.use(cors());// 文件上傳
// const upload = multer({ dest: 'static/upload/' });
app.use(express.static(path.join(__dirname, 'public')));
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/api/upload/', uploadRouter);// error handler
app.use(function(err, req, res, next) {// set locals, only providing error in developmentres.locals.message = err.message;res.locals.error = req.app.get('env') === 'development' ? err : {};// render the error pageres.status(err.status || 500);res.render('error');
});module.exports = app;
四、測試
1、上傳文件(圖片)
?查看存儲上傳文件的位置
?2、刪除文件(圖片)
前端組件,鼠標進入到圖片區域,點擊刪除按鍵
前端作出對應提示
最后前端的文件列表也為空,成功刪除了文件。
后端查看文件夾,發現剛上傳的文件由于前端的刪除操作,也對應進行了刪除。
?
?五、總結
一些注意點
express應用默認端口號是3000,而案例演示的是4000(因為一般情況,3000端口容易被其他代碼程序給使用用,避免這種情況,可以使用一個新的端口號(或者把占用3000端口的程序都關閉))
關于文件上傳的設置,涉及到的知識點比較多,比如fs.unlink,path相關的知識,需要大家自行進行補充了解,部分知識點可以參考下方這篇博客文章。
【NODE】01-fs和path常用知識點
如果有問題,請留言評論~
如果你喜歡這篇文章,留下你的點贊和收藏~?
?