close #1199: Reimplement Zipper (#1891)

This commit is contained in:
Glavo 2022-12-02 17:26:00 +08:00 committed by GitHub
parent d4c0c30283
commit ea91ee91ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 25 deletions

View File

@ -198,7 +198,7 @@ public class World {
throw new IOException(); throw new IOException();
try (Zipper zipper = new Zipper(zip)) { try (Zipper zipper = new Zipper(zip)) {
zipper.putDirectory(file, "/" + worldName + "/"); zipper.putDirectory(file, worldName);
} }
} }

View File

@ -24,8 +24,12 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;
/** /**
* Non thread-safe * Non thread-safe
@ -34,30 +38,45 @@ import java.nio.file.attribute.BasicFileAttributes;
*/ */
public final class Zipper implements Closeable { public final class Zipper implements Closeable {
private final FileSystem fs; private final ZipOutputStream zos;
private final byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
public Zipper(Path zipFile) throws IOException { public Zipper(Path zipFile) throws IOException {
this(zipFile, null); this(zipFile, StandardCharsets.UTF_8);
} }
public Zipper(Path zipFile, Charset encoding) throws IOException { public Zipper(Path zipFile, Charset encoding) throws IOException {
Files.deleteIfExists(zipFile); this.zos = new ZipOutputStream(Files.newOutputStream(zipFile), encoding);
fs = CompressingUtils.createWritableZipFileSystem(zipFile, encoding); }
private static String normalize(String path) {
path = path.replace('\\', '/');
if (path.startsWith("/"))
path = path.substring(1);
if (path.endsWith("/"))
path = path.substring(0, path.length() - 1);
return path;
}
private static String resolve(String dir, String file) {
if (dir.isEmpty()) return file;
if (file.isEmpty()) return dir;
return dir + "/" + file;
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
fs.close(); zos.close();
} }
/** /**
* Compress all the files in sourceDir * Compress all the files in sourceDir
* *
* @param source the file in basePath to be compressed * @param source the file in basePath to be compressed
* @param rootDir the path of the directory in this zip file. * @param targetDir the path of the directory in this zip file.
*/ */
public void putDirectory(Path source, String rootDir) throws IOException { public void putDirectory(Path source, String targetDir) throws IOException {
putDirectory(source, rootDir, null); putDirectory(source, targetDir, null);
} }
/** /**
@ -68,31 +87,32 @@ public final class Zipper implements Closeable {
* @param filter returns false if you do not want that file or directory * @param filter returns false if you do not want that file or directory
*/ */
public void putDirectory(Path source, String targetDir, ExceptionalPredicate<String, IOException> filter) throws IOException { public void putDirectory(Path source, String targetDir, ExceptionalPredicate<String, IOException> filter) throws IOException {
Path root = fs.getPath(targetDir); String root = normalize(targetDir);
Files.createDirectories(root);
Files.walkFileTree(source, new SimpleFileVisitor<Path>() { Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (".DS_Store".equals(file.getFileName().toString())) { if (".DS_Store".equals(file.getFileName().toString())) {
return FileVisitResult.SKIP_SUBTREE; return FileVisitResult.SKIP_SUBTREE;
} }
String relativePath = source.relativize(file).normalize().toString(); String relativePath = normalize(source.relativize(file).normalize().toString());
if (filter != null && !filter.test(relativePath.replace('\\', '/'))) { if (filter != null && !filter.test(relativePath)) {
return FileVisitResult.SKIP_SUBTREE; return FileVisitResult.SKIP_SUBTREE;
} }
Files.copy(file, root.resolve(relativePath), StandardCopyOption.COPY_ATTRIBUTES); putFile(file, resolve(root, relativePath));
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }
@Override @Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String relativePath = source.relativize(dir).normalize().toString(); String relativePath = normalize(source.relativize(dir).normalize().toString());
if (filter != null && !filter.test(relativePath.replace('\\', '/'))) { if (filter != null && !filter.test(relativePath)) {
return FileVisitResult.SKIP_SUBTREE; return FileVisitResult.SKIP_SUBTREE;
} }
Path path = root.resolve(relativePath); try {
if (Files.notExists(path)) { zos.putNextEntry(new ZipEntry(resolve(root, relativePath) + "/"));
Files.createDirectory(path); zos.closeEntry();
} catch (ZipException ignored) {
// Directory already exists
} }
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }
@ -104,19 +124,44 @@ public final class Zipper implements Closeable {
} }
public void putFile(Path file, String path) throws IOException { public void putFile(Path file, String path) throws IOException {
Files.copy(file, fs.getPath(path), StandardCopyOption.COPY_ATTRIBUTES); path = normalize(path);
BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
ZipEntry entry = new ZipEntry(attrs.isDirectory() ? path + "/" : path);
entry.setCreationTime(attrs.creationTime());
entry.setLastAccessTime(attrs.lastAccessTime());
entry.setLastModifiedTime(attrs.lastModifiedTime());
if (attrs.isDirectory()) {
try {
zos.putNextEntry(entry);
zos.closeEntry();
} catch (ZipException ignored) {
// Directory already exists
}
} else {
try (InputStream input = Files.newInputStream(file)) {
zos.putNextEntry(entry);
IOUtils.copyTo(input, zos, buffer);
zos.closeEntry();
}
}
} }
public void putStream(InputStream in, String path) throws IOException { public void putStream(InputStream in, String path) throws IOException {
Files.copy(in, fs.getPath(path), StandardCopyOption.COPY_ATTRIBUTES); zos.putNextEntry(new ZipEntry(normalize(path)));
IOUtils.copyTo(in, zos, buffer);
zos.closeEntry();
} }
public void putTextFile(String text, String path) throws IOException { public void putTextFile(String text, String path) throws IOException {
putTextFile(text, "UTF-8", path); putTextFile(text, StandardCharsets.UTF_8, path);
} }
public void putTextFile(String text, String encoding, String pathName) throws IOException { public void putTextFile(String text, Charset encoding, String path) throws IOException {
Files.write(fs.getPath(pathName), text.getBytes(encoding)); zos.putNextEntry(new ZipEntry(normalize(path)));
zos.write(text.getBytes(encoding));
zos.closeEntry();
} }
} }