需求描述:
需要完成这样一个功能:后台通过poi生成excle,前台点击按钮可直接下载。
代码逻辑(核心部分):
第一种:
public String generatePlanExcel(@RequestParam(value = "planId") int planId, HttpServletRequest request, HttpServletResponse response) throws Exception{
// 1.创建新的Excel工作簿(workbook)
// 1.1 07版本的Excel需要XSSFWorkbook对象
Workbook workbook = new XSSFWorkbook();
// 2.使用workbook创建sheet
// 2.1在Excel工作簿中建一工作表(sheet),其名为缺省值 Sheet0
//Sheet sheet = workbook.createSheet();
// 2.2如要新建一名为"预案详细信息"的工作表,其语句为:
Sheet sheet = workbook.createSheet("预案详细信息");
...
String fileName = planRecordAndResources.getName() + ".xlsx";
ByteArrayOutputStream os = new ByteArrayOutputStream();
workbook.write(os);
byte[] content = os.toByteArray();
InputStream is = new ByteArrayInputStream(content);
// 设置response参数,可以打开下载页面
response.reset();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName,"utf-8"));
response.setHeader("Content-Length", String.valueOf(is.available()));
response.setCharacterEncoding("UTF-8");
ServletOutputStream sout = response.getOutputStream();
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(is);
bos = new BufferedOutputStream(sout);
byte[] buff = new byte[2048];
int bytesRead;
// Simple read/write loop.
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
} catch (Exception e) {
logger.error("导出excel出现异常:", e);
} finally {
if (bis != null){
bis.close();
}
if (bos != null){
bos.close();
}
}
...
}
第二种:
public String generatePlanExcel(@RequestParam(value = "planId") int planId, HttpServletRequest request, HttpServletResponse response) throws Exception{
// 1.创建新的Excel工作簿(workbook)
// 1.1 07版本的Excel需要XSSFWorkbook对象
Workbook workbook = new XSSFWorkbook();
// 2.使用workbook创建sheet
// 2.1在Excel工作簿中建一工作表(sheet),其名为缺省值 Sheet0
//Sheet sheet = workbook.createSheet();
// 2.2如要新建一名为"预案详细信息"的工作表,其语句为:
Sheet sheet = workbook.createSheet("预案详细信息");
//临时文件
File tempFile = null;
try {
//要保存的文件名
String filename = planRecordAndResources.getName() + ".xlsx";
//要保存的根目录
String rootDir = request.getSession().getServletContext().getRealPath("/");
//要保存的目录路径
String path = rootDir + File.separator + "tempfile";
File saveDir = new File(path);
if (!saveDir.exists()) {
saveDir.mkdirs();// 如果文件不存在则创建文件夹
}
//文件路径
path = path + File.separator + filename;
//初始化临时文件
tempFile = new File(path);
//输出流
OutputStream out = new FileOutputStream(tempFile);
try {
//保存文件
workbook.write(out);
} catch (IOException e) {
e.printStackTrace();
}
out.close();
// 以流的形式下载文件。
BufferedInputStream fis = new BufferedInputStream(new FileInputStream(path));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName,"utf-8"));
response.setHeader("Content-Length", "" + tempFile.length());
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
toClient.write(buffer);
toClient.flush();
toClient.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (tempFile != null && tempFile.exists()) {
tempFile.delete();// 删除临时文件
}
}
问题描述:
接口写完之后,通过swagger和postman进行测试发现文件无法正常打开。
下载的文件是这样的:
用WPS打开该文件是这样的:
用office Excel打开该文件是这样的:
刚开始感觉是自身代码出现了问题,但是通过第二种先生成临时文件的方式实现excel下载,我在本地打开临时生成的excel文件是没有问题的,通过流的方式转换和下载之后就仍然出现上面的问题。于是乎感觉可能是response设置的问题,便开始了长达2小时的百度求知。
查到的解决方式很多都是重复的,大致上可以分为:
1.说response中必须有这行设置
response.setHeader("Content-Length", fis.available()+"");
2.说创建xls与xlsx工作薄的不同
导出xlsx格式,创建工作薄的时候用:
Workbook workbook = new XSSFWorkbook();
导出xls格式,创建工作薄的时候用:
Workbook workbook = new HSSFWorkbook();
3.说response.setContentType设置的不同
导出xlsx格式设置ContentType需要:
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
导出xls格式设置ContentType需要:
response.setContentType("application/vnd.ms-excel");
在网上找到的所有的解决方法都尝试仍然无效之后,找到了这篇博客
使用httpServletResponse和POI写入excle时标题和内容乱码的问题解决
发现,如果单纯没有和前台对接的前提下,使用swagger或者postman进行接口测试,无论怎么修改response的header都没有办法下载出正常的excel文件,但是通过在浏览器中直接输入url的方式,就得到了正常的可打开且格式正确的excel。
至于为何通过swagger和postman无法下载处正常的可打开的excel文件望知情大佬指证。