Zip Utils (Function)
压缩的流式传输
ZipUtils
/**
* 压缩工具类
*/
public class ZipUtils {
private static final String ZIP_FILE_SEPARATOR = "/";
public static <T> void compress(T entity, Function<T, String> nameGetter,
Function<T, List<T>> children,
Predicate<T> isDir, Predicate<T> isFile, Function<T, InputStream> inputGetter,
OutputStream output) throws Exception {
if (Objects.isNull(output)) {
throw new RuntimeException("output stream cannot be null");
}
try (ZipOutputStream zos = new ZipOutputStream(output)) {
bfs(entity, nameGetter, children, isDir, isFile, inputGetter, zos);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static <T> void bfs(T entity, Function<T, String> nameGetter,
Function<T, List<T>> childrenGetter,
Predicate<T> isDirFunc, Predicate<T> isFileFunc,
Function<T, InputStream> inputGetter,
ZipOutputStream zos) throws Exception {
if (Objects.isNull(entity)) {
throw new RuntimeException("entity cannot be null");
}
Queue<EntityWrapper<T>> queue = new LinkedList<>();
queue.add(new EntityWrapper<>(entity, ""));
while (!queue.isEmpty()) {
EntityWrapper<T> ew = queue.poll();
T e = ew.e();
boolean isDir = isDirFunc.test(e);
boolean isFile = isFileFunc.test(e);
String name = nameGetter.apply(e);
if (StringUtils.isBlank(name) || name.contains(ZIP_FILE_SEPARATOR)) {
throw new RuntimeException("name cannot be blank or contains illegal characters: " + name);
}
String fileName = StringUtils.isBlank(ew.parent) ? name : ew.parent + ZIP_FILE_SEPARATOR + name;
System.out.println(fileName);
if (isDir) {
zos.putNextEntry(new ZipEntry(fileName + ZIP_FILE_SEPARATOR));
List<T> children = childrenGetter.apply(e);
if (children != null && !children.isEmpty()) {
for (T child : children) {
queue.add(new EntityWrapper<>(child, fileName));
}
}
} else if (isFile) {
zos.putNextEntry(new ZipEntry(fileName));
readWrite(inputGetter.apply(e), zos);
} else {
throw new RuntimeException("unknown type for entity: " + e);
}
}
}
private record EntityWrapper<T>(T e, String parent) {
}
private static <T> void dfs(T entity, Function<T, String> nameGetter,
Function<T, List<T>> childrenGetter,
Predicate<T> isDirFunc, Predicate<T> isFileFunc,
Function<T, InputStream> inputGetter,
ZipOutputStream zos, String parent) throws Exception {
if (Objects.isNull(entity)) {
throw new RuntimeException("entity cannot be null");
}
boolean isDir = isDirFunc.test(entity);
boolean isFile = isFileFunc.test(entity);
String name = nameGetter.apply(entity);
if (StringUtils.isBlank(name) || name.contains(ZIP_FILE_SEPARATOR)) {
throw new RuntimeException("name cannot be blank or contains illegal characters: " + name);
}
String fileName = StringUtils.isBlank(parent) ? name : parent + ZIP_FILE_SEPARATOR + name;
if (isDir) {
zos.putNextEntry(new ZipEntry(fileName + ZIP_FILE_SEPARATOR));
List<T> children = childrenGetter.apply(entity);
if (children != null && !children.isEmpty()) {
for (T child : children) {
dfs(child, nameGetter, childrenGetter, isDirFunc, isFileFunc, inputGetter, zos, fileName);
}
}
} else if (isFile) {
zos.putNextEntry(new ZipEntry(fileName));
readWrite(inputGetter.apply(entity), zos);
} else {
throw new RuntimeException("unknown type for entity: " + entity);
}
}
private static void readWrite(InputStream in, @NotNull OutputStream out) throws Exception {
if (in == null) {
return;
}
try (in) {
byte[] buffer = new byte[2048];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.flush();
}
}
}
TestCase
@Test
public void testCompressFile() throws Exception {
File directory = new File("D:/apache-maven-3.9.9");
ZipUtils.compress(
directory,
File::getName,
file -> {
File[] files = file.listFiles();
return files != null ? Arrays.asList(files) : List.of();
},
File::isDirectory, File::isFile,
file -> {
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
return null;
}
},
new FileOutputStream("D:/" + UUID.randomUUID() + ".zip")
);
}
27 January 2026