Skip to content

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")
    );
}