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

前言:

       在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、界面上

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);
      }

到此结束!

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现前端上传文件到本地并将url存入本地数据库,需要前后端协同完成以下几个步骤: 1. 前端使用 Vue.js 实现文件上传,并将文件传递给后端。 2. 后端使用 Spring Boot 接收前端传递的文件,并将文件保存到本地。 3. 后端生成本地文件的URL,将URL保存数据库中。 4. 前端从数据库中获取URL,展示文件信息。 以下是具体实现步骤: 1. 前端实现文件上传使用 Vue.js 和 Axios 实现,代码如下: ```html <template> <div> <input type="file" @change="onFileChange"> <button @click="uploadFile">上传</button> </div> </template> <script> import axios from 'axios'; export default { data() { return { file: null }; }, methods: { onFileChange(event) { this.file = event.target.files[0]; }, uploadFile() { const formData = new FormData(); formData.append('file', this.file); axios.post('/api/upload', formData).then(response => { console.log(response.data); }).catch(error => { console.log(error); }); } } }; </script> ``` 2. 后端使用 Spring Boot 接收前端传递的文件,并将文件保存到本地。代码如下: ```java @RestController @RequestMapping("/api") public class FileController { @Value("${file.upload-dir}") private String uploadDir; @PostMapping("/upload") public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException { String filename = file.getOriginalFilename(); Path path = Paths.get(uploadDir, filename); Files.write(path, file.getBytes()); return "上传成功"; } } ``` 在 Spring Boot 中,使用 `@Value` 注解获取配置文件中的变量值,即上传文件存储的路径。 3. 后端生成本地文件的URL,将URL保存数据库中。代码如下: ```java @RestController @RequestMapping("/api") public class FileController { @Autowired private FileRepository fileRepository; @Value("${file.base-url}") private String baseUrl; @PostMapping("/upload") public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException { String filename = file.getOriginalFilename(); Path path = Paths.get(uploadDir, filename); Files.write(path, file.getBytes()); String url = baseUrl + filename; fileRepository.save(new FileEntity(filename, url)); return "上传成功"; } } ``` 在 Spring Boot 中,使用 `@Value` 注解获取配置文件中的变量值,即本地文件的URL前缀。 4. 前端从数据库中获取URL,展示文件信息。代码如下: ```html <template> <div> <div v-for="file in files" :key="file.id"> <a :href="file.url">{{ file.name }}</a> </div> </div> </template> <script> import axios from 'axios'; export default { data() { return { files: [] }; }, mounted() { axios.get('/api/files').then(response => { this.files = response.data; }).catch(error => { console.log(error); }); } }; </script> ``` 在 Vue.js 中,使用 `axios` 发送请求获取文件信息,展示文件的名称和URL。需要注意的是,文件的URL需要使用 `<a>` 标签展示,并且需要设置 `href` 属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值