上传EXCEL文件到后端,导入并解析EXCEL的前后端实现(Vue.js + java后端)

版权声明:本文为博主原创文章,未经博主允许不得转载。转载请注明来自我的CSDN博客:链接地址 https://blog.csdn.net/zcyzsy/article/details/84572920

vue.js前端,Java后端,如何导入excel文件,并且解析,本文给了前后端代码的实现,以及完美实践OK之后的分享。

前端主要用了element-ui的upload组件。

关于这个组件的官方文档很少:http://element-cn.eleme.io/#/zh-CN/component/upload ,也没仔细给个完整的demo,所以踩完坑写个完整的博客。

1.VUE前端demo

 <el-upload

          class="upload-demo"
          ref="upload"
          :action="uploadUrl()"
          :data="uploadData"
          name="excelFile"
          :on-preview="handlePreview"
          :on-remove="handleRemove"
          :file-list="fileList"
          :on-error="uploadFalse"
          :on-success="uploadSuccess"
          :auto-upload="false"
          :before-upload="beforeAvatarUpload">
          <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
          <el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">批量导入</el-button>
          <div slot="tip" class="el-upload__tip">只能上传excel文件</div>
          </el-upload>

关于每个字段的意思:

  • uploadUrl() 是后台接口(接受上传的文件并做后端的逻辑处理)

    !!!注意:uploadUrl方法中,直接return的是你的后端URL接口,可以是相对路径,也可以是绝对路径

    取相对路径的时候,会自动加上请求所在页面的前缀htpp://XXX作为请求URL的前缀,如果这样拼接的不对,自己就加上绝对路径:http://localhost:8080/XX 这样类似的URL

  • upLoadData是上传文件时要上传的额外参数,也可以不写,将参数直接带在URL请求中,见下面的demo

    形似: url + "?businessName=" + this.businessName

  • uploadError是上传文件失败时的回掉函数,uploadSuccess是文件上传成功时的回掉函数,

  • beforeAvatarUpload是在上传文件之前调用的函数,可以在这里进行文件类型的判断,对上传格式及大小作限制

  • :auto-upload="false"是停止文件自动上传模式

  • name="excelFile"这里是将导入的EXCEL文件命名,并传给后端,所以后端接口的入参也要是这个名字,下面会贴代码。

  • :file-list="fileList" 显示已上传的文件列表,效果见下图3

  • <el-button slot="trigger" size="small" type="primary">选取文件</el-button>

    <el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">批量导入</el-button>

    是为了实现将选取文件和上传分开,效果如下:

  • 关于上传多个文件

  • 首先是设置是否可以同时选中多个文件上传,这个也是<input type='file'>的属性,添加multiple即可。另外el-upload组件提供了:limit属性来设置最多可以上传的文件数量,超出此数量后选择的文件是不会被上传的。:on-exceed绑定的方法则是处理超出数量后的动作。(但一般单个文件批量导入够用了)代码如下:

    <el-upload      
    multiple     
    :limit="3"     
    :on-exceed="handleExceed">   
    </el-upload>

    上面每个方法对应的实现:

 methods: {
    uploadUrl: function() {
      return (
        "/fanxing/import/batchInsertShops" +
        "?businessName=" +
        this.businessName +
        "&businessStatus=" +
        this.businessStatus +
        "&businessType=" +
        this.businessType
      );
    },
    uploadSuccess(response, file, fileList) {
      if (response.status) {
        alert("文件导入成功");
      } else {
        alert("文件导入失败");
      }
    },
    uploadFalse(response, file, fileList) {
      alert("文件上传失败!");
    },
    // 上传前对文件的大小的判断
    beforeAvatarUpload(file) {
      const extension = file.name.split(".")[1] === "xls";
      const extension2 = file.name.split(".")[1] === "xlsx";
      const extension3 = file.name.split(".")[1] === "doc";
      const extension4 = file.name.split(".")[1] === "docx";
      const isLt2M = file.size / 1024 / 1024 < 10;
         if (!extension && !extension2 && !extension3 && !extension4) {
           alert("上传模板只能是 xls、xlsx、doc、docx 格式!");
          }
          if (!isLt2M) {
           console.log("上传模板大小不能超过 10MB!");
         }
        return extension || extension2 || extension3 || (extension4 && isLt2M);
    },
    submitUpload() {
      if (this.businessType != null) {
        //触发组件的action
        this.$refs.upload.submit();
      }
      if (this.businessType == null) {
        this.businessType = "businessType不能为空";
      }
    },
    handleRemove(file, fileList) {
      console.log(file, fileList);
    },
    handlePreview(file) {
      if (file.response.status) {
        alert("此文件导入成功");
      } else {
        alert("此文件导入失败");
      }
    }
  }

