Java处理压缩文件的两种方式!!!!

方式1:前端上传压缩文件zip格式

controller:

java 复制代码
 @ApiOperation("上传或保存ELC地图文件(ZIP文件)")
    @PostMapping("/zipUpload")
    public GeneralResponse zipUpload(@RequestParam("body") String body, @RequestParam("file") MultipartFile zipFile) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        boolean b = false;
        JSONObject res = new JSONObject();
        List<UnzipFileVo> unzipFileVoList = ZipUtil.Ectract(zipFile);
        // zip压缩包的内容转换成文件流集合
        List<UnzipFileVo> collect = unzipFileVoList.stream().filter(item -> item.getFile().getOriginalFilename().contains(".") && item.getFile().getSize() > 0).collect(Collectors.toList());//获取zip文件里面的文件,并组装到新的List对象//过滤文件夹
        for (int i = 0; i < collect.size(); i++) {
            b = ElcMapFileManager.getInstance().uploadFile(body, collect.get(i).getFile(), res);
        }
        if (!b) {
            return GeneralResponse.failure(ThreadLocalUtil.get().getComment());
        }
        return GeneralResponse.success(res);
    }

ZipUtil:

java 复制代码
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

public class ZipUtil {

    public static List<UnzipFileVo> Ectract(MultipartFile multipartFile) throws IOException {
        List<UnzipFileVo> list= new ArrayList<>();
        //获取文件输入流
        InputStream input = multipartFile.getInputStream();
        //获取ZIP输入流(一定要指定字符集Charset.forName("GBK")否则会报java.lang.IllegalArgumentException: MALFORMED)
        ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(input), Charset.forName("GBK"));
        ZipFile zf = toFile(multipartFile);
        //定义ZipEntry置为null,避免由于重复调用zipInputStream.getNextEntry造成的不必要的问题
        ZipEntry ze = null;
        //循环遍历
        while ((ze =zipInputStream.getNextEntry())!= null) {
            InputStream is = zf.getInputStream(ze);

            UnzipFileVo unzipFileVo = new UnzipFileVo();
            unzipFileVo.setFile(getMultipartFile(is,ze.getName()));
            list.add(unzipFileVo);
        }
        //一定记得关闭流
        zipInputStream.closeEntry();
        input.close();
        return list;
    }

    /**
     * 获取封装得MultipartFile
     *
     * @param inputStream inputStream
     * @param fileName    fileName
     * @return MultipartFile
     */
    public static MultipartFile getMultipartFile(InputStream inputStream, String fileName) {
        FileItem fileItem = createFileItem(inputStream, fileName);
        //CommonsMultipartFile是feign对multipartFile的封装,但是要FileItem类对象
        return new CommonsMultipartFile(fileItem);
    }

    private  static  ZipFile toFile(MultipartFile multipartFile) throws IOException {
        if (multipartFile == null || multipartFile.getSize() <= 0) {
            return null;
        }
        File file = multipartFileToFile(multipartFile);
        if (file == null || !file.exists()) {
            return null;
        }
        ZipFile zipFile = new ZipFile(file);
        return zipFile;
    }

    private static File multipartFileToFile(MultipartFile multipartFile) {
        File file = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = multipartFile.getInputStream();
            file = new File(multipartFile.getOriginalFilename());
            outputStream = new FileOutputStream(file);
            write(inputStream, outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return file;
    }

    public static void  write(InputStream inputStream, OutputStream outputStream) {
        byte[] buffer = new byte[4096];
        try {
            int count = inputStream.read(buffer, 0, buffer.length);
            while (count != -1) {
                outputStream.write(buffer, 0, count);
                count = inputStream.read(buffer, 0, buffer.length);
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * FileItem类对象创建
     *
     * @param inputStream inputStream
     * @param fileName    fileName
     * @return FileItem
     */
    public static FileItem createFileItem(InputStream inputStream, String fileName) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        String textFieldName = "file";
        FileItem item = factory.createItem(textFieldName, MediaType.MULTIPART_FORM_DATA_VALUE, true, fileName);
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        OutputStream os = null;
        //使用输出流输出输入流的字节
        try {
            os = item.getOutputStream();
            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            inputStream.close();
        } catch (IOException e) {

            throw new IllegalArgumentException("文件上传失败");
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {

                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                }
            }
        }

        return item;
    }
}

UnziipFileVo:

java 复制代码
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;

@Data
public class UnzipFileVo {
    private MultipartFile file;
}

方式2:前端上传文件(压缩文件内容)

前端实现:

Java实现:

controller:

java 复制代码
@ApiOperation("上传或保存ELC地图文件(前端压缩文件内容传递到后端)")
    @PostMapping("/decompressedUpload")
    //    @PreAuthorize("@userAuthority.check('elcMapFile:decompressedUpload')")
    public GeneralResponse decompressedUpload(@RequestParam("body") String body, @RequestParam("file") MultipartFile file) {
        try {
            JSONObject res = new JSONObject();
            InputStream rawStream = file.getInputStream();
            // 将压缩流转换为字节数组
            byte[] compressedBytes = IOUtils.toByteArray(rawStream);

            // 解压逻辑(自动检测GZIP/DEFLATE)
            byte[] decompressedBytes = DecompressUtil.decompressAuto(compressedBytes);

            // 调用业务逻辑传递解压后数据
            boolean b = ElcMapFileManager.getInstance().uploadFile(body, file.getOriginalFilename(), decompressedBytes, res);
            if (!b) {
                return GeneralResponse.failure(ThreadLocalUtil.get().getComment());
            }
            return GeneralResponse.success(res);
        } catch (Exception e) {
            try {
                throw e;
            } finally {
                return GeneralResponse.failure(e.getMessage());
            }
        }
    }

DecomressUtil:

java 复制代码
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;

public class DecompressUtil {

    public static byte[] decompressAuto(byte[] compressedBytes) throws IOException, DataFormatException {
        // 优先尝试GZIP解压(检测头字节1F8B)
        if (isGzipFormat(compressedBytes)) {
            return decompressGzip(compressedBytes);
        } else {
            // 否则按DEFLATE处理
            return decompressDeflate(compressedBytes);
        }
    }

    public static boolean isGzipFormat(byte[] data) {
        return data.length >= 2 && (data[0] == (byte) 0x1F) && (data[1] == (byte) 0x8B);
    }

    public static byte[] decompressGzip(byte[] compressedBytes) throws IOException {
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(compressedBytes);
            GZIPInputStream gzip = new GZIPInputStream(bis);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = gzip.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            return bos.toByteArray();
        } catch (Exception e) {
            throw e;
        }
    }

    public static byte[] decompressDeflate(byte[] compressedBytes) throws DataFormatException {
        // 移除对zlib头的检查,因为当使用nowrap=true时数据不含头
        Inflater inflater = new Inflater(false); // 使用nowrap=true处理原始DEFLATE数据
        inflater.reset();
        inflater.setInput(compressedBytes);

        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[10240];
            while (!inflater.finished()) {
                int count;
                try {
                    count = inflater.inflate(buffer);
                } catch (DataFormatException e) {
                    throw new DataFormatException("解压失败: " + e.getMessage());
                }
                if (count == 0) {
                    if (inflater.needsInput()) {
                        throw new DataFormatException("输入数据不完整");
                    }
                    if (inflater.needsDictionary()) {
                        throw new DataFormatException("需要字典但未提供");
                    }
                }
                bos.write(buffer, 0, count);
            }
            return bos.toByteArray();
        } finally {
            inflater.end();
        }
    }
}

