vue2/ElementUi+Springboot+EsayExcel导出表格,“Excel无法打开文件”填坑

以上是症状,实际的关键做法是在发送请求的时候,加上一个参数:responseType:"blob"

网上的做法或者声称的解决方案,好多都没用,所以我整理了一个保姆教程。

因为我是基于vue2+ElementUi做的前端  Maven+springboot做的后台,所以技术栈不契合的,请自行补课。

前端代码:

vue页面:

  <!--elementUi中的按钮组件-->
<el-button type="primary" @click="exportData">导出今日数据</el-button>

vue生命周期中的methods内,完成导出函数

//导入请求api,这里解构的方法是下方调用的方法
import { exportData } from '@/api/orders'

//这个是自定义的文件下载js,下面会贴出完整代码
import { download } from '@/utils/downloadfile'


methods: {

exportData(){
            let _this=this;  
            //api中声明请求后台的方法
            exportData().then(response => {  
             download(response,"data.xlsx");  //启动下载方法,这里的文件名可自定义,但是后缀一定得是“.xlsx”
            })
          } 
}

@/api/orders.js中的请求方法

//网络请求及鉴权组件(elementUi自带)
import request from '@/utils/request'
import { getToken,setToken } from '@/utils/auth' 
const state = {
  token: getToken()
}


//api请求
export function exportData(){
  return request({
    url: '/orders/exportData',//因为要用后台的response返回文件流,所以这个跳转的后台方法返回值要设置为void
    method: 'get',
    responseType:"blob",//我在这里卡了2个多小时,几乎95%以上的教程都没提到需要设置这个参数
    params:{
      token:getToken() //也可在封装的request.js中挂载token,看个人爱好
    }
  })
}

上面还会有个问题,因为ElementUi集成的网络请求,要求服务端返回响应状态码。但是后端方法设置返回值为void,导致框架无法正常获取响应状态码,所以在处理响应的时候,要考虑一下这里的特殊情况!!!

@/unit/request.js

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
  */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  response => {
    const res = response.data;

     //如果没有返回code则直接向调用端返回response.data
     //当然我这里只是做个提醒,相关返回操作您可以根据自己的实际需要,做的更优雅一些
     if (res.code==undefined){
        return res;
       }

   // if the custom code is not 200, it is judged as an error.
    if (res.code !== 200) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      }) 

      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

@/utils/downloadfile.js 下载工具

//主要作用是将返回的文件流以blob的方式,模拟生成下载链接并模拟点击,完成下载
export function download(data, filename) { 
    let blob = new Blob([data], { type: 'application/x-www-form-urlencoded' });
    let blobUrl = window.URL.createObjectURL(blob);
    const aElement = document.createElement('a');
    document.body.appendChild(aElement);
    aElement.style.display = 'none';
    aElement.href = blobUrl;
    aElement.download = filename;
    aElement.click();
    document.body.removeChild(aElement);
}

后台代码:

pom.xml中引入Excel相关依赖

<dependencies>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.6</version>
        </dependency>
    </dependencies>

设置导出实体类,其中继承了Excel设置

注意:该类可以是项目原来的对象类加Excel注解,如果有需要的话,也可以自己写一个导出类,方便导出数据格式化。

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class exportData implements Serializable {

    @ColumnWidth(10)
    @ExcelProperty(value = "序号", index = 0)
    private Integer id; 

    @ColumnWidth(15)
    @ExcelProperty(value = "用户姓名", index = 1)
    private String cusname;

    @ColumnWidth(25)
    @ExcelProperty(value = "用户电话", index = 2)
    private String cusphoneNo;
 
}

这里还有个坑:如果导出的数据中有null值,也会导致Excel导出异常,所以sql语句配置要处理一下null值。

下方为mybatis/Mapper文件中的查询设置:

 <!--(@i:=@i+1) AS 'id'/(SELECT @i:=0) AS itable 这部分是生成行号的操作,因为数据id不一定是连续的,导出的文件中不加行号比较不易阅读-->
 <!--IFNULL(cusphoneNo,'') AS 'cusphoneNo' 将空值转为空字符串,用来填充文件--> 

<select id="toexport" resultType="com.hz.entity.exportData">
      SELECT (@i:=@i+1) AS 'id',cusname AS 'cusname',IFNULL(cusphoneNo,'') AS 'cusphoneNo' from  orders,(SELECT @i:=0) AS itable;
  </select>

后台导出方法

    @GetMapping("/exportData")
    @ApiOperation(value = "导出数据")
    @ResponseBody //实现数据接口
    public void exportData(HttpServletResponse response) {
        try{
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");

            // URLEncoder.encode防止中文乱码
            String filName = URLEncoder.encode("导出的数据表", "utf-8"); 
            response.setHeader("Content-disposition", "attachment;filename=" + filName +".xlsx");
            //用model层处理数据,mybatis做相关数据操作,这里就不细说了,别说你连不上数据库
            List<exportData> etlst =  ordersService.toexport();
            EasyExcel.write(response.getOutputStream(),exportData.class).excelType(ExcelTypeEnum.XLSX).sheet("sheet1").doWrite(etlst);

        }catch (Exception e){
            System.out.println(e.getMessage());
        }

    }

注意的坑:

1、前端请求时一定要带要求返回的类型,即responseType:"blob"

2、因为要用后台的response返回文件流,所以后台导出方法返回值要设置为void,让EasyExcel的write和response输出流进行主动返回。

3、mybatis中的查询及返回给导出类的映射数据不能为空,所以如果可能出现空值的字段,要提前做好处理。

总之,导出的Excel文件无法打开,不是数据格式有问题,就是文件格式有问题,但是照我写的来,基本都能成功。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值