You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure Blob多线程压缩文件报错:no current ZIP entry

问题:Azure Blob多线程压缩文件抛出ZipException异常

我在Azure Blob中压缩文件时,单线程常规压缩能正常生成压缩包,但改用CompletableFuture多线程处理时抛出异常。

代码示例

try (ZipOutputStream zipStream = new ZipOutputStream(outputStream)) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();

        for (String singleFileName : fileNames) {
            String singleFilePath = azureFilePath + "/" + singleFileName.trim();
            BlobClient blobClient = blobContainerClient.getBlobClient(singleFilePath);

            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try (ByteArrayOutputStream fileOutputStream = new ByteArrayOutputStream()) {
                    if (blobClient.exists()) {
                        // Download the file to a separate output stream
                        blobClient.download(fileOutputStream);

                        // Add the file to the zip stream
                        zipStream.putNextEntry(new ZipEntry(singleFileName));
                        zipStream.write(fileOutputStream.toByteArray());
                      // zipStream.closeEntry();
                    } else {
                        throw new RuntimeException("File not found: " + singleFilePath);
                    }
                } catch (IOException e) {
                    throw new RuntimeException("Error processing file: " + singleFileName, e);
                }finally {
                    try {
                        zipStream.closeEntry();
                    } catch (IOException e) {
                        // Handle any potential exception when closing the zip entry
                        e.printStackTrace();
                    }
                }
            });

            futures.add(future);
        }
        CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        allOf.join();
    }

    // Upload the zip file to the same location
    BlobClient zipFileClient = blobContainerClient.getBlobClient(azureFilePath + "/" + zipFileName);
    zipFileClient.upload(new ByteArrayInputStream(outputStream.toByteArray()), outputStream.size(),true);

异常信息

java.util.zip.ZipException: no current ZIP entry
at java.base/java.util.zip.ZipOutputStream.write(ZipOutputStream.java:343) ~[na:na]
at java.base/java.io.FilterOutputStream.write(FilterOutputStream.java:108) ~[na:na]
at main.java.com.azure.filezip.ApiControllerzip.lambda$uploadZip$0(ApiControllerzip.java:78) ~[classes/:na]

解决建议

  • 核心原因:ZipOutputStream并非线程安全
    ZipOutputStream的putNextEntry、write、closeEntry等方法没有线程同步机制,多线程同时操作会导致内部状态混乱——比如线程A刚调用putNextEntry,线程B就覆盖了当前entry,线程A再执行write时就找不到对应的entry,直接抛出异常。

  • 可行的优化方案:

    1. 多线程下载+单线程写入Zip流
      将文件下载与Zip写入解耦,用多线程并行下载Blob文件到内存,所有文件下载完成后,由主线程统一写入ZipOutputStream,既保留下载的性能优势,又避免多线程操作Zip流的冲突。调整后示例代码:
      try (ZipOutputStream zipStream = new ZipOutputStream(outputStream)) {
          List<CompletableFuture<Map.Entry<String, byte[]>>> futures = new ArrayList<>();
      
          for (String singleFileName : fileNames) {
              String singleFilePath = azureFilePath + "/" + singleFileName.trim();
              BlobClient blobClient = blobContainerClient.getBlobClient(singleFilePath);
      
              CompletableFuture<Map.Entry<String, byte[]>> future = CompletableFuture.supplyAsync(() -> {
                  try (ByteArrayOutputStream fileOutputStream = new ByteArrayOutputStream()) {
                      if (blobClient.exists()) {
                          blobClient.download(fileOutputStream);
                          return new AbstractMap.SimpleEntry<>(singleFileName, fileOutputStream.toByteArray());
                      } else {
                          throw new RuntimeException("File not found: " + singleFilePath);
                      }
                  } catch (IOException e) {
                      throw new RuntimeException("Error processing file: " + singleFileName, e);
                  }
              });
      
              futures.add(future);
          }
      
          // 等待所有文件下载完成,逐个写入Zip流
          for (CompletableFuture<Map.Entry<String, byte[]>> future : futures) {
              Map.Entry<String, byte[]> entry = future.join();
              zipStream.putNextEntry(new ZipEntry(entry.getKey()));
              zipStream.write(entry.getValue());
              zipStream.closeEntry();
          }
      }
      
      // 上传Zip文件到Blob
      BlobClient zipFileClient = blobContainerClient.getBlobClient(azureFilePath + "/" + zipFileName);
      zipFileClient.upload(new ByteArrayInputStream(outputStream.toByteArray()), outputStream.size(), true);
      
    2. 加全局锁强制串行写入(不推荐)
      如果一定要在多线程中写入Zip流,可给zipStream加全局锁,但这样会让写入操作串行化,失去多线程的性能优势,仅作为应急方案:
      // 在lambda中操作Zip流时加锁
      synchronized (zipStream) {
          zipStream.putNextEntry(new ZipEntry(singleFileName));
          zipStream.write(fileOutputStream.toByteArray());
          zipStream.closeEntry();
      }
      
    3. 内存优化(针对大文件场景)
      如果处理的文件较大,用ByteArrayOutputStream会占用过多内存,建议下载到临时文件,再从临时文件写入ZipOutputStream,避免内存溢出。

内容的提问来源于stack exchange,提问作者Mani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 17:55:25