[英]Java ZIP - how to unzip folder?
是否有任何示例代碼,如何將文件夾從 ZIP 部分解壓縮到我想要的目錄中? 我已將文件夾“FOLDER”中的所有文件讀入字節數組,如何從其文件結構中重新創建?
我不確定你所說的部分是什么意思? 你的意思是在沒有 API 幫助的情況下自己做嗎?
如果您不介意使用一些開源庫,那么有一個很酷的 API 叫做zip4J
它易於使用,我認為有很好的反饋。 看這個例子:
String source = "folder/source.zip";
String destination = "folder/source/";
try {
ZipFile zipFile = new ZipFile(source);
zipFile.extractAll(destination);
} catch (ZipException e) {
e.printStackTrace();
}
如果要解壓的文件有密碼,可以試試這個:
String source = "folder/source.zip";
String destination = "folder/source/";
String password = "password";
try {
ZipFile zipFile = new ZipFile(source);
if (zipFile.isEncrypted()) {
zipFile.setPassword(password);
}
zipFile.extractAll(destination);
} catch (ZipException e) {
e.printStackTrace();
}
我希望這很有用。
這是我正在使用的代碼。 根據您的需要更改 BUFFER_SIZE。
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public final class ZipUtils {
private static final int BUFFER_SIZE = 4096;
public static void extract(ZipInputStream zip, File target) throws IOException {
try {
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
File file = new File(target, entry.getName());
if (!file.toPath().normalize().startsWith(target.toPath())) {
throw new IOException("Bad zip entry");
}
if (entry.isDirectory()) {
file.mkdirs();
continue;
}
byte[] buffer = new byte[BUFFER_SIZE];
file.getParentFile().mkdirs();
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
int count;
while ((count = zip.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
out.close();
}
} finally {
zip.close();
}
}
}
最簡潔、無庫的 Java 7+ 變體:
public static void unzip(InputStream is, Path targetDir) throws IOException {
targetDir = targetDir.toAbsolutePath();
try (ZipInputStream zipIn = new ZipInputStream(is)) {
for (ZipEntry ze; (ze = zipIn.getNextEntry()) != null; ) {
Path resolvedPath = targetDir.resolve(ze.getName()).normalize();
if (!resolvedPath.startsWith(targetDir)) {
// see: https://snyk.io/research/zip-slip-vulnerability
throw new RuntimeException("Entry with an illegal path: "
+ ze.getName());
}
if (ze.isDirectory()) {
Files.createDirectories(resolvedPath);
} else {
Files.createDirectories(resolvedPath.getParent());
Files.copy(zipIn, resolvedPath);
}
}
}
}
兩個分支都需要createDirectories
,因為 zip 文件並不總是包含所有父目錄作為單獨的條目,但可能僅包含它們以表示空目錄。
該代碼解決了ZIP-slip 漏洞,如果某些 ZIP 條目將 go 在targetDir
之外,它將失敗。 此類 ZIP 不是使用常用工具創建的,並且很可能是為了利用該漏洞而手工制作的。
同樣可以使用 Ant 壓縮庫來實現。 它將保留文件夾結構。
Maven 依賴:-
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-compress</artifactId>
<version>1.2</version>
</dependency>
示例代碼:-
Unzip unzipper = new Unzip();
unzipper.setSrc(theZIPFile);
unzipper.setDest(theTargetFolder);
unzipper.execute();
這是一個遵循更現代慣例的簡單解決方案。 如果要解壓縮較大的文件,您可能希望將緩沖區大小更改為更小。 這樣您就不會將所有文件信息保留在內存中。
public static void unzip(File source, String out) throws IOException {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source))) {
ZipEntry entry = zis.getNextEntry();
while (entry != null) {
File file = new File(out, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
} else {
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
int bufferSize = Math.toIntExact(entry.getSize());
byte[] buffer = new byte[bufferSize > 0 ? bufferSize : 1];
int location;
while ((location = zis.read(buffer)) != -1) {
bos.write(buffer, 0, location);
}
}
}
entry = zis.getNextEntry();
}
}
}
這是我用來解壓縮具有多個目錄的 zip 文件的代碼。 沒有使用外部庫。
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class UnzipFile
{
public static void main(String[] args) throws IOException
{
String fileZip = "src/main/resources/abcd/abc.zip";
File destDir = new File("src/main/resources/abcd/abc");
try (ZipFile file = new ZipFile(fileZip))
{
Enumeration<? extends ZipEntry> zipEntries = file.entries();
while (zipEntries.hasMoreElements())
{
ZipEntry zipEntry = zipEntries.nextElement();
File newFile = new File(destDir, zipEntry.getName());
//create sub directories
newFile.getParentFile().mkdirs();
if (!zipEntry.isDirectory())
{
try (FileOutputStream outputStream = new FileOutputStream(newFile))
{
BufferedInputStream inputStream = new BufferedInputStream(file.getInputStream(zipEntry));
while (inputStream.available() > 0)
{
outputStream.write(inputStream.read());
}
inputStream.close();
}
}
}
}
}
}
這是基於這篇文章但經過重構(並使用Lombok
)的更“現代”的完整代碼:
import lombok.var;
import lombok.val;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipInputStream;
import static java.nio.file.Files.createDirectories;
public class UnZip
{
public static void unZip(String sourceZipFile, String outputDirectory) throws IOException
{
val folder = new File(outputDirectory);
createDirectories(folder.toPath());
try (val zipInputStream = new ZipInputStream(new FileInputStream(sourceZipFile, Charset.forName("Cp437"))))
{
var nextEntry = zipInputStream.getNextEntry();
while (nextEntry != null)
{
val fileName = nextEntry.getName();
val newFile = new File(outputDirectory + File.separator + fileName);
newFile.getParentFile().mkdirs();
if(fileName.endsWith("/")){
newFile.mkdirs();
} else {
writeFile(zipInputStream, newFile);
}
writeFile(zipInputStream, newFile);
nextEntry = zipInputStream.getNextEntry();
}
zipInputStream.closeEntry();
}
}
private static void writeFile(ZipInputStream inputStream, File file) throws IOException
{
val buffer = new byte[1024];
file.createNewFile();
try (val fileOutputStream = new FileOutputStream(file))
{
int length;
while ((length = inputStream.read(buffer)) > 0)
{
fileOutputStream.write(buffer, 0, length);
}
}
}
}
您應該從 zip 文件中獲取所有條目:
Enumeration entries = zipFile.getEntries();
然后遍歷這個枚舉,從中獲取ZipEntry
,檢查它是否是一個目錄,然后分別創建目錄或只是提取一個文件。
使用其他庫后,我偶然發現了這個: https://github.com/thrau/jarchivelib
遠遠優於。
Gradle: implementation group: 'org.rauschig', name: 'jarchivelib', version: '1.2.0'
import org.rauschig.jarchivelib.ArchiveFormat;
import org.rauschig.jarchivelib.Archiver;
import org.rauschig.jarchivelib.ArchiverFactory;
import org.rauschig.jarchivelib.CompressionType;
public static void unzip(File zipFile, File targetDirectory) throws IOException, IllegalAccessException {
Archiver archiver = ArchiverFactory.createArchiver(ArchiveFormat.ZIP);
archiver.extract(zipFile, targetDirectory);
}
public static void unTarGz(File tarFile, File targetDirectory) throws IOException {
Archiver archiver = ArchiverFactory.createArchiver(ArchiveFormat.TAR, CompressionType.GZIP);
archiver.extract(tarFile, targetDirectory);
}
其他庫對於這個簡單的任務來說太復雜了。 這就是為什么我喜歡這個庫 - 2 行,完成。
根據 petrs 的回答,這是我現在使用的 kotlin 版本:
fun ZipInputStream.extractTo(target: File) = use { zip ->
var entry: ZipEntry
while (zip.nextEntry.also { entry = it ?: return } != null) {
val file = File(target, entry.name)
if (entry.isDirectory) {
file.mkdirs()
} else {
file.parentFile.mkdirs()
zip.copyTo(file.outputStream())
}
}
}
一個非常好的答案的替代方案,仍然是無 ZIP-slip 漏洞、無庫和 Java 7+ 變體,但基於流:
public static void unzip(File zipFile, Path targetDir) throws IOException {
// Normalize the path to get rid parent folder accesses
Path targetRoot = targetDir.normalize();
// Open the zip file as a FileSystem
try (FileSystem fs = FileSystems.newFileSystem(URI.create("jar:" + zipFile.toURI()), Map.of())) {
for (Path root : fs.getRootDirectories()) {
try (Stream<Path> walk = Files.walk(root).filter(Files::isRegularFile)) {
// For each file in the zip
walk.forEach(
source -> {
// Compute the target path of the file in the destination folder
Path target = targetRoot.resolve(root.relativize(source).toString()).normalize();
if (target.startsWith(targetRoot)) {
// Only copy safe files
// see: https://snyk.io/research/zip-slip-vulnerability
try {
// Create parent folders if needed
Files.createDirectories(target.getParent());
// Copy the file to the destination
Files.copy(source, target);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.