• Springboot和vue实现文件导入



    前言

    文件导入时大多数项目无法回避的问题,这两天深入学习了文件导入,在这里进行记录,使用到的技术是Springboot+Vue,前端组件使用el-upload


    一、Java后端使用MultipartFile

    @PostMapping(value = "/upload")
        public String upload(@RequestParam("file") MultipartFile multipartFile) {
            return FileUploadUtil.upload(multipartFile);
        }
    
    • 1
    • 2
    • 3
    • 4

    如果使用Springboot架构,直接使用MultipartFile工具即可,后端拿到MultipartFile对象之后,对其进一步处理就能拿到数据,或者存入数据库,或者保存到本地都可以。

    二、Java后端直接从request中读取并转换为字符串

    	Part importFile = request.getPart("file");
    	InputStream inputstream = importFile.getInputStream();
    	BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputstream, "UTF-8"));
    	StringBuilder stringBuilder = new StringBuilder();
    	String inputStr;
    	while ((inputStr = streamReader.readLine()) != null) {
    	    stringBuilder.append(inputStr);
    	}
    	String s = stringBuilder.toString();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    直接从request中读取需要使用Part类,从request中根据名称获取到part对象,然后再转换为流的形式,之后使用BufferedReader流读取器,逐行读取文件内容并添加到字符串构造器中,生成字符串。

    二、Java后端直接从request中读取并存入本地文件

    HttpServletRequest request = context.getRequest();
    FileOutputStream fos = new FileOutputStream("C:\\Users\\Junhao\\Desktop\\import.json");
    byte[] buffer = new byte[1024];
    int len;
    Part file = request.getPart("file");
    InputStream inputstream = file.getInputStream();
    
    while ((len = inputstream.read(buffer)) != -1){
        fos.write(buffer, 0, len);
    }
    fos.close();
    inputstream.close();
    String responseString = readInputStream(inputstream);
    System.out.println(responseString);
    BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputstream, "UTF-8"));
    StringBuilder stringBuilder = new StringBuilder();
    String inputStr;
    while ((inputStr = streamReader.readLine()) != null) {
        stringBuilder.append(inputStr);
    }
    String s = stringBuilder.toString();
    Object parse = JSON.parse(s);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    二、前端使用el-upload

    由于要求在上传之前进行检验,然后根据检验的结果,对于冲突的实体,逐项选择覆盖已有实体,或者使用原来实体,这相对于单纯的文件上传,提高了难度

    1.el-upload使用

       <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
          <el-upload
            ref="upload"
            :limit="1"
            accept=".json"
            :headers="upload.headers"
            :action="upload.url"
            :disabled="upload.isUploading"
            :on-progress="handleFileUploadProgress"
            :on-success="handleFileSuccess"
            :on-change="handleChange"
            :before-remove="handleRemove"
            :auto-upload="false"
            drag
            :data="upload.uploadData"
          >
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">
              将文件拖到此处,或
              <em>点击上传</em>
            </div>
            <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“json”格式文件!</div>
          </el-upload>
            <div v-if="showImportCheckRes" style="margin-top: 8px">
              <el-tabs active-name="thing">
                  <el-tab-pane name="thing" label="事物" style="height: 130px;" class="scrollbar">
                    <el-scrollbar style="height: 100%">
                      <el-form label-position="left">
                        <div v-for="item in importCheckRes.existThings">
                          <el-form-item :label="item.code" label-width="160px">
                            <el-radio-group v-model="item.value">
                              <el-radio :label="0">暂不导入</el-radio>
                              <el-radio :label="1">覆盖</el-radio>
                            </el-radio-group>
                          </el-form-item>
                        </div>
                      </el-form>
                    </el-scrollbar>
                  </el-tab-pane>
                  <el-tab-pane name="thingTemplate" label="事物模板" style="height: 130px;" class="scrollbar">
                    <el-scrollbar style="height: 100%">
                      <el-form label-position="left">
                        <div v-for="item in importCheckRes.existThings">
                          <el-form-item :label="item.code" label-width="160px">
                            <el-radio-group v-model="item.value">
                              <el-radio :label="0">暂不导入</el-radio>
                              <el-radio :label="1">覆盖</el-radio>
                            </el-radio-group>
                          </el-form-item>
                        </div>
                      </el-form>
                    </el-scrollbar>
                  </el-tab-pane>
                <el-tab-pane name="dataModel" label="数据模型" style="height: 130px;" class="scrollbar">
                  <el-scrollbar style="height: 100%">
                    <el-form label-position="left">
                      <div v-for="item in importCheckRes.existDataModels">
                        <el-form-item :label="item.code" label-width="160px">
                          <el-radio-group v-model="item.value">
                            <el-radio :label="0">暂不导入</el-radio>
                            <el-radio :label="1">覆盖</el-radio>
                          </el-radio-group>
                        </el-form-item>
                      </div>
                    </el-form>
                  </el-scrollbar>
                </el-tab-pane>
                <el-tab-pane name="modelTag" label="模型标签" style="height: 130px;" class="scrollbar">
                  <el-scrollbar style="height: 100%">
                    <el-form label-position="left">
                      <div v-for="item in importCheckRes.existModelTags">
                        <el-form-item :label="item.code" label-width="160px">
                          <el-radio-group v-model="item.value">
                            <el-radio :label="0">暂不导入</el-radio>
                            <el-radio :label="1">覆盖</el-radio>
                          </el-radio-group>
                        </el-form-item>
                      </div>
                    </el-form>
                  </el-scrollbar>
                </el-tab-pane>
              </el-tabs>
            </div>
          <div slot="footer" class="dialog-footer">
            <el-button type="primary" @click="submitImport" size="mini">确 定</el-button>
            <el-button @click="upload.open = false" size="mini">取 消</el-button>
          </div>
        </el-dialog>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    2.on-change验证文件内容

        handleChange(file){
          if (this.importStatus === 1){
            return;
          }
          let that = this
          let raw = file.raw
          const reader = new FileReader()
          reader.readAsText(raw, 'UTF-8')
          reader.onload=async function(evt){
            let dataJson = JSON.parse(evt.target.result)
            const Entities = dataJson.Entities
            const entityCode = {}
            Object.keys(Entities).forEach(item=>{
              const tempArray = []
              Object.values(Entities[item])[0].forEach(i=>{
                tempArray.push(i.code)
              })
              that.$set(entityCode, item, JSON.parse(JSON.stringify(tempArray)))
            })
            that.$nextTick(()=>{
              importCheck(entityCode).then(res=>{
                that.importCheckRes = res.data
                that.showImportCheckRes = true
              })
            })
          }
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在前端先解析文件,读取JSON数据,然后将要导入的code发送到后端,返回哪些是已有的,然后在前端进行覆盖或者暂不导入的选择,选择完成之后点击确定,携带选择的结果进行导入

        submitImport() {
          const tempJson = JSON.parse(JSON.stringify(this.importCheckRes))
          const importCheckRes = {}
          Object.keys(tempJson).forEach(item=>{
            const tempArray = []
            tempJson[item].forEach(i=>{
              if (i.value === 1){
                tempArray.push(i.code)
              }
            })
            this.$set(importCheckRes, item, tempArray);
          })
          this.$set(this.upload, 'uploadData', { importCheckRes: JSON.stringify(importCheckRes) })
          this.$nextTick(()=>{
            this.$refs.upload.submit()
          })
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3、效果截图

    在这里插入图片描述


    总结

    这两天的项目中,学习了Java导出数据,其中遇到坑及总结如下:

    1. 导出时响应函数返回值必须为void。
    2. 前端也需要进行处理,使用vue的this.download()即可。
    3. 如果是普通的Springboot架构,导入可以直接使用MultipartFile。
    4. 如果不能使用MultiPartFile,那么可以使用Part从request.getPart(“fileName”)的方式获取part,然后进一步解析part获取文件内容。
    5. 后端可以将读取到的文件内容转换为字符串,使用BufferedRead和StringBuilder即可。
    6. 前端可以读取文件的内容,使用FileReader和reader.onload()即可。
    7. 如果如果单纯验证文件格式、大小或者名称是否正确,来判断是否终止文件上传任务,那么建议使用before-upload(如果返回false,或者promise返回reject,文件上传立即终止,并删除刚添加的文件)。如果需要对提交内容进行验证,根据验证结果做下一步操作,那么使用el-upload的on-change事件。
  • 相关阅读:
    Error occurred while trying to proxy request项目突然起不来了
    【DL with Pytorch】第 5 章 :风格迁移
    AlexNet学习实现花的种类识别
    详解 Flink CDC 的介绍和入门案例
    C++11闭包函数的几种实现方法
    计算机毕业设计选题推荐-房屋租赁系统-Java/Python项目实战
    navicat 导入dmp文件
    CAE行业再添神器,CAE产品组件套包CEETRON SDKS全新发布
    mediasoup-client的H5在ios的微信内置浏览器上无法视频通话,报错device not supported
    【C语言】文件的操作与文件函数的使用(详细讲解)
  • 原文地址:https://blog.csdn.net/qq_43403676/article/details/126924324