项目场景:
需求:后端使用 Java 前端使用 Vue 实现Excel文件下载
问题1 Excel文件下载乱码
前端 axios 请求代码 ,
大部分人添加 responseType: 'blob'乱码问题就已经解决了
但是我的问题还不在此…
export function download(param) {
return fetch({
url: '/api/serve/Excel/'+param+'/download',
method: 'post',
responseType: 'blob' // 解决乱码问题
})
}
前端 调用 axios 并处理返回结果代码
downloadFile() {
download(this.chickId.id).then(res => {
let blob = new Blob([res], {
type: 'application/octet-stream'
});
var filename = this.chickId.processName;
let url = window.URL.createObjectURL(blob)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', filename + '.xls')
document.body.appendChild(link)
link.click()
window.URL.revokeObjectURL(link.href) //释放url
document.body.removeChild(link) //释放标签
this.$message({
showClose: true,
message: '文件开始下载',
type: 'success'
});
},error=>{
this.$message({
showClose: true,
message: '>_< 下载失败 !',
type: 'error'
});
})
},
后端代码
public HttpServletResponse downloadFileType(String filePath,HttpServletResponse response,String contentType)
throws IOException {
OutputStream outPutStream = null;
try {
// filePath是指欲下载的文件的路径。
File file = new File(filePath);
// 取得文件名。
String filename = file.getName();
// 以流的形式下载文件。
InputStream fis = new BufferedInputStream(new FileInputStream(filePath));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
response.addHeader("Content-Type", contentType);
response.addHeader("Content-Disposition",
"attachment; filename=\"" + new String(filename.getBytes(StandardCharsets.UTF_8),
StandardCharsets.ISO_8859_1) + "\"");
response.flushBuffer();
outPutStream = response.getOutputStream();
outPutStream.write(buffer);
outPutStream.flush();
outPutStream.close();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (outPutStream != null) {
outPutStream.close();
}
}
return response;
}
问题描述
前端下载的Excel文件出现乱码数据:
问题2 No ‘Access-Control-Allow-Origin’ header is present on the requested:
测试发现 使用 Postman 请求下载文件是正常的,但通过 axios 得到的就是乱码文件,一开始便排除了后端导致乱码问题。
尝试修改前端代码URL改成具体地址'http://192.168.6.66:8000/api/serve/Excel/'+param+'/download'
此时页面又出现一个跨域问题No 'Access-Control-Allow-Origin' header is present on the requested
``
export function download(param) {
return fetch({
url: 'http://192.168.6.66:8000/api/serve/Excel/'+param+'/download',
method: 'post',
responseType: 'blob'
})
}
后端添加请求头可解决跨域问题
response.addHeader("Access-Control-Allow-Origin","*");
到这里Excel文件乱码问题就解决了
,但是维护起来比较麻烦而且不支持微服务及负载均衡。
排查到此 指定地址下载文件不会出现乱码,那么是否表明乱码问题并非前端引起的?
在后端排查 因项目中使用了SpringCloud Gateway 网关服务
如项目未使用基本可以不用往下看了
前端页面正常下载响应长度
content-length: 1188864
前端页面乱码下载响应长度
content-length: 1594920
在 GatewayFilterChain
过滤中测试
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
// 释放掉内存
DataBufferUtils.release(join);
String json = new String(content, StandardCharsets.UTF_8);
originalResponse.getHeaders().setContentLength(json.getBytes().length);
System.out.println("==============json: "+json.getBytes().length+"===============");
System.out.println("==============content: "+content.length+"===============");
return bufferFactory.wrap(json.getBytes());
这里对 content 进行转换 String json = new String(content, StandardCharsets.UTF_8); 也是就是导致Excel下载乱码
这里是一位离职的同事写的,所以也不清楚为什么需要转换为String,有知道的小伙伴可以在下方评论一下。
控制台输出内容
==============json: 1594920===============
==============content: 1188864===============
问题3 net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)**
将返回结果替换成
content
,本以为就结束了。此时在页面下载控制台提示:net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)
是的,又一个新问题出现了。
return bufferFactory.wrap(content);
整理一下百度后
net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)
错误原因可能在两个方面
1.使用nginx做反向代理可能是因为header过大
2.因为没有权限,导致了请求失败,被拒绝。
我的是第三种
3.响应长度content-length
与实际 length不符
解决方案:
设置内容长度
json.getBytes().length
替换成content.length
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
// 释放掉内存
DataBufferUtils.release(join);
originalResponse.getHeaders().setContentLength(content.length);
return bufferFactory.wrap(content);
又是找BUG的一天,到这里就结束了!