在spring boot中间开发,我们经常会遇到需求要集成Excel的上传与下载,所以我们要集成excel功能,一般我们常用的就是集成POI组件。
这里不介绍如何集成POI组件和怎么生成excel的方法。而是在下载excel后,excel打不开的问题。
这一次我在spring cloud项目中就遇到生成了excel文件,然后下载到本地,打开不开,文件被损坏,而且通过修复工具无法修复。
问题复现:
1)最后下载Excel文件生成字节码,然后定义header信息,然后返回给reposnse流。
header:
headers.setContentDispositionFormData("attachment",
new String(("数据" + name + dateFormat.format(new Date()) + ".xls").getBytes("UTF-8"), "iso-8859-1"));
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
最后形成的文件流如下:
return new ResponseEntity<byte[]>(baos.toByteArray(), headers, HttpStatus.CREATED);
2) 集成到项目中,发现下载下来,文件打不开,而且通过wps修复工具,也无法完成修复。
3) 所以怀疑是不是业务数据输出不正确的问题,故删减Excel的数据,进行验证。发现问题同样存在。
4)为了干的彻底,直接生成空excel表,发现同样打不开。那和业务逻辑没有关系。
5)另外开一个spring boot demo项目,同样进行下载,发现能够打开,而且数据正常。
6)所以怀疑集成到当前项目的问题,一定是当前项目配置中对输出流格式有定义。
7)然后对spring cloud的数据设置进行查询,发现所有配置都正确(这里过程略,从流的config里面的定义)
8)既然当前项目里面的配置都是正确,为什么下载打不开呢?
9)然后开始对比下载后的文件,发现都是空文件,能够正常打开,数据是4096,而不正常数据只有4093,那问题非常明显,是这个项目把数据删减或修改了。
10)使用二进制工具,对比下这两个excel文件,发现数据完全不一样。故非常肯定是输出流问题。
11) 从生成excel的字节流对比,然后debug跟踪到response ,发现流没有改变。
12) 所以只能从spring cloud项目中来查看原因。
13) 然后从网关查询,突然发现是网关转换了流,网关在进行了一个日志拦截,对所有的流进行日志记录。
@Component
public class PrintRequestLogFilter extends ZuulFilter {
private static Logger logger = LoggerFactory.getLogger(PrintRequestLogFilter.class);
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
try{
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
InputStream in = request.getInputStream();
String reqBody = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
Map<String, String[]> map = request.getParameterMap();
if (map != null) {
StringBuilder sb = new StringBuilder();
sb.append("request parameters:\t");
for (Map.Entry<String, String[]> entry : map.entrySet()){
sb.append("[" + entry.getKey() + "=" + printArray(entry.getValue()) + "]");
}
logger.info(sb.toString());
}
//打印json请求参数
if (reqBody != null){
logger.info("request body:\t" + reqBody);
}
//打印response
InputStream out = ctx.getResponseDataStream();
String outBody = StreamUtils.copyToString(out, Charset.forName("UTF-8"));
if (outBody != null){
logger.info("response body:\t" + outBody);
}
ctx.setResponseBody(outBody);
}catch (IOException e){
logger.error("ZUUL日志信息输出异常:" , e);
}
return null;
}
发现这里有一个大坑,这个大坑就是把所有的输出流给转换了。
问题找到了就好解决了。
从这个流的header里面如果是文件,就不要按照这个流进行转换了,文件下载就恢复正常了。