springboot easyexcel export excel case and file cannot be opened


  Exporting data is one of the back-end classic modules. From the original poi to the current easyexcel, etc., they are all working hard to help developers narrow the gap between data and excel. But in the simple export, you will also encounter some problems, so write an article to record it~

background introduction

The general export flow chart is as follows:

组装数据
导出

Assembling data: including table header, data and style
export in excel: file flow

easyexcel header and data

The official documents summarize the classics~

Header: It is divided into fixed header and dynamic header, and then you can continue to divide simple version and complex version. Among them, the complex version has three or four levels of headers (previously write poi hard-coded to explode=_=||)~ An example is as follows ( picture source ):
insert image description here
Data: Changes with the header

Header

Simple

insert image description here

To fix the header , you can declare an entity class for definition, as follows:

@Data
public class TitleData {
    
    
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
}

Dynamic table headers can only be defined by manually writing code, as follows:

// 外层数组,一个值代表一列
List<List<String>> headList = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("字符串标题");
headList.add(head0);

List<String> head1 = new ArrayList<String>();
head1.add("日期标题");
headList.add(head1);

List<String> head2 = new ArrayList<String>();
head2.add("数字标题");
headList.add(head2);

merge

insert image description here

@Data
public class ComplexHeadData {
    
    
    @ExcelProperty({
    
    "主标题", "字符串标题"})
    private String string;
    @ExcelProperty({
    
    "主标题", "日期标题"})
    private Date date;
    @ExcelProperty({
    
    "主标题", "数字标题"})
    private Double doubleData;
}

web project export method

There are generally two export methods for web projects:

  • Provide a download link (asynchronous): first generate excel and upload it to the oss/file server, return the file link to the front end, and the front end will download it by itself
  • File stream (synchronous): generate excel and stuff it into the response stream

This article mainly focuses on the file streaming method. The sample code is as follows:

// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());

Real knowledge comes from practice

Simple dynamic header case

This case is relatively simple, dynamically define the table header and create data according to the request parameter time, and return it to the front end in the form of a file stream. The effect is as follows:
renderings
Add dependencies to pom:

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>2.2.7</version>
</dependency>

The business logic code is as follows:

@RestController
public class ResultController {
    
    
    
	@PostMapping(value = "/export")
    public void export(@RequestParam Integer time) {
    
    
    
    	try {
    
    
			HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
			response.setHeader("Content-Disposition", "attachment; filename=export.xlsx");
	        // 响应类型,编码
	        response.setContentType("application/vnd.ms-excel;charset=utf-8");
	        response.setCharacterEncoding("utf-8");
			EasyExcel.write(response.getOutputStream()).head(getHead(time)).sheet("数据").doWrite(getData(time));
		} catch (IOException e) {
    
    
			log.error("导出问卷数据失败!错误信息为:{}", e.getMessage());
			e.printStackTrace();
		}
    }
    
    /**
     * 获取excel标题栏(姓名、手机号、提交时间)
     * @param questionnaireId
     * @return
     */
    private List<List<String>> getHead(Integer time) {
    
    
    	
    	// 外层数组,一个值代表一列
    	List<List<String>> headList = new ArrayList<List<String>>();
    	List<String> nameList = new ArrayList<String>();
    	nameList.add("姓名");
    	headList.add(nameList);
    	
    	List<String> telList = new ArrayList<String>();
    	telList.add("手机号");
    	headList.add(telList);
    	
    	List<String> submitDTList = new ArrayList<String>();
    	submitDTList.add("提交时间");
    	headList.add(submitDTList);
    	
    	for (int i = 0 ; i < time ; i ++ ) {
    
    
    		List<String> list = new ArrayList<String>();
    		list.add("动态标题" + i);
        	headList.add(list);
    	}
    	
    	return headList;
    }
    
    /**
	 * 获取excel表格数据
	 * @return
	 */
	private List<List<Object>> getData(Integer time) {
    
    
		
		// 将填写结果 + 提交时间合并为一行数据
		List<List<Object>> resultList = new ArrayList<List<Object>>();
		for (int i = 0 ; i < 3 ; i ++ ) {
    
    
			
			List<Object> list = new ArrayList<Object>();
			list.add("花卷" + i);
			list.add("1347000000" + i);
			list.add("2022-01-18 14:54:00");
			
			for (int j = 0 ; j < time ; j ++ ) {
    
    
	    		list.add("动态内容" + j);
	    	}
			
			resultList.add(list);
		}
		return resultList;
	}
  
}

problems encountered

Excel cannot be opened during front-end joint debugging

Description of the problem: Debugging with postman in the background is ok, and excel can be opened normally! However, the excel downloaded during front-end debugging prompts that it is damaged and cannot be opened! ! !
Tips: Brave (not afraid of death) questioning the front end, your code has a BUG!
solution: responseType: blobThe front end needs to add settings in request and response

( The following pseudo code, please focus on the responseType setting )

  1. The front-end request should setresponseType为arraybuffer或blob
return request({
    
    
url: '/platform/export',
method: 'post',
responseType: 'blob',
headers: {
    
    
    'Content-Type': 'application/x-www-form-urlencoded'
},
data: qs.stringify(data)

})

  1. The front end receives the response in blob format and sets the type toapplication/msexcel
handleExport(){
    
    
  exportData({
    
    id: this.id}).then(res => {
    
    
    if(res){
    
    
      const fileName = this.name + '.xlsx';
        var blob = new Blob([res], {
    
    
        type: "application/msexcel;charset=utf-8",
      });
      const URL = window.URL || window.webkitURL;
      const downloadElement = document.createElement("a");
      const href = URL.createObjectURL(blob); // 创建下载的链接
      downloadElement.href = href;
      downloadElement.download = fileName; // 下载后文件名
      document.body.appendChild(downloadElement);
      downloadElement.click(); // 点击下载
      document.body.removeChild(downloadElement); // 下载完成移除元素
      URL.revokeObjectURL(href); // 释放掉blob对象
    }
  })
},

The download file interface sometimes returns file stream and sometimes returns json

The test lady brought up an order. After the token timed out, clicking the download button can still export normally. The excel content is the error code json string returned by the back-end authentication system =_=||. I searched it online and discussed it with the big guys. Next, it is not advisable to encapsulate the file stream into a unified return json method set by the system, and finally hold the thin legs of the front-end lady to find a solution. Solution
: After the front-end obtains the response, it content-typeis distinguished
Online reference documents
insert image description here



To be continued...

Guess you like

Origin blog.csdn.net/huhui806/article/details/122561103