本文主要讲解已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 }

  ③. 开始开发业务相关代码, 新建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;

  ④. 新建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;

四、搭建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 };

  ②. 新建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>

五、效果图展示

NodeJS开发服务端实现文件上传下载和数据增删改查的更多相关文章

  1. WEB服务端安全---文件上传漏洞

    1.简述 文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务端命令的能力.这种攻击方式是最直接和有效的,而且互联网中我们经常会用到文件上传功能,它本身是没有问题的,正常的业 ...

  2. Nginx + Lua搭建文件上传下载服务

    收录待用,修改转载已取得腾讯云授权 最新腾讯云技术公开课直播,提问腾讯W3C代表,如何从小白成为技术专家?点击了解活动详情 作者 | 庄进发 编辑 | 迷鹿 庄进发,信息安全部后台开发工程师,主要负责 ...

  3. web端、android端的文件上传

    1.web端的文件上传. 这里是利用了第三方的jar包.这里所需要的jar包我已经上传到本博客的资源里了,以下是连接 http://download.csdn.net/detail/caihongsh ...

  4. centos 6.5下安装文件上传下载服务

    centos 6.5下安装文件上传下载服务 由于每次在CentOS中要下载一些配置文件到物理机,和上传一些文件到服务器,导致来回的开启ftp软件有点麻烦,这里我们可以使用文件上传下载服务,来解决上传和 ...

  5. nodejs+express-实现文件上传下载管理的网站

    Nodejs+Express-实现文件上传下载管理的网站 项目Github地址(对你有帮助记得给星哟):https://github.com/qcer/updo 后端:基于nodejs的express ...

  6. iOS开发之结合asp.net webservice实现文件上传下载

    iOS开发中会经常用到文件上传下载的功能,这篇文件将介绍一下使用asp.net webservice实现文件上传下载. 首先,让我们看下文件下载. 这里我们下载cnblogs上的一个zip文件.使用N ...

  7. .Net Core 图片文件上传下载

    当下.Net Core项目可是如雨后春笋一般发展起来,作为.Net大军中的一员,我热忱地拥抱了.Net Core并且积极使用其进行业务的开发,我们先介绍下.Net Core项目下实现文件上传下载接口. ...

  8. B/S文件上传下载解决方案

    需求: 项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在20G内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以20G来进行限制. PC端全平台支持,要求支持Window ...

  9. SpringMVC文件上传下载(单文件、多文件)

    前言 大家好,我是bigsai,今天我们学习Springmvc的文件上传下载. 文件上传和下载是互联网web应用非常重要的组成部分,它是信息交互传输的重要渠道之一.你可能经常在网页上传下载文件,你可能 ...

  10. Java 客户端操作 FastDFS 实现文件上传下载替换删除

    FastDFS 的作者余庆先生已经为我们开发好了 Java 对应的 SDK.这里需要解释一下:作者余庆并没有及时更新最新的 Java SDK 至 Maven 中央仓库,目前中央仓库最新版仍旧是 1.2 ...

随机推荐

  1. [ARC165D] Substring Comparison

    Problem Statement For an integer sequence $X=(X_1,X_2,\dots,X_n)$, let $X[L,R]$ denote the integer s ...

  2. 微调baichuan2-7b遇到的显存坑

    问题描述: 微调baichuan2-7b模型,验证一轮后继续训练第一个iteration显存大幅增加 项目链接: https://github.com/wp931120/baichuan_sft_lo ...

  3. JXNU acm选拔赛 涛涛的Party

    涛涛的Party Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other) Total Subm ...

  4. flask统一异常捕获(作用:统一捕获指定的异常并返回)

    flask中可以使用装饰器errorhandler来对指定的异常.状态码等统一捕获并处理. 对指定的状态码进行统一捕获 @app.errorhandler(404) # 参数e是异常的详细信息的对象, ...

  5. Supershell防溯源反制配置

    简介 项目地址:https://github.com/tdragon6/Supershell Supershell是一个集成了reverse_ssh服务的WEB管理平台,使用docker一键部署(快速 ...

  6. startx详解

    linux下startx命令详解 用途 初始化一个 X 会话. 语法 startx [ -d Display:0 ] [ -t | -w ] [ -x Startup | [ -r Resources ...

  7. mysql将查询结果生成临时表

    MySQL中将查询的结果生成临时表,列类型与查询的列一致,百度搜索到的没啥用. 直接上SQL: 将结果生成临时表 create temporary table temp_tb_name as (sel ...

  8. 动态规划问题(六)最长公共子序列(LCS)

    问题描述 ​ 给你两个字符串,要求得到这两个字符串的最长公共子序列长度. ​ 比如:对于输入的字符串 S1 "AGGTAB" 和 S2 "GXTXAYB",它们 ...

  9. 云小课|使用SpringBoot快速构建FunctionGraph HTTP函数

    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:本篇云小课主要指导 ...

  10. HDC.Cloud2021|开发者们都在谈的云原生到底长什么样?

    摘要:云原生数据库基于存储与计算分离架构,与传统数据库相比,具备高性能.高扩展.一致性.易管理和多云支持等特性,在海量数据处理.智能存储.业务应用等方面表现出了强大的生命力. 近几年,云原生的风越刮越 ...