贴上效果图:

2.后端接口的实现

controller层的实现,主要看接口如何定义

@Controller
@RequestMapping(value = "/fanxing/import")
public class ImportController {
    @Resource
    ImportDataService importDataService;

    @RequestMapping(value = "/batchInsertShops", method = RequestMethod.POST)
    @ResponseBody
    public ResultData<Integer> batchInsert(@RequestParam("excelFile") MultipartFile excelFile,
                                           @RequestParam(value = "businessName", required = true ) String businessName,
                                           @RequestParam(value = "businessStatus", required = true) Integer businessStatus,
                                           @RequestParam(value = "businessType", required = true) Integer businessType) throws IOException {
        String name = excelFile.getOriginalFilename();
        if (name.length() < 6 || !name.substring(name.length() - 5).equals(".xlsx")) {
            return ResultDataBuilder.failWithNull("文件格式错误", ResultCode.FILE_FORMAT_ERROR.getCode());
        }
        //TODO 业务逻辑,通过excelFile.getInputStream(),处理Excel文件
        ExcelUtils.excelToShopIdList(excelFile.getInputStream());
    }
}

后端如何解析Excel文件

package com.dianping.fanxing.system.admin.web.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ExcelUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExcelUtils.class);
    public static List<Integer> excelToShopIdList(InputStream inputStream) {
        List<Integer> list = new ArrayList<>();
        Workbook workbook = null;
        try {
            workbook = WorkbookFactory.create(inputStream);
            inputStream.close();
            //工作表对象
            Sheet sheet = workbook.getSheetAt(0);
            //总行数
            int rowLength = sheet.getLastRowNum() + 1;
            //工作表的列
            Row row = sheet.getRow(0);
            //总列数
            int colLength = row.getLastCellNum();
            //得到指定的单元格
            Cell cell = row.getCell(0);
            for (int i = 1; i < rowLength; i++) {
                row = sheet.getRow(i);
                for (int j = 0; j < colLength; j++) {
                    cell = row.getCell(j);
                    if (cell != null) {
                        cell.setCellType(Cell.CELL_TYPE_STRING);
                        String data = cell.getStringCellValue();
                        data = data.trim();
                        if (StringUtils.isNumeric(data))
                            list.add(Integer.parseInt(data));
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse excel file error :", e);
        }
        return shopIds;
    }
}

3.汉字传输的乱码问题

关于前端输入汉字,传到后端的controller层乱码问题,确定前端页面设置的是UTF-8

原因:Spring MVC 是基于Servlet,在Http请求到达Servlet解析之前,GET过来的URL已经被Tomcat先做了一次URLDecode。

Tomcat对GET方式默认的URL解码结果是iso-8859-1而不是UTF-8!

解决办法1:

decodeFName = new String(fName.getBytes("iso-8859-1"),"utf-8");

解决方法2:

进入Tomcat的安装目录下,conf目录下找到server.xml文件,配置如下,主要添加 URIEncoding="UTF-8"就好了

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               URIEncoding="UTF-8" />

建议第二种,一劳永逸

参考链接:https://segmentfault.com/a/1190000013796215#articleHeader5

主要遇到的问题:

1.前端组件使用问题

2.前端请求的相对路径,被upload组件自动补全了前缀,导致请求到不了后端

3.post请求的汉字到了后端的controller层乱码。

至此,前后端联调完毕,done。后端的去写前端真的心累,此文记录下这两天的摸索吧。

猜你喜欢

转载自blog.csdn.net/zcyzsy/article/details/84572920
今日推荐