最近在写一个大量小文件直接压缩到一个zip的需求,由于zip中的entry每一个都是独立的,不需要追加写入,也就是一个entry文件,写一个内容,

因此直接使用了多线程来处理,结果就翻车了,代码给出了如下的错误:write beyond end of stream!

下面直接还原当时的代码场景:

 1 public class MultiThreadWriteZipFile {
2
3 private static ExecutorService executorService = Executors.newFixedThreadPool(50);
4
5 private static CountDownLatch countDownLatch = new CountDownLatch(50);
6
7
8 @Test
9 public void multiThreadWriteZip() throws IOException, InterruptedException {
10 File file = new File("D:\\Gis开发\\数据\\影像数据\\china_tms\\2\\6\\2.jpeg");
11 //创建一个zip
12 ZipOutputStream zipOutputStream =
13 new ZipOutputStream(new FileOutputStream(new File("E:\\java\\test\\test.zip")));
14
15 for (int i = 0; i < 50; i++){
16 String entryName = i + File.separator + i + File.separator + i + ".jpeg";
17 executorService.submit(() -> {
18 try {
19 writeSource2ZipFile(new FileInputStream(file),entryName,zipOutputStream);
20 countDownLatch.countDown();
21 } catch (IOException e) {
22 e.getLocalizedMessage();
23 }
24 });
25 }
26 //阻塞主线程
27 countDownLatch.await();
28 //关闭流
29 zipOutputStream.close();
30 }
31
32
33 public void writeSource2ZipFile(InputStream inputStream,
34 String zipEntryName,
35 ZipOutputStream zipOutputStream) throws IOException {
36 //新建entry
37 zipOutputStream.putNextEntry(new ZipEntry(zipEntryName));
38 byte[] buf = new byte[1024];
39 int position;
40 //entry中写数据
41 while((position = inputStream.read(buf)) != -1){
42 zipOutputStream.write(buf);
43 }
44 zipOutputStream.closeEntry();
45 zipOutputStream.flush();
46 }
47 }

直接运行上面的代码就会报错:write beyond end of stream

将 private static ExecutorService executorService = Executors.newFixedThreadPool(50);

修改为

private static ExecutorSercvice executorService = Executors.newSingleThreadExecutor();

此时代码运行正常!

至于原因嘛,我们跟踪下代码也就明白其中的原因了,我们先来看报错的代码出处:

在java.util包下的DeflaterOutputStream的201行(jdk1.8,其它版本可能会有差异),我们来看代码

 public void write(byte[] b, int off, int len) throws IOException {
if (def.finished()) {
throw new IOException("write beyond end of stream");
}
if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
throw new IndexOutOzfBoundsException();
} else if (len == 0) {
return;
}
if (!def.finished()) {
def.setInput(b, off, len);
while (!def.needsInput()) {
deflate();
}
}
}

关键的原因就是def.finished()对应的状态信息,而这个状态是在Deflater这个类中定义的,这个类也是Java基于ZLIB压缩库实现的,一个压缩工具类。

而下面的这段代码就是改变这个状态的,

public void finish() {
synchronized (zsRef) {
finish = true;
}
}

而这个代码的调用之处,最源头就是我们上面的zipOutputStream.putNextEntry(new ZipEntry(zipEntryName)); 这行代码,

其实先思路,就是每次新增一个entry的时候,都需要将上一次的entry关闭掉,此时也就触发了这个条件,而这个状态并不是线程私有的,我们通过下面的代码就可以知道

public
class Deflater { private final ZStreamRef zsRef;
private byte[] buf = new byte[0];
private int off, len;
private int level, strategy;
private boolean setParams;
private boolean finish, finished;
private long bytesRead;
private long bytesWritten;

因此在多线程下,这个状态肯定是线程不安全的!

好了本次关于多线程下写zip报错的问题,就介绍到这里!

Java 多线程写zip文件遇到的错误 write beyond end of stream!的更多相关文章

  1. JAVA多线程下载网络文件

    JAVA多线程下载网络文件,开启多个线程,同时下载网络文件.   源码如下:(点击下载 MultiThreadDownload.java) import java.io.InputStream; im ...

  2. java多线程批量读取文件(七)

    新公司入职一个多月了,至今没有事情可以做,十来个新同事都一样抓狂,所以大家都自己学习一些新东西,我最近在看zookeeper,感觉蛮不错的,和微服务的zuul以及eureka功能类似,只是代码复杂了一 ...

  3. Android中用Java代码实现zip文件解压缩

    如果需要下载的文件有很多是中文名的,解压时有中文名的文件出现乱码,试了很多方法不能解决问题.据说有一个Java插件包,用这个插件包可以解决中文名乱码的问题,但不知解压的文件是否要用它提供的类压缩后的文 ...

  4. Java多线程断点下载文件

    Java实现断点续传+多线程下载 如下代码所示,每一步都有注解 思路: 通过URL连接到服务器上要下载的文件,得到文件的大小: 算出每条线程下载的开始位置和结束位置,例如,有两条线程下载100Byte ...

  5. Java—解压zip文件

    import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import ja ...

  6. JAVA解压ZIP文件

    import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.Inp ...

  7. Java多线程读取大文件

    前言 今天是五一假期第一天,按理应该是快乐玩耍的日子,但是作为一个北漂到京师的开发人员,实在难想出去那玩耍.好玩的地方比较远,近处又感觉没意思.于是乎,闲着写篇文章,总结下昨天写的程序吧. 昨天下午朋 ...

  8. java多线程批量读取文件( 八)--读写分离

    package com.net.thread.future; import java.io.BufferedReader; import java.io.BufferedWriter; import ...

  9. java IO流 Zip文件操作

    一.简介 压缩流操作主要的三个类 ZipOutputStream.ZipFile.ZipInputStream ,经常可以看到各种压缩文件:zip.jar.GZ格式的压缩文件 二.ZipEntry   ...

随机推荐

  1. Navicat的使用与python中使用MySQL的基本方法

    Navicat的使用与python中使用MySQL的基本方法 Navicat的下载及安装 下载地址 http://www.navicat.com.cn/download/navicat-premium ...

  2. 【问题解决】npm ERR! code EINTEGRITY

    问题说明 Jenkins构建前端安装依赖报错: npm ERR! code EINTEGRITY 11:05:42 npm ERR! sha512-IJy2B5Ot9wIAGwjSKF94+8yhVC ...

  3. Sirni题解(最小生成树,埃氏筛)(继 Liang-梁)

    目录 前言 题意 思路 一些建议 前言 本篇是对Liang-梁的Sirni(最小生成树,埃氏筛)的后继博客. 通篇原文:https://blog.csdn.net/qq_37555704/articl ...

  4. 【java】学习路径39-Buffered缓冲输出流

    import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; pu ...

  5. Java Web中MVC设计模式与IOC

    MVC是由Model(模型).View(视图).Controller(控制器)三个模块组成 视图:用于做数据展示以及和用户交互的一个界面(html页面) 控制层:能够接受客户端的请求,具体的业务功能还 ...

  6. Html飞机大战(三):定义状态

    好家伙, 1.为飞机大战定义状态 1.开始 START 有一个飞机大战LOGO &天空 2.开始时 STRATING 有一个飞机加载的界面&天空 3.运行时 RUNNING 我方飞机& ...

  7. GNSS模块使用笔记

    目录 目录 GNSS芯片 NMEA0183 协议 指令 GNSS TO MCU MCU TO GNSS GNSS芯片 ATGM336H-5N31(GPS+BDS双模) 原理图 NMEA0183 协议 ...

  8. Gitea v1.17.0 正式发布 | 集成软件包管理器、容器镜像仓库

    我们自豪地宣布 Gitea v1.17.0 发布了.本次发布带来了诸多新特性和累积的更新,我们强烈建议用户在更新到最新版本之前仔细阅读发行注记. 在 1.17.0 版本的开发中我们一共合并了 645 ...

  9. java~springboot(2022之后)~目录索引

    回到占占推荐博客索引 最近写了不过关于java,spring,微服务的相关文章,今天把它整理一下,方便大家学习与参考. java~springboot(2022之前)~目录索引 java~spring ...

  10. Shell 脚本实践指南

    代码风格规范 开头有"蛇棒" 所谓shebang其实就是在很多脚本的第一行出现的以#!开头的注释,他指明了当我们没有指定解释器的时候默认的解释器,一般可能是下面这样: #!/bin ...