最近在写一个大量小文件直接压缩到一个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. springboot中,子项目的boot依赖全部爆红

    应仔细检查父项目的dependencyManagement是否指定了打包方式<type>为pom,<scope>为import

  2. qt调用quit()后未结束线程解决方案

    正常操作 正常写Qt的多线程,需要继承QThread,然后重写run函数,调用start后,在run函数中实现子线程的运行,这个时候会开启事件循环,可以调用quit()函数来结束当前的线程. 其他操作 ...

  3. ansible 003 常用模块

    常用模块 file 模块 管理被控端文件 回显为绿色则,未变更,符合要求 黄色则改变 红色则报错 因为默认值为file,那么文件不存在,报错 改为touch则创建 将state改为directory变 ...

  4. mybatis 输出sql日志

    logging.level.com.dsmp.server.core.pgsqldao=debug com.dsmp.server.core.pgsqldao 为包名

  5. 使用 MAUI 在 Windows 和 Linux 上绘制 PPT 的图表

    我在做一个图表工具软件,这个软件使用 MAUI 开发.我的需求是图表的内容需要和 PPT 的图表对接,需要用到 OpenXML 解析 PPT 内容,读取到 PPT 图表元素的内容,接着使用 MAUI ...

  6. .Net之接口小知识

    目的 通过一个简单的项目,在原来的文章基础上完善一下常用的几种WebApi编写方式以及请求方式,一方面是用于给我一个前端朋友用来学习调用接口,另一方面让我测试HttpClient的一些效果. 本文示例 ...

  7. 如何搭建安全的 CI/CD 管道?

    Eolink 前端负责人黎芷君进行了<工程化- CI / CD>的主题演讲,围绕 CI/CD 管道安全的实践,分享自己在搭建 CI/CD 管道过程中所总结的重要经验,与开发者深入讨论 &q ...

  8. day04-1群聊功能

    多用户即时通讯系统04 4.编码实现03 4.5功能实现-群聊功能实现 4.5.1思路分析 群聊的实现思路和私聊的实现非常类似. 不同的是:私聊时,服务端接收到消息后,只需要找出接收方的socket并 ...

  9. 研发效能之技术治理&技术治理架构师

    最近很多公司专门设置了一个职位叫「技术治理架构师」,主要负责公司技术治理相关事宜.这是个非常有意思的职位.技术治理的活,之前我们也是做的,只是没有提的这么明确,一般都是研发效能团队.PMO.架构团队. ...

  10. Java基础之变量

    Java基础之变量 目录 Java基础之变量 1.变量概述 1.1 为什么需要变量 1.2 简单理解 1.3 变量使用注意事项 1.4 程序中+号的使用 1.5 Java数据类型 1.6 整数类型 1 ...