注意:

在执行这行代码时:count = inflater.inflate(buffer);

我遇到了一个错误:DataFormatException: invalid stored block lengths

我尝试了很多方法都没有效果,这时候你需要看一下这句代码:

复制代码
Inflater inflater = new Inflater(false); // 使用nowrap=true处理原始DEFLATE数据

我最开始用的是true,会导致一直报错,后来改成false就解决了。

大致问题原因如下:

nowrap参数的核心区别
  1. ​**nowrap=true**​

    • 适用场景 :处理 原始DEFLATE压缩数据(不含zlib头部和尾部)
    • 行为特点
      • 跳过对zlib头部(如0x78 9C)和校验尾部的解析
      • 直接解压纯DEFLATE格式的二进制流
      • 适用于前端使用pako.deflate(data, { windowBits: -15 })生成的压缩数据
  2. ​**nowrap=false**​

    • 适用场景 :处理 完整zlib格式数据(包含标准zlib头部和校验尾部)
    • 行为特点
      • 要求输入数据以zlib头部(如0x78 9C)开头
      • 自动校验Adler-32尾部完整性
      • 若数据不含头部或校验失败,会抛出DataFormatException

详细分析

1. ​前端压缩行为解析

从提供的代码片段可推断:

javascript 复制代码
const compressedData = pako.deflate(JSON.stringify(mapInfo), { to: 'string' });
  • 关键点
    • pako.deflate 默认生成zlib格式数据 (包含zlib头0x78 9C和Adler-32校验尾部)
    • 未显式设置windowBits: -15,因此不是原始DEFLATE格式
2. ​后端解压参数匹配
  • ​**nowrap=false的作用**​:

    • 要求输入数据包含完整的zlib头部和校验尾部
    • 自动验证数据完整性(Adler-32校验和)
  • ​**nowrap=true的陷阱**​:

    • 若强制设为true,后端会跳过zlib头解析,导致解压时数据偏移,引发DataFormatException(如invalid stored block lengths

后端代码:

java 复制代码
// 正确设置nowrap=false以处理zlib格式数据
Inflater inflater = new Inflater(false); 
相关推荐
czhaii3 分钟前
PLC脉冲位置 单片机跟踪读取记录显示
开发语言·c#
骑牛小道士3 分钟前
Java基础 集合框架 Collection接口和抽象类AbstractCollection
java
alden_ygq29 分钟前
当java进程内存使用超过jvm设置大小会发生什么?
java·开发语言·jvm
triticale42 分钟前
【Java】网络编程(Socket)
java·网络·socket
淘源码d1 小时前
什么是ERP?ERP有哪些功能?小微企业ERP系统源码,SpringBoot+Vue+ElementUI+UniAPP
java·源码·erp·erp源码·企业资源计划·企业erp·工厂erp
源码方舟1 小时前
【基于ALS模型的教育视频推荐系统(Java实现)】
java·python·算法·音视频
蜗牛沐雨1 小时前
Rust 中的 `PartialEq` 和 `Eq`:深入解析与应用
开发语言·后端·rust
Python私教1 小时前
Rust快速入门:从零到实战指南
开发语言·后端·rust
Mcworld8571 小时前
整数分解JAVA
java·开发语言
请你喝好果汁6411 小时前
python_竞态条件
开发语言·python