前言:
在egg中的上传是有两种方法来实现:官方入口
-
File 模式:
-
Stream 模式:
目录:
2、代码中:上传以后,mysql中新增数据,并且,public/ upload/ excel 里面保存我们上传的文件
2、准备一个excel文件,如果是按照我教程的话,第九步就是下载功能的实现,入口点我
4、app/ controller/ new/ uploadFile.js
7、serve/ common.js ,这里放给mysql库里存数据的相关代码
实现效果:
1、界面上
2、代码中:上传以后,mysql中新增数据,并且,public/ upload/ excel 里面保存我们上传的文件
这里来讲一讲他的Stream 模式模式的用法:
后端部分:
1、引入插件:
cnpm install xlsx await-stream-ready stream-wormhole --save
2、准备一个excel文件,如果是按照我教程的话,第九步就是下载功能的实现,入口点我
3、router文件里面配置路由
module.exports = app => {
//上传-批量用户数据-用的file
app.post('/uploadUserList', controller.new.uploadFile.uploadFiles_stream);
}
4、app/ controller/ new/ uploadFile.js
5、uploadFile.js
const Controller = require('egg').Controller;
const xlsx = require('xlsx')
const fs = require('fs')
const path = require('path');
class uploadFileController extends Controller {
/**
* @上传文件-将excel文件数据输入到user表
* 两种类型,file和stream
* 这里使用的是stream
*/
async uploadFiles_stream(){
const {ctx} = this;
this.uploadFileStream()
this.ctx.body = {
code:200,
masg:'success',
data:'上传成功'
};
}
//stream 获取上传文件,然后解析存库
async uploadFileStream(){
const {ctx} = this;
const stream = await ctx.getFileStream() ;
// 存储获取到的数据
let exceldata = [];
stream.on('data', function(chunk) {
// 读取内容
const workbook = xlsx.read(chunk, { type: 'buffer' });
// 遍历每张工作表进行读取(这里默认只读取第一张表)
for (const sheet in workbook.Sheets) {
if (workbook.Sheets.hasOwnProperty(sheet)) {
// 利用 sheet_to_json 方法将 excel 转成 json 数据
exceldata = exceldata.concat(xlsx.utils.sheet_to_json(workbook.Sheets[sheet]));
// break; // 如果只取第一张表,就取消注释这行
}
}
console.log(exceldata); // 打印解析出来的Excel 内容
if(exceldata.length>0){
//将excel数据增添到库里
ctx.service.common.addSimCard(exceldata);
}
})
//将上传的文件保存到本地
ctx.service.common.saveStreamFile(stream)
}
}
module.exports = uploadFileController;
7、serve/ common.js ,这里放给mysql库里存数据的相关代码
const Service = require('egg').Service;
const fs = require('fs')
const path = require('path');
// 异步二进制 写入流
const awaitWriteStream = require('await-stream-ready').write;
// 管道读入一个虫洞。
const sendToWormhole = require('stream-wormhole');
class CommonService extends Service {
/**
* 上次excel添加到mysql数据库
* @param {Array} headers excel标题栏
* @param {Array} result excel内容
*/
async addSimCard(result) {
const values = []; //[ [1,'张三','13519105845',...] ,[],[]... ]
result.forEach(item=> {
let _arr = [];
_arr[0] = parseInt(Math.random()*100000)
_arr[1] = item['姓名']
_arr[2] = item['手机号']
_arr[3] = item['地址']
_arr[4] = item['年龄']
_arr[5] = item['邮箱']
values.push(_arr);
});
// 重点sql语句
const addSql = 'INSERT INTO user (id,name,phone,address,age,email) VALUES ?';
const _result = await this.app.mysql.query(addSql, [values]);
console.log('上传成功')
}
/**
* 将上传的文件保存到本地- stream类型
* @param stream excel标题栏
*/
async saveStreamFile(stream){
//新建一个文件名,如果本地有就覆盖掉
const filename = stream.filename.split('.')[0] + path
.extname(stream.filename)
.toLocaleLowerCase();
//文件生成绝对路径
const target = path.join('', 'app/public/upload/excel', filename);
//生成一个文件写入 文件流
const writeStream = fs.createWriteStream(target);
try {
//把文件流 写入
await awaitWriteStream(stream.pipe(writeStream));
} catch (err) {
//出错则关闭管道
await sendToWormhole(stream);
throw err;
}
}
}
module.exports = CommonService;
前端部分 vue+element:
1、封装文件: uploadAndDown.vue
<template>
<el-upload
v-if="Refresh"
class="upload-demo"
ref="upload"
:action="action"
:headers="headers"
:multiple="multiple"
:data="data"
:name="name"
:with-credentials="cookieOK"
:show-file-list="showFileList"
:drag="drag"
:accept="accept"
:list-type="listType"
:auto-upload="autoUpload"
:file-list="fileList"
:disabled="is_disabled"
:on-preview="handlePreview"
:on-remove="handleRemove"
:on-success="handleSuccess"
:on-error="handleError"
:on-progress="handleProgress"
:on-exceed="handleExceed"
:on-change="onChange"
:before-upload="beforeUpload"
:before-remove="beforeRemove"
:http-request="httpRequest"
>
<el-button slot="trigger" type="primary" icon="el-icon-upload2">选取文件</el-button>
<el-button style="margin-left: 10px;"
type="success"
@click="submitUploadSD"
:disabled="fileList.length==0"
:title="fileList.length==0?'请先选中文件':''"
icon="el-icon-upload">开始上传</el-button>
<el-button type="danger"
v-if="fileList.length>0"
icon="el-icon-delete"
@click.stop="clearFiles"
title="清空选中文件"
circle></el-button>
<el-button style="margin-left: 10px;"
type="primary"
@click.stop="downFile"
icon="el-icon-download">下载模板</el-button>
<!--提示信息-->
<div slot="tip" class="el-upload__tip" v-if="tip_text!=''">{{tip_text}}</div>
</el-upload>
</template>
<script>
//element的上传文件组件
export default {
props:{
/**
* 自动上传参数
* */
autoUpload:{ // 是否需要选取完自动上传功能
type: Boolean,
default: false
},
action:{//上传的地址
type: String,
default: ''
},
headers: {//设置上传的请求头部
type:Object,
default: () => {
return {}
}
},
data: {//上传时额外带的参数
type:Object,
default: () => {
return {}
}
},
name:{//上传的文件字段名
type: String,
default: 'file'
},
cookieOK:{//支持发送 cookie 凭证信息
type: Boolean,
default: true
},
/**
* 公共参数
* */
showFileList:{//是否显示已上传文件列表
type: Boolean,
default: true
},
drag:{//是否启用拖拽上传
type: Boolean,
default: false
},
accept:{//接受文件类型-图片上传类型-不同的格式之间以逗号隔开
type: String,
// default:'.doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
default: '.xlsx,.xls'
},
listType:{ // 文件列表的类型 - text/picture/picture-card
type: String,
default: 'text'
},
fileList:{//已上传的文件列表,
type:Array,
default: () => {
// { 默认格式
// name: 'food.jpeg',
// url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'
// }
return []
}
},
is_disabled:{//是否禁止,true是禁止,false不禁止
type: Boolean,
default: false
},
multiple:{//是否可以多选
type: Boolean,
default: true
},
limit:{//最大允许上传个数
type: Number,
default: 30
},
tip_text:{//提示信息
type: String,
default: ''
},
/**
* 手动上传参数
* */
needFromUpload:{ // form表单,将上传的file文件通过 formUpload 方法发送出去
type: Boolean,
default: false
},
},
watch: {},
data() {
return {
Refresh:true,//强制刷新
}
},
created() {
},
mounted() {
},
methods: {
/**
* 上传-默认事件
* */
//文件列表移除文件时的钩子
handleRemove(file, fileList) {
console.log('当前移除的是'+file);
},
//点击文件列表中已上传的文件时的钩子
handlePreview(file) {
console.log('当前点击的是'+file);
},
//文件上传成功时的钩子
handleSuccess(response, file, fileList) {
console.log('文件上传成功');
},
//文件上传失败时的钩子
handleError(err, file, fileList) {
console.log('文件上传失败');
},
//文件上传时的钩子
handleProgress(event, file, fileList) {
console.log(file);
},
//文件超出个数限制时的钩子
handleExceed(files, fileList) {
console.log('文件超出个数限制');
},
//覆盖默认的上传行为,可以自定义上传的实现
httpRequest(){
},
//删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。
beforeRemove(file, fileList) {
console.log('当前删除的文件'+file);
this.fileList.forEach((item,index)=>{
if(item == file){
this.fileList.splice(index,1)
}
})
},
/**
* 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
*/
onChange(file, fileList) {
this.fileList = fileList;
console.log('当前的选中文件:');
console.log(fileList);
},
/**
* 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
*/
beforeUpload(file) {
console.log(file);
},
/**
* 上传-自定义事件
* */
submitUpload() {
this.$refs.upload.submit();
},
//清空已上传的文件列表(该方法不支持在 before-upload 中调用)
clearFiles(){
this.$refs.upload.clearFiles();
this.fileList = [];
},
//取消上传某个文件
abortFiles(file){
this.$refs.upload.abort(file);
},
/**
* 手动上传点击确认
* */
submitUploadSD(){
let arr = this.fileList;
let str = {
fileList:arr
}
this.$emit('uploadFile',str);
},
/**
* 下载模板点击
* */
downFile(){
this.$emit('downFile');
},
/**
* 手动刷新上传组件
* */
RefreshUpload(){
let arr = this.fileList;
this.Refresh = false;
this.$nextTick(()=>{
this.Refresh = true;
})
},
},
components: {},
beforeDestroy() {
}
}
</script>
<style lang='scss' scoped>
</style>
2、调用封装文件
<upload-and-down
class="uploadAndDown"
@uploadFile="uploadFile"
@downFile="downFile">
</upload-and-down>
3、上传和下载的事件
/**
* 上传
* */
uploadFile(str){
let fileList = str.fileList;
let formData = new FormData
fileList.forEach(item=>{
formData.append('file',item.raw)
})
this.$axios({
method:'post',
url:'http://localhost:7001/uploadUserList',
data:formData,
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(res => {
this.$message.success('上传成功')
})
},
//下载事件
downFile(){
console.log('点击下载按钮');
// 创建隐藏的可下载链接
var eleLink = document.createElement('a');
eleLink.style.display = 'none';
eleLink.href = 'http://localhost:7001/toexecl';
// 触发点击
document.body.appendChild(eleLink);
eleLink.click();
// 然后移除
document.body.removeChild(eleLink);
}