项目场景:

需求:后端使用 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文件出现乱码数据:

axios 下载excel文件 乱码_java


问题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的一天,到这里就结束了!