egg(十一):上传excel文件并解析内容,存到mysql数据库,并保存到本地,vue+egg实现前后端

妖狐艹你老母 2022-09-16 11:09 668阅读 0赞

前言:

在egg中的上传是有两种方法来实现:官方入口

  • File 模式:

  • Stream 模式:

目录:

实现效果:

1、界面上

2、代码中:上传以后,mysql中新增数据,并且,public/ upload/ excel 里面保存我们上传的文件

这里来讲一讲他的Stream 模式模式的用法:

后端部分:

1、引入插件:

2、准备一个excel文件,如果是按照我教程的话,第九步就是下载功能的实现,入口点我

3、router文件里面配置路由

4、app/ controller/ new/ uploadFile.js

5、uploadFile.js

7、serve/ common.js ,这里放给mysql库里存数据的相关代码

前端部分 vue+element:

1、封装文件: uploadAndDown.vue

2、调用封装文件

3、上传和下载的事件

到此结束!


实现效果:

1、界面上

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAUG9ubmVudWx0_size_20_color_FFFFFF_t_70_g_se_x_16

2、代码中:上传以后,mysql中新增数据,并且,public/ upload/ excel 里面保存我们上传的文件

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAUG9ubmVudWx0_size_8_color_FFFFFF_t_70_g_se_x_16

这里来讲一讲他的Stream 模式模式的用法:

后端部分:

1、引入插件:

  1. cnpm install xlsx await-stream-ready stream-wormhole --save

2、准备一个excel文件,如果是按照我教程的话,第九步就是下载功能的实现,入口点我

3、router文件里面配置路由

  1. module.exports = app => {
  2. //上传-批量用户数据-用的file
  3. app.post('/uploadUserList', controller.new.uploadFile.uploadFiles_stream);
  4. }

4、app/ controller/ new/ uploadFile.js

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAUG9ubmVudWx0_size_6_color_FFFFFF_t_70_g_se_x_16

5、uploadFile.js

  1. const Controller = require('egg').Controller;
  2. const xlsx = require('xlsx')
  3. const fs = require('fs')
  4. const path = require('path');
  5. class uploadFileController extends Controller {
  6. /**
  7. * @上传文件-将excel文件数据输入到user表
  8. * 两种类型,file和stream
  9. * 这里使用的是stream
  10. */
  11. async uploadFiles_stream(){
  12. const {ctx} = this;
  13. this.uploadFileStream()
  14. this.ctx.body = {
  15. code:200,
  16. masg:'success',
  17. data:'上传成功'
  18. };
  19. }
  20. //stream 获取上传文件,然后解析存库
  21. async uploadFileStream(){
  22. const {ctx} = this;
  23. const stream = await ctx.getFileStream() ;
  24. // 存储获取到的数据
  25. let exceldata = [];
  26. stream.on('data', function(chunk) {
  27. // 读取内容
  28. const workbook = xlsx.read(chunk, { type: 'buffer' });
  29. // 遍历每张工作表进行读取(这里默认只读取第一张表)
  30. for (const sheet in workbook.Sheets) {
  31. if (workbook.Sheets.hasOwnProperty(sheet)) {
  32. // 利用 sheet_to_json 方法将 excel 转成 json 数据
  33. exceldata = exceldata.concat(xlsx.utils.sheet_to_json(workbook.Sheets[sheet]));
  34. // break; // 如果只取第一张表,就取消注释这行
  35. }
  36. }
  37. console.log(exceldata); // 打印解析出来的Excel 内容
  38. if(exceldata.length>0){
  39. //将excel数据增添到库里
  40. ctx.service.common.addSimCard(exceldata);
  41. }
  42. })
  43. //将上传的文件保存到本地
  44. ctx.service.common.saveStreamFile(stream)
  45. }
  46. }
  47. module.exports = uploadFileController;

7、serve/ common.js ,这里放给mysql库里存数据的相关代码

  1. const Service = require('egg').Service;
  2. const fs = require('fs')
  3. const path = require('path');
  4. // 异步二进制 写入流
  5. const awaitWriteStream = require('await-stream-ready').write;
  6. // 管道读入一个虫洞。
  7. const sendToWormhole = require('stream-wormhole');
  8. class CommonService extends Service {
  9. /**
  10. * 上次excel添加到mysql数据库
  11. * @param {Array} headers excel标题栏
  12. * @param {Array} result excel内容
  13. */
  14. async addSimCard(result) {
  15. const values = []; //[ [1,'张三','13519105845',...] ,[],[]... ]
  16. result.forEach(item=> {
  17. let _arr = [];
  18. _arr[0] = parseInt(Math.random()*100000)
  19. _arr[1] = item['姓名']
  20. _arr[2] = item['手机号']
  21. _arr[3] = item['地址']
  22. _arr[4] = item['年龄']
  23. _arr[5] = item['邮箱']
  24. values.push(_arr);
  25. });
  26. // 重点sql语句
  27. const addSql = 'INSERT INTO user (id,name,phone,address,age,email) VALUES ?';
  28. const _result = await this.app.mysql.query(addSql, [values]);
  29. console.log('上传成功')
  30. }
  31. /**
  32. * 将上传的文件保存到本地- stream类型
  33. * @param stream excel标题栏
  34. */
  35. async saveStreamFile(stream){
  36. //新建一个文件名,如果本地有就覆盖掉
  37. const filename = stream.filename.split('.')[0] + path
  38. .extname(stream.filename)
  39. .toLocaleLowerCase();
  40. //文件生成绝对路径
  41. const target = path.join('', 'app/public/upload/excel', filename);
  42. //生成一个文件写入 文件流
  43. const writeStream = fs.createWriteStream(target);
  44. try {
  45. //把文件流 写入
  46. await awaitWriteStream(stream.pipe(writeStream));
  47. } catch (err) {
  48. //出错则关闭管道
  49. await sendToWormhole(stream);
  50. throw err;
  51. }
  52. }
  53. }
  54. module.exports = CommonService;

前端部分 vue+element:

1、封装文件: uploadAndDown.vue

  1. <template>
  2. <el-upload
  3. v-if="Refresh"
  4. class="upload-demo"
  5. ref="upload"
  6. :action="action"
  7. :headers="headers"
  8. :multiple="multiple"
  9. :data="data"
  10. :name="name"
  11. :with-credentials="cookieOK"
  12. :show-file-list="showFileList"
  13. :drag="drag"
  14. :accept="accept"
  15. :list-type="listType"
  16. :auto-upload="autoUpload"
  17. :file-list="fileList"
  18. :disabled="is_disabled"
  19. :on-preview="handlePreview"
  20. :on-remove="handleRemove"
  21. :on-success="handleSuccess"
  22. :on-error="handleError"
  23. :on-progress="handleProgress"
  24. :on-exceed="handleExceed"
  25. :on-change="onChange"
  26. :before-upload="beforeUpload"
  27. :before-remove="beforeRemove"
  28. :http-request="httpRequest"
  29. >
  30. <el-button slot="trigger" type="primary" icon="el-icon-upload2">选取文件</el-button>
  31. <el-button style="margin-left: 10px;"
  32. type="success"
  33. @click="submitUploadSD"
  34. :disabled="fileList.length==0"
  35. :title="fileList.length==0?'请先选中文件':''"
  36. icon="el-icon-upload">开始上传</el-button>
  37. <el-button type="danger"
  38. v-if="fileList.length>0"
  39. icon="el-icon-delete"
  40. @click.stop="clearFiles"
  41. title="清空选中文件"
  42. circle></el-button>
  43. <el-button style="margin-left: 10px;"
  44. type="primary"
  45. @click.stop="downFile"
  46. icon="el-icon-download">下载模板</el-button>
  47. <!--提示信息-->
  48. <div slot="tip" class="el-upload__tip" v-if="tip_text!=''">{
  49. {tip_text}}</div>
  50. </el-upload>
  51. </template>
  52. <script>
  53. //element的上传文件组件
  54. export default {
  55. props:{
  56. /**
  57. * 自动上传参数
  58. * */
  59. autoUpload:{ // 是否需要选取完自动上传功能
  60. type: Boolean,
  61. default: false
  62. },
  63. action:{//上传的地址
  64. type: String,
  65. default: ''
  66. },
  67. headers: {//设置上传的请求头部
  68. type:Object,
  69. default: () => {
  70. return {}
  71. }
  72. },
  73. data: {//上传时额外带的参数
  74. type:Object,
  75. default: () => {
  76. return {}
  77. }
  78. },
  79. name:{//上传的文件字段名
  80. type: String,
  81. default: 'file'
  82. },
  83. cookieOK:{//支持发送 cookie 凭证信息
  84. type: Boolean,
  85. default: true
  86. },
  87. /**
  88. * 公共参数
  89. * */
  90. showFileList:{//是否显示已上传文件列表
  91. type: Boolean,
  92. default: true
  93. },
  94. drag:{//是否启用拖拽上传
  95. type: Boolean,
  96. default: false
  97. },
  98. accept:{//接受文件类型-图片上传类型-不同的格式之间以逗号隔开
  99. type: String,
  100. // default:'.doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  101. default: '.xlsx,.xls'
  102. },
  103. listType:{ // 文件列表的类型 - text/picture/picture-card
  104. type: String,
  105. default: 'text'
  106. },
  107. fileList:{//已上传的文件列表,
  108. type:Array,
  109. default: () => {
  110. // { 默认格式
  111. // name: 'food.jpeg',
  112. // url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'
  113. // }
  114. return []
  115. }
  116. },
  117. is_disabled:{//是否禁止,true是禁止,false不禁止
  118. type: Boolean,
  119. default: false
  120. },
  121. multiple:{//是否可以多选
  122. type: Boolean,
  123. default: true
  124. },
  125. limit:{//最大允许上传个数
  126. type: Number,
  127. default: 30
  128. },
  129. tip_text:{//提示信息
  130. type: String,
  131. default: ''
  132. },
  133. /**
  134. * 手动上传参数
  135. * */
  136. needFromUpload:{ // form表单,将上传的file文件通过 formUpload 方法发送出去
  137. type: Boolean,
  138. default: false
  139. },
  140. },
  141. watch: {},
  142. data() {
  143. return {
  144. Refresh:true,//强制刷新
  145. }
  146. },
  147. created() {
  148. },
  149. mounted() {
  150. },
  151. methods: {
  152. /**
  153. * 上传-默认事件
  154. * */
  155. //文件列表移除文件时的钩子
  156. handleRemove(file, fileList) {
  157. console.log('当前移除的是'+file);
  158. },
  159. //点击文件列表中已上传的文件时的钩子
  160. handlePreview(file) {
  161. console.log('当前点击的是'+file);
  162. },
  163. //文件上传成功时的钩子
  164. handleSuccess(response, file, fileList) {
  165. console.log('文件上传成功');
  166. },
  167. //文件上传失败时的钩子
  168. handleError(err, file, fileList) {
  169. console.log('文件上传失败');
  170. },
  171. //文件上传时的钩子
  172. handleProgress(event, file, fileList) {
  173. console.log(file);
  174. },
  175. //文件超出个数限制时的钩子
  176. handleExceed(files, fileList) {
  177. console.log('文件超出个数限制');
  178. },
  179. //覆盖默认的上传行为,可以自定义上传的实现
  180. httpRequest(){
  181. },
  182. //删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。
  183. beforeRemove(file, fileList) {
  184. console.log('当前删除的文件'+file);
  185. this.fileList.forEach((item,index)=>{
  186. if(item == file){
  187. this.fileList.splice(index,1)
  188. }
  189. })
  190. },
  191. /**
  192. * 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
  193. */
  194. onChange(file, fileList) {
  195. this.fileList = fileList;
  196. console.log('当前的选中文件:');
  197. console.log(fileList);
  198. },
  199. /**
  200. * 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
  201. */
  202. beforeUpload(file) {
  203. console.log(file);
  204. },
  205. /**
  206. * 上传-自定义事件
  207. * */
  208. submitUpload() {
  209. this.$refs.upload.submit();
  210. },
  211. //清空已上传的文件列表(该方法不支持在 before-upload 中调用)
  212. clearFiles(){
  213. this.$refs.upload.clearFiles();
  214. this.fileList = [];
  215. },
  216. //取消上传某个文件
  217. abortFiles(file){
  218. this.$refs.upload.abort(file);
  219. },
  220. /**
  221. * 手动上传点击确认
  222. * */
  223. submitUploadSD(){
  224. let arr = this.fileList;
  225. let str = {
  226. fileList:arr
  227. }
  228. this.$emit('uploadFile',str);
  229. },
  230. /**
  231. * 下载模板点击
  232. * */
  233. downFile(){
  234. this.$emit('downFile');
  235. },
  236. /**
  237. * 手动刷新上传组件
  238. * */
  239. RefreshUpload(){
  240. let arr = this.fileList;
  241. this.Refresh = false;
  242. this.$nextTick(()=>{
  243. this.Refresh = true;
  244. })
  245. },
  246. },
  247. components: {},
  248. beforeDestroy() {
  249. }
  250. }
  251. </script>
  252. <style lang='scss' scoped>
  253. </style>

2、调用封装文件

  1. <upload-and-down
  2. class="uploadAndDown"
  3. @uploadFile="uploadFile"
  4. @downFile="downFile">
  5. </upload-and-down>

3、上传和下载的事件

  1. /**
  2. * 上传
  3. * */
  4. uploadFile(str){
  5. let fileList = str.fileList;
  6. let formData = new FormData
  7. fileList.forEach(item=>{
  8. formData.append('file',item.raw)
  9. })
  10. this.$axios({
  11. method:'post',
  12. url:'http://localhost:7001/uploadUserList',
  13. data:formData,
  14. headers:{
  15. 'Content-Type': 'application/x-www-form-urlencoded'
  16. }
  17. }).then(res => {
  18. this.$message.success('上传成功')
  19. })
  20. },
  21. //下载事件
  22. downFile(){
  23. console.log('点击下载按钮');
  24. // 创建隐藏的可下载链接
  25. var eleLink = document.createElement('a');
  26. eleLink.style.display = 'none';
  27. eleLink.href = 'http://localhost:7001/toexecl';
  28. // 触发点击
  29. document.body.appendChild(eleLink);
  30. eleLink.click();
  31. // 然后移除
  32. document.body.removeChild(eleLink);
  33. }

到此结束!

发表评论

表情:
评论列表 (有 0 条评论,668人围观)

还没有评论,来说两句吧...

相关阅读