本文主要讲解已NodeJS作为服务器完成文件的上传下载和数据增删改查,前端框架为Vue3,UI框架为element-plus,Node版本为V16.14.2.
项目场景模拟是开发一个项目管理的系统,支持任务和项目的增删改查、以及上传、下载项目附件包。
其他依赖如下
"dependencies": { "body-parser": "^1.20.2", "cors": "^2.8.5", "express": "^4.18.2", "fs": "^0.0.1-security", "http": "^0.0.1-security", "moment": "^2.29.4", "multiparty": "^4.2.3", "mysql": "^2.18.1" }Node依赖包
"dependencies": { "axios": "^1.3.4", "core-js": "^3.8.3", "dayjs": "^1.11.7", "element-plus": "^2.2.32", "js2uml": "^0.2.4", "mysql": "^2.18.1", "vue": "^3.2.13", "vue-router": "^4.1.6" }, "devDependencies": { "@babel/core": "^7.12.16", "@babel/eslint-parser": "^7.12.16", "@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-eslint": "~5.0.0", "@vue/cli-service": "~5.0.0", "eslint": "^7.32.0", "eslint-plugin-vue": "^8.0.3", "less": "^4.1.3", "less-loader": "^11.1.0" }Web依赖包
一、配置Node服务
首先npm i express 安装express组件作为Web服务,创建一个基础文件index.js代码如下:
const express = require('express'); const router = express(); router.listen(3000); console.log('服务器已启动,请访问 localhost:3000');
然后cmd到当前目录,输入node index.js即可启动服务
二、前端搭建
1. 打开 cmd 窗口输入命令 npm i -g @vue/cli 安装vue脚手架
2. 输入 vue create 项目名称初始化项目
3. 输入 npm i axios 安装axios组件用于请求交互
4. 输入 npm i element-plus 安装Ele用户快速搭建UI
5. 输入 npm run serve 启动Web服务,打开浏览器访问localhost:端口号 即可验证是否搭建成功
三、Node服务搭建
1. 首先安装mysql数据库,mysql数据库为轻型数据库,所以在自己本地安装也不会太影响性能
访问 https://dev.mysql.com/downloads/mysql/ 下载数据库,按照步骤安装完成后,在配置完环境变量后即可(注:初始化时会生成一个默认的初始密码,需要记录下,否则后面登陆需要重置,比较耗时)
2. 安装navicat 客户端,按照提示步骤安装即可
3. 开发Node服务代码
①. 在index.js 中增加如下代码,为了方便大家阅读,所以一次性罗列,可以增删不同的项来学习他们的用处
②. 新建一个db.js文件,主要创建数据库连接池和提供操作数据库接口
const mysql = require('mysql'); const pool = mysql.createPool({ connectionlimit: 50, // 最大连接数 host: 'localhost', user: 'root', // 数据库用户 password: '123456', // 数据库密码 database: 'task' // 新建的数据库名 }) /** * 数据库查询 * @param {*} sql 查询语句 * @param {*} p 参数 * @param {*} c 回调函数 */ function query(sql, params = [], callback) { pool.getConnection((err, connection) => { connection.query(sql, params, (queryErr, res) => { connection.release(); callback.apply(null, [queryErr ? false : true, queryErr ? queryErr : res]); }) }) } module.exports = { query }View Code
③. 开始开发业务相关代码, 新建border.js文件,主要处理跟项目和任务相关的逻辑
const express = require('express'); // 处理日期格式 const moment = require('moment'); // 文件上传的流解析 const multiparty = require('multiparty'); const fs = require('fs'); const db = require('./db'); function guid() { function S4() { return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); } return S4(); } function creatRes(flag, data) { let res = {}; if (flag) { res = { code: 1001, data: data } } else { res = { code: 4001, errMsg: data } } return res; } const router = express.Router(); /** * 获取文件列表 */ router.get('/getTaskList', (req, res) => { const sql = 'select t.*, p.name as productName from task t join product p where t.productId = p.productId' db.query(sql, null, (flag, data) => { res.send(creatRes(flag, data)); }) }) /** * 添加任务 */ router.post('/addTask', (req, res) => { const param = req.body; const id = guid(); const values = [id, param.name, param.startTime, param.progress, param.productId, param.desc, param.endTime, moment().format('YYYY-MM-DD HH:mm:ss') ]; const sql = 'INSERT INTO task VALUES(?, ?, ?, ?, ?, ?, ?, ?)'; db.query(sql, values, (flag, data) => { res.send(creatRes(flag, data)) }) }) /** * 删除任务 */ router.post('/deleteTask', (req, res) => { const param = req.body; const sql = `DELETE FROM task where id='${param.id}'`; db.query(sql, null, (flag, data) => { res.send(creatRes(flag, data)); }) }) /** * 获取项目列表 */ router.get('/getProductList', (req, res) => { const sql = 'select * from product'; db.query(sql,null, (flag, data) => { res.send(creatRes(flag, data)); }) }) /** * 添加项目 */ router.post('/addProduct', (req, res) => { const param = req.body; const id = guid(); const sql = 'INSERT INTO product VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; const sqlParam = [id, param.name, param.productId, param.startTime, param.endTime, param.progress, param.desc, moment().format('YYYY-MM-DD HH:mm:ss'), '', '']; db.query(sql, sqlParam, (flag, data) => { res.send(creatRes(flag, data)); }) }) /** * 删除项目 */ router.post('/deleteProduct', (req, res) => { const param = req.body; const querySql = `select packageId from product where id ='${param.id}'`; db.query(querySql, null, (flag, data) => { if (flag) { fs.unlink(__dirname + '/demo/' + data[0].packageId, () => { const sql = `DELETE t, p from product p left join task t on p.productId = t.productId where p.id= '${param.id}'`; db.query(sql, null, (flag, data) => { res.send(creatRes(flag, data)); }) }) } }) }) /** * 给项目上传附件包并将包保存到本地 */ router.post('/upload', (req, res) => { let form = new multiparty.Form(); // 将上传的文件流解析并保存到本地,此时文件名是随机产生的 form.uploadDir = __dirname + '/demo/'; form.parse(req, (err, fields, files) => { const packageName = files.file[0].originalFilename; const randomName = Math.ceil(Math.random() * 10000)+ '_' + packageName; // 将随机产生的文件名修改为上传时的文件名 fs.rename(files.file[0].path, __dirname + '\\demo\\' + randomName, () => { console.log('修改成功-----'); }); const sql = `update product set packageName="${packageName}",packageId="${randomName}" where id = "${fields.id[0]}"`; db.query(sql, null, (flag, data)=> { res.send(creatRes(flag, data)); }) }) }) /** * 将存储的文件下载到本地 */ router.get('/download', (req, res) => { const sql = 'select packageId,packageName from product where id = "' + req.query.id + '"'; db.query(sql, null, (flag, result) => { const data = result[0]; const path = __dirname + '\\demo\\' + data.packageId; const f = fs.createReadStream(path); const userAgent = (req.headers['user-agent']||'').toLowerCase(); const filename = data.packageName; // 这句话不能少,不然Web端获取不到响应头参数 res.setHeader('Access-Control-Expose-Headers', 'Content-disposition'); // 不同的浏览器上传文件流时将参数写入到响应头的方式 if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) { res.writeHead(200, { 'Content-type': 'application/octet-stream', 'Content-disposition': 'attachment; filename=' + encodeURIComponent(filename), }); } else if(userAgent.indexOf('firefox') >= 0) { res.writeHead(200, { 'Content-type': 'application/octet-stream', 'Content-disposition': 'attachment; filename*="utf8\'\'' + encodeURIComponent(filename)+'"', }); } else { /* safari等其他非主流浏览器只能自求多福了 */ res.writeHead(200, { 'Content-type': 'application/octet-stream', 'Content-disposition': 'attachment; filename=' + new Buffer(filename).toString('binary'), }); } f.pipe(res); }) }) module.exports = router;View Code
④. 新建package.js文件,无实质内容,主要为了展示分模块路由的使用方法
const express = require('express'); const db = require('./db'); const router = express.Router(); function creatRes(flag, data) { let res = {}; if (flag) { res = { code: 1001, data: data } } else { res = { code: 4001, errMsg: data } } return res; } router.get('/getList', (req, res) => { db.query('select * from package', null, (flag, data) => { res.send(creatRes(flag, data)); }) }) module.exports = router;View Code
四、搭建Web端, 到第三步,Node服务已经开发完毕,Web端搭建默认各位老总对Web端很熟,所以大部分东西就直接上代码了
①. 首先新建axios.js文件,提供get、post、download、upload接口
import axios from "axios"; const instance = axios.create({ // Node服务地址和端口 baseURL: 'http://localhost:3000/', timeout: 30000 }) instance.interceptors.response.use(function (response) { return response; }, function (error) { return Promise.reject(error); }); function get(url, param) { return new Promise((reslove, reject) => { instance.get(url, { params: param }).then(res => { if (res.data.code === 1001) { reslove(res.data.data); } else { reject(res.data.errMsg); } }); }) } function post(url, param) { return new Promise((reslove, reject) => { instance.post(url, param).then(res => { if (res.data.code === 1001) { reslove(res.data.data); } else { reject(res.data.errMsg); } }) }) } function upload(url, param) { // 请求头配置必不可少 const config = { headers: { 'Content-type': 'multipart/form-data' } }; return new Promise((reslove, reject) => { instance.post(url, param, config).then(res => { if (res.data.code === 1001) { reslove(res.data.data); } else { reject(res.data.errMsg); } }); }) } function download(url, param) { return new Promise((reslove, reject) => { instance.get(url, { params: param, responseType: 'blob' }).then(res => { if (res.data instanceof Blob) { // 获取文件名,重新命名,否则下载后文件名会变 const fileName = decodeURIComponent(res.headers['content-disposition'].split(';')[1].split('filename=')[1]); const blob = new Blob([res.data], { type: 'application/x-zip-compressed' }); const a = document.createElement('a'); const href = window.URL.createObjectURL(blob); a.href = href; a.download = fileName; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(href); document.body.removeChild(a); reslove(fileName); } else { reject('文件下载失败'); } }); }) } export default { get, post, download, upload };View Code
②. 新建Product.vue文件,主要包含增、删、查和上传下载文件功能
<template> <div class="package"> <el-table :data="list" stripe style="width: 100%"> <el-table-column prop="productName" label="产品名称" /> <el-table-column prop="productId" label="产品ID" /> <el-table-column prop="packageName" label="包名" /> <el-table-column prop="createTime" label="创建时间" /> <el-table-column prop="updateTime" label="更新时间时间" /> <el-table-column fixed="right" label="操作" > <template #default="scope"> <div class="upload-button"> <input type="file" @change="upload(scope.row, $event)"/> <el-button link type="primary" size="small" >上传</el-button> </div> <el-button link type="primary" size="small" @click="download(scope.row)">下载</el-button> </template> </el-table-column> </el-table> </div> </template> <script> import { onMounted, ref } from 'vue' import axios from '../utils/axios.js'; import { ElMessage } from 'element-plus' export default { name: 'PackageView', setup() { const list = ref([]); function getList() { axios.get('/package/getList').then(res => { list.value = res; }) } function upload(row, e) { const file = e.target.files[0]; if (file) { const form = new FormData(); form.append('file', file); form.append('id', row.id); axios.upload('/package/upload', form).then(() => { ElMessage({ showClose: false, message: '上传成功.', type: 'success' }); getList(); }); } } function download(row) { const param = { id: row.id }; axios.download('/package/download', param).then(() => { ElMessage({ showClose: false, message: '下载成功.', type: 'success' }) }); } onMounted(() => { getList(); }) return { list, upload, download } } } </script> <style lang="less" scoped> .package { .upload-button { width: 30px; display: inline; position: relative; input { width: 30px; opacity: 0; z-index: 1; position: absolute; cursor: pointer; } } } </style>View Code
六、效果图展示
标签:const,NodeJS,res,上传下载,改查,param,flag,sql,data From: https://www.cnblogs.com/codeOnMar/p/17201142.html