Java 多线程压缩改进
- 上一篇多线程压缩,压缩文件过多,过大的文件,会导致内存溢出,改进如下
- 提供了一个参数,用于设置多线程压缩,压缩文件大小的最大总和
- 每一次达到最大压缩大小,就将重新启动一次多线程压缩
- 经过多次压缩后,合并多个压缩文件吗,得到最后的结果
具体代码如下
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.20</version>
</dependency>
package top.youlingdada.http;
import org.apache.commons.compress.archivers.zip.ParallelScatterZipCreator;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.input.NullInputStream;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.*;
import java.util.zip.*;
public class ZipCompressUtils {
private static final ThreadLocal<Long> maxSize = new ThreadLocal<>();
private static final ThreadLocal<Long> currentSize = new ThreadLocal<>();
private static final ThreadLocal<String> stopPath = new ThreadLocal<>();
public static void compressFiles(String zipOutName, long max, String... paths) throws IOException, ExecutionException, InterruptedException {
maxSize.set(max);
stopPath.set(null);
List<String> zipFiles = new ArrayList<>();
int n = 0;
do {
Path tempFile = Files.createTempFile(System.currentTimeMillis() + "", "zip");
zipFiles.add(tempFile.toString());
System.out.println("重载压缩次数:" + n++);
currentSize.set(max);
ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
compressFiles(tempFile.toString(), executor, Deflater.BEST_SPEED, paths);
} while (stopPath.get() != null);
marge(zipOutName, zipFiles);
for (String str : zipFiles) {
new File(str).delete();
}
}
public static void compressFiles(String zipOutName, ExecutorService executorService, int level, String... paths) throws IOException, ExecutionException, InterruptedException {
ParallelScatterZipCreator parallelScatterZipCreator = new ParallelScatterZipCreator(executorService);
OutputStream outputStream = Files.newOutputStream(Paths.get(zipOutName));
ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream);
zipArchiveOutputStream.setLevel(level);
zipArchiveOutputStream.setEncoding("UTF-8");
for (String path : paths) {
File temp = new File(path);
compress(parallelScatterZipCreator, temp, temp.getName());
}
parallelScatterZipCreator.writeTo(zipArchiveOutputStream);
zipArchiveOutputStream.close();
outputStream.close();
}
public static void compressFiles(String zipOutName, ThreadFactory factory, int level, String... paths) throws IOException, ExecutionException, InterruptedException {
ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), factory, new ThreadPoolExecutor.CallerRunsPolicy());
compressFiles(zipOutName, executor, level, paths);
}
protected static void compress(ParallelScatterZipCreator parallelScatterZipCreator, File inputFile, String relativePath) throws IOException, ExecutionException, InterruptedException {
if (inputFile == null) {
return;
}
if (inputFile.isDirectory()) {
File[] files = inputFile.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (file.isDirectory()) {
compress(parallelScatterZipCreator, new File(inputFile.getAbsolutePath() + "/" + file.getName()), relativePath + "/" + file.getName());
} else {
final InputStreamSupplier inputStreamSupplier = () -> {
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
return new NullInputStream(0);
}
};
String stop = stopPath.get();
String path = relativePath + "/" + file.getName();
if (stop != null && !stop.equals(path)) {
continue;
}
stopPath.set(null);
if (currentSize.get() < file.length()) {
if (maxSize.get() < file.length()) {
throw new IllegalArgumentException("设置最大压缩容量应大于每个文件的大小,当前容量大小:" + maxSize.get() + ",文件大小:" + file.length());
}
stopPath.set(path);
break;
} else {
currentSize.set(currentSize.get() - file.length());
}
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(path);
zipArchiveEntry.setMethod(ZipArchiveEntry.DEFLATED);
zipArchiveEntry.setSize(file.length());
parallelScatterZipCreator.addArchiveEntry(zipArchiveEntry, inputStreamSupplier);
}
}
} else {
String stop = stopPath.get();
String path = relativePath + "/" + inputFile.getName();
if (stop != null && !stop.equals(path)) {
return;
}
stopPath.set(null);
if (currentSize.get() < inputFile.length()) {
if (maxSize.get() < inputFile.length()) {
throw new IllegalArgumentException("设置最大压缩容量应大于每个文件的容量,当前容量大小:" + maxSize.get() + ",文件大小:" + inputFile.length());
}
stopPath.set(path);
return;
} else {
currentSize.set(currentSize.get() - inputFile.length());
}
final InputStreamSupplier inputStreamSupplier = () -> {
try {
return new FileInputStream(inputFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
return new NullInputStream(0);
}
};
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(relativePath + "/" + inputFile.getName());
zipArchiveEntry.setMethod(ZipArchiveEntry.DEFLATED);
zipArchiveEntry.setSize(inputFile.length());
parallelScatterZipCreator.addArchiveEntry(zipArchiveEntry, inputStreamSupplier);
}
}
public static void marge(String output, List<String> sourceZipFiles) {
byte buff[] = new byte[8192];
ZipOutputStream out = null;
List<ZipInputStream> ins = new ArrayList<>();
try {
out = new ZipOutputStream(Files.newOutputStream(Paths.get(output)));
HashSet<String> names = new HashSet<>();
for (String sourceZipFile : sourceZipFiles) {
ZipFile zipFile = new ZipFile(sourceZipFile, StandardCharsets.UTF_8);
ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(Paths.get(sourceZipFile)));
ins.add(zipInputStream);
ZipEntry ze;
Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
while (enumeration.hasMoreElements()) {
ze = enumeration.nextElement();
if (!ze.isDirectory()) {
if (names.contains(ze.getName())) {
continue;
}
ZipEntry oze = new ZipEntry(ze.getName());
out.putNextEntry(oze);
if (ze.getSize() > 0) {
DataInputStream dis = new DataInputStream(zipFile.getInputStream(ze));
int len = 0;
while ((len = dis.read(buff)) > 0) {
out.write(buff, 0, len);
}
out.closeEntry();
out.flush();
}
names.add(oze.getName());
}
}
zipInputStream.closeEntry();
}
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("合并压缩文件失败");
} finally {
try {
if (out != null) {
out.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
try {
for (ZipInputStream in : ins) {
if (in != null) {
in.close();
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}