Java二进制zip,excel文件流到前端时,修改jQuery接受二进制数据。转文件后提示文件损坏(不可预料的压缩文件末端)处理

前言

在互联网上查找了各种资料,大部分资料都说是后端的流关闭顺序不正确。反复试验关闭顺序。发现文件下载到前端一致报这个错误。有的说是前端接收类型要加上responseType:‘arraybuffer’或者‘blob’,但是添加上后仍然不正确。

2022年08月03日发现主要问题可能是因为前端jQuery接受二进制数据流的处理不正确。

Java二进制zip,excel文件流到前端时,修改jQuery接受二进制数据。转文件后提示文件损坏(不可预料的压缩文件末端)处理_第1张图片

原因

我使用的是jQury的ajax,向后端调用接口,可以调用成功,后端返回的是byte[] 文件流(下附后端代码)。调试的时候发现接收到的data数据是string类型,并且显示接收到的数据344KB,明显比我发送的数据122KB多,这个问题很值得深入研究。
在浏览器中直接访问后端接口,可以下载成功,打开后不报错,于是现在可以明确的定位问题到前端了,但是前端代码可以发送请求成功,也可以接收到返回的数据。说明代码是没问题的?但是为什么接收的数据不正确呢?
有可能是公司框架不支持接收二进制流数据,所以强制将二进制流数据转换为了文本数据。
Java二进制zip,excel文件流到前端时,修改jQuery接受二进制数据。转文件后提示文件损坏(不可预料的压缩文件末端)处理_第2张图片

@Controller
@RequestMapping(value="/BPTiwInvoiceManage")
@MultipartConfig
public class InvoiceManagerExportZip {
    @RequestMapping(value ="exportzip", method = { RequestMethod.GET ,RequestMethod.POST})
    public void exportZip(@RequestParam(value = "filterList",required = false) String filterString,HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException {
        ZipFileService zfs= new ZipFileService();
        byte[] bytesFile = zfs.getSqlCompress("", true);
        response.reset();
        response.setHeader("Content-Disposition","attachment;fileName="+"test.zip");
        response.addHeader("Content-Length", ""+bytesFile.length);
        response.setContentType("application/octet-stream;charset=UTF-8");
        ServletOutputStream outputStream = response.getOutputStream();
        //new ReturnZip("test.zip",bytesFile).getData()
        IOUtils.write(bytesFile, outputStream);
        response.flushBuffer();

    }}

两种解决方案

一、采用原生的ajax方法接受二进制数据

我尝试了一下,使用原生的ajax去调用接口(大部分情况下,都是用jQuery,原生的还是挺陌生的/(ㄒoㄒ)/~~)。经过反复锤炼代码,最终将代码完善。很重要的一步就是一定要添加xhr.responseType = ‘blob’;,成功下载了文件,调试的时候接收的流数据无法打印(如下图),意味着成功的接收了正确的二进制流数据。打开也不报错。
Java二进制zip,excel文件流到前端时,修改jQuery接受二进制数据。转文件后提示文件损坏(不可预料的压缩文件末端)处理_第3张图片

代码如下:

var url ="http://www.xgp.com:5200/BPTiwInvoiceManage/exportzip";  //要打开的网页地址
// 发送ajax 请求 需要 五步

// 1、异步对象
var xhr = new XMLHttpRequest();
// 2、设置属性.responseType一定要设置。
xhr.open('post', url);
xhr.responseType = 'blob';
// 如果想要使用post提交数据,必须添加此行
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// 3、将数据通过send方法传递
xhr.send("filterList=" + JSON.stringify(filterList));
// 发送并接受返回值
xhr.onreadystatechange = function () {
    // 5、获取异步调用返回的数据。
    if (xhr.readyState == 4 && xhr.status == 200) {
        //alert(xhr.responseText);二进制流下无法打印
        var type = xhr.getResponseHeader('Content-Type');
        var blob = new Blob([this.response], {type: type});
        var URL = window.URL || window.webkitURL;
        var objectUrl = URL.createObjectURL(blob);
        var fileName = decodeURI(xhr.getResponseHeader("Content-Disposition").split("filename=")[1]);
        if (fileName) {
          	var a = document.createElement('a');
          	if (typeof a.download === 'undefined') {
            	window.location = objectUrl;
          	} else {
            	a.href = objectUrl;
            	a.download = fileName;
            	document.body.appendChild(a);
            	a.click();
            	a.remove();
          	}
        } else {
          		window.location = objectUrl;
        }
    }
};

二、

再次寻找资料,发现jQuery的AJAX默认不接收二进制数据,需要其他的参数配合。默认的jQuery的dataType只能设置以下选项,不能设置blob,所以我们需要自己进行改动。
在这里插入图片描述

增加以下属性

    processData: false,
	dataType: 'binary',
	xhrFields: { responseType: "blob" },

最佳实践:

       $.ajax({
            url: downloadZipUrl, //Server script to process data
            type: 'POST',
            data: "123456789(你的数据)",  // Form data
            cache: false,  //Options to tell jQuery not to process data or worry about content-type.
            contentType: 'application/json',
            processData: false,
			dataType: 'binary',
			xhrFields: { responseType: "blob" },
			success(data, textStatus, xhr) {
				alert('请求成功');
				var type = xhr.getResponseHeader('Content-Type');
				var blob = new Blob([data], {type: type});
				var URL = window.URL || window.webkitURL;
				var objectUrl = URL.createObjectURL(blob);
				var fileName = decodeURI(xhr.getResponseHeader("Content-Disposition").split("filename=")[1]);
				if (fileName) {
					  var a = document.createElement('a');
					  if (typeof a.download === 'undefined') {
						window.location = objectUrl;
					  } else {
						a.href = objectUrl;
						a.download = fileName;
						document.body.appendChild(a);
						a.click();
						a.remove();
					  }
				} else {
					window.location = objectUrl;
				}

            },
            error(jqXHR, textStatus, errorThrown) {
				console.log('fail');
            }
        });

最后附上Java后端的压缩代码:


import org.apache.commons.io.IOUtils;
/*
	*把sourceFilePath路径下的文件压缩并将压缩包返回浏览器响应response
*/

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class FileZipUtil {

    public static void exportZip(HttpServletResponse response, String sourceFilePath) {
        //文件名以时间戳作为前缀
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String filePrefix = sdf.format(new Date());
        String downloadName = filePrefix + ".zip";
        //将文件进行打包下载
        try {
            OutputStream out = response.getOutputStream();
            //接收压缩包字节
            byte[] data = createZip(sourceFilePath);
            deleteFile(new File(sourceFilePath));
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Expose-Headers", "*");
            response.setHeader("Content-disposition", "attachment;filename=" + downloadName);
            response.addHeader("Content-Length", "" + data.length);
            response.setContentType("application/octet-stream;charset=UTF-8");
            IOUtils.write(data,out);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static void handlerFile(ZipOutputStream zip, File file, String dir) throws Exception {
        //如果当前的是文件夹,则进行进一步处理
        if (file.isDirectory()) {
            //得到文件列表信息
            File[] fileArray = file.listFiles();
            if (fileArray == null) {
                return;
            }
            //将文件夹添加到下一级打包目录
            zip.putNextEntry(new ZipEntry(dir + "/"));
            dir = dir.length() == 0 ? "" : dir + "/";
            //递归将文件夹中的文件打包
            for (File f : fileArray) {
                handlerFile(zip, f, dir + f.getName());
            }
        } else {
            //当前的是文件,打包处理
            //创建文件缓冲输入流,读取目标文件
            FileInputStream fin = new FileInputStream(file);
            ZipEntry entry = new ZipEntry(dir);
            zip.putNextEntry(entry);
            int length;
            byte[] buffer = new byte[1024];
            while((length = fin.read(buffer)) > 0) {
                zip.write(buffer, 0, length);
            }
            zip.flush();
            fin.close();
            zip.closeEntry();
        }
    }
    //返回二进制压缩包文件流
    private static byte[] createZip(String sourceFilePath) throws Exception{
        //1、创建字节数组输出流,用于返回压缩后的输出流字节数组
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        //2、创建压缩输出流
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        //将目标文件打包成zip导出
        File file = new File(sourceFilePath);
        handlerFile(zip, file,"");
        //IOUtils.closeQuietly(zip);
        zip.close();
        return outputStream.toByteArray();
    }
    //删除文件
    private static void deleteFile(File file) {
        if (file.exists()) {//判断文件是否存在
            if (file.isFile()) {//判断是否是文件
                file.delete();//删除文件
            } else if (file.isDirectory()) {//否则如果它是一个目录
                File[] files = file.listFiles();//声明目录下所有的文件 files[];
                for (int i = 0; i < files.length; i++) {//遍历目录下所有的文件
                    deleteFile(files[i]);//把每个文件用这个方法进行迭代
                }
                file.delete();//删除文件夹
            }
        } else {
            System.out.println("所删除的文件不存在");
        }
    }
}

你可能感兴趣的:(jquery,ajax,zip,java,js)