I am trying to zip files in Azure blob. I can create a zip file with the normal zip method but to improve performance when I try using completable future I get an exception.
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);
Throws exception at zipStream.write(fileOutputStream.toByteArray());
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]
Any suggestions?
java.util.zip.ZipException: no current ZIP entry
The above error occurred when the code tried to write to the ZipOutputStream
before creating a new entry with putNextEntry()
.
To fix this, the write()
method should be moved inside the CompletableFuture
, after the call to putNextEntry()
.
Code:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.io.ByteArrayOutputStream;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobClient;
public class App {
public static void main(String[] args) {
String connectionString = "xxxx";
String containerName = "test";
String azureFilePath = "sample";
String zipFileName = "output.zip";
List<String> fileNames = Arrays.asList("demo.pdf","jgs.html","test.jpg");
zipFilesInBlob(connectionString, containerName, azureFilePath, zipFileName, fileNames);
System.out.println("Zip operation completed successfully.");
}
public static void zipFilesInBlob(String connectionString, String containerName, String azureFilePath,
String zipFileName, List<String> fileNames) {
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(connectionString)
.buildClient();
BlobContainerClient blobContainerClient = blobServiceClient.getBlobContainerClient(containerName);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (ZipOutputStream zipStream = new ZipOutputStream(outputStream)) {
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (String singleFileName : fileNames) {
String singleFilePath = azureFilePath + "/" + singleFileName.trim();
final BlobClient blobClient = blobContainerClient.getBlobClient(singleFilePath);
final String finalSingleFileName = singleFileName;
final String finalSingleFilepath = singleFilePath;
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
try (ByteArrayOutputStream fileOutputStream = new ByteArrayOutputStream()) {
if (blobClient.exists()) {
blobClient.download(fileOutputStream);
zipStream.putNextEntry(new ZipEntry(finalSingleFileName));
zipStream.write(fileOutputStream.toByteArray());
zipStream.closeEntry();
} else {
throw new RuntimeException("File not found: " + finalSingleFilepath);
}
} catch (IOException e) {
throw new RuntimeException("Error processing file: " + finalSingleFileName, e);
}
}
});
futures.add(future);
}
CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
allOf.join();
} catch (IOException e) {
throw new RuntimeException("Error creating zip file", e);
}
BlobClient zipFileClient = blobContainerClient.getBlobClient(azureFilePath + "/" + zipFileName);
zipFileClient.upload(new ByteArrayInputStream(outputStream.toByteArray()), outputStream.size(), true);
}
}
The above code creates a ZipOutputStream and loops through the list of file names, downloading each file from Azure Storage and adding it to the zip stream. Finally, it uploads the zip file to Azure Storage.
Output:
Zip operation completed successfully.
Portal:
Reference: