如何使用java压缩文件夹本身
Posted
技术标签:
【中文标题】如何使用java压缩文件夹本身【英文标题】:how to zip a folder itself using java 【发布时间】:2013-04-04 20:01:51 【问题描述】:假设我有以下目录结构。
D:\reports\january\
一月内假设有两个 excel 文件,分别是 A.xls 和 B.xls。很多地方都写过如何使用java.util.zip
压缩文件。但我想将一月文件夹本身压缩到报告文件夹中,以便 january 和 january.zip 都将出现在报告中。 (这意味着当我解压缩 january.zip 文件时,我应该得到 january 文件夹)。
任何人都可以使用java.util.zip
向我提供执行此操作的代码。请让我知道这是否可以通过使用其他库更轻松地完成。
非常感谢...
【问题讨论】:
为什么版主不关闭这类问题,它直接违反了第一页的规则“不要问...你没有试图找到答案的问题(显示你的工作!)”。 【参考方案1】:试试这个 zip("C:\testFolder", "D:\testZip.zip")
public void zip( String sourcDirPath, String zipPath) throws IOException
Path zipFile = Files.createFile(Paths.get(zipPath));
Path sourceDirPath = Paths.get(sourcDirPath);
try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipFile));
Stream<Path> paths = Files.walk(sourceDirPath))
paths
.filter(path -> !Files.isDirectory(path))
.forEach(path ->
ZipEntry zipEntry = new ZipEntry(sourceDirPath.relativize(path).toString());
try
zipOutputStream.putNextEntry(zipEntry);
Files.copy(path, zipOutputStream);
zipOutputStream.closeEntry();
catch (IOException e)
System.err.println(e);
);
System.out.println("Zip is created at : "+zipFile);
【讨论】:
【参考方案2】:改进@Nikita Koksharov 的代码在打包空目录时遇到问题。
private void zipDirectory(OutputStream outputStream, Path directoryPath) throws IOException
try (ZipOutputStream zs = new ZipOutputStream(outputStream))
Path pp = directoryPath;
Files.walk(pp)
.forEach(path ->
try
if (Files.isDirectory(path))
zs.putNextEntry(new ZipEntry(pp.relativize(path).toString() + "/"));
else
ZipEntry zipEntry = new ZipEntry(pp.relativize(path).toString());
zs.putNextEntry(zipEntry);
Files.copy(path, zs);
zs.closeEntry();
catch (IOException e)
System.err.println(e);
);
测试使用
FileOutputStream zipOutput = new FileOutputStream("path_to_file.zip");
Path pathOutput = Path.of("path_directory_fid");
zipDirectory(outputStream, pathOutput);
【讨论】:
【参考方案3】:我已经修改了上面的解决方案,将Files.walk
替换为Files.list
。这也假设您要压缩的目录仅包含文件而不包含任何子目录。
private void zipDirectory(Path dirPath) throws IOException
String zipFilePathStr = dirPath.toString() + ".zip";
Path zipFilePath = Files.createFile(Paths.get(zipFilePathStr));
try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(zipFilePath)))
Files.list(dirPath)
.filter(filePath-> !Files.isDirectory(filePath))
.forEach(filePath->
ZipEntry zipEntry = new ZipEntry(dirPath.relativize(filePath).toString());
try
zs.putNextEntry(zipEntry);
Files.copy(filePath, zs);
zs.closeEntry();
catch (IOException e)
System.err.println(e);
);
【讨论】:
【参考方案4】:使用zip4j 你可以简单地做到这一点
ZipFile zipfile = new ZipFile(new File("D:\\reports\\january\\filename.zip"));
zipfile.addFolder(new File("D:\\reports\\january\\"));
它将归档您的文件夹及其中的所有内容。
使用.extractAll
方法将其全部显示出来:
zipfile.extractAll("D:\\destination_directory");
【讨论】:
【参考方案5】:增强的 Java 8+ 示例(来自 Nikita Koksharov's answer)
public static void pack(String sourceDirPath, String zipFilePath) throws IOException
Path p = Files.createFile(Paths.get(zipFilePath));
Path pp = Paths.get(sourceDirPath);
try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(p));
Stream<Path> paths = Files.walk(pp))
paths
.filter(path -> !Files.isDirectory(path))
.forEach(path ->
ZipEntry zipEntry = new ZipEntry(pp.relativize(path).toString());
try
zs.putNextEntry(zipEntry);
Files.copy(path, zs);
zs.closeEntry();
catch (IOException e)
System.err.println(e);
);
Files.walk
已包装在 try with resources
块中,以便可以关闭流。这解决了SonarQube
确定的阻止程序问题。
感谢@Matt Harrison 指出这一点。
【讨论】:
这里的forEach不会关闭,有什么问题? @Elik 我上次检查时一切正常。 “这里的 forEach 不会关闭”是什么意思? 我的意思是forEach好像没有关闭,所以卡在最后,执行也不会终止。 @Elik 确保您传递了有效的 sourceDirPath 和 zipFilePath。pack("D:\\reports\\january\\", "D:\\reports\\january.zip");
之类的东西,自从 the answer I forked from 以来,只有你一个人被投票了近 62 次。【参考方案6】:
包java.util.Zip
可以轻松解决,不需要额外的Jar
文件
只需复制以下代码和run it
与您的IDE
//Import all needed packages
package general;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipUtils
private List <String> fileList;
private static final String OUTPUT_ZIP_FILE = "Folder.zip";
private static final String SOURCE_FOLDER = "D:\\Reports"; // SourceFolder path
public ZipUtils()
fileList = new ArrayList < String > ();
public static void main(String[] args)
ZipUtils appZip = new ZipUtils();
appZip.generateFileList(new File(SOURCE_FOLDER));
appZip.zipIt(OUTPUT_ZIP_FILE);
public void zipIt(String zipFile)
byte[] buffer = new byte[1024];
String source = new File(SOURCE_FOLDER).getName();
FileOutputStream fos = null;
ZipOutputStream zos = null;
try
fos = new FileOutputStream(zipFile);
zos = new ZipOutputStream(fos);
System.out.println("Output to Zip : " + zipFile);
FileInputStream in = null;
for (String file: this.fileList)
System.out.println("File Added : " + file);
ZipEntry ze = new ZipEntry(source + File.separator + file);
zos.putNextEntry(ze);
try
in = new FileInputStream(SOURCE_FOLDER + File.separator + file);
int len;
while ((len = in .read(buffer)) > 0)
zos.write(buffer, 0, len);
finally
in.close();
zos.closeEntry();
System.out.println("Folder successfully compressed");
catch (IOException ex)
ex.printStackTrace();
finally
try
zos.close();
catch (IOException e)
e.printStackTrace();
public void generateFileList(File node)
// add file only
if (node.isFile())
fileList.add(generateZipEntry(node.toString()));
if (node.isDirectory())
String[] subNote = node.list();
for (String filename: subNote)
generateFileList(new File(node, filename));
private String generateZipEntry(String file)
return file.substring(SOURCE_FOLDER.length() + 1, file.length());
参考mkyong..我为当前问题的要求更改了代码
【讨论】:
这会在 zip 中创建一个文件夹,其中包含压缩文件夹的名称。为了避免这种情况,而不是 ZipEntry ze = new ZipEntry(source + File.separator + file);使用 ZipEntry ze = new ZipEntry(file); 这在 windows 和 linux/android 之间是不兼容的,因为 node.toString() 将提供一个特定于环境的路径分隔符。作为一种解决方法: generateZipEntry(root, node.getAbsolutePath().replaceAll("\\\\", "/"))【参考方案7】:此方法压缩文件夹并将所有子文件和文件夹(包括空文件夹)添加到 zip 文件中。
void zipFolder(Path sourceDir, Path targetFile) throws IOException
ZipDirectoryVisitor zipVisitor = new ZipDirectoryVisitor(sourceDir);
Files.walkFileTree(sourceDir, zipVisitor);
FileOutputStream fos = new FileOutputStream(targetFile.toString());
ZipOutputStream zos = new ZipOutputStream(fos);
byte[] buffer = new byte[1024];
for (ZipEntry entry : zipVisitor.getZipEntries())
zos.putNextEntry(entry);
Path curFile = Paths.get(sourceDir.getParent().toString(), entry.toString());
if (!curFile.toFile().isDirectory())
FileInputStream in = new FileInputStream(Paths.get(sourceDir.getParent().toString(), entry.toString()).toString());
int len;
while ((len = in.read(buffer)) > 0)
zos.write(buffer, 0, len);
in.close();
zos.closeEntry();
zos.close();
这里是 ZipDirectoryVisitor 的实现:
class ZipDirectoryVisitor extends SimpleFileVisitor<Path>
private Path dirToZip;
private List<ZipEntry> zipEntries; // files and folders inside source folder as zip entries
public ZipDirectoryVisitor(Path dirToZip) throws IOException
this.dirToZip = dirToZip;
zipEntries = new ArrayList<>();
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException
// According to zip standard backslashes
// should not be used in zip entries
String zipFile = dirToZip.getParent().relativize(path).toString().replace("\\", "/");
ZipEntry entry = new ZipEntry(zipFile);
zipEntries.add(entry);
return FileVisitResult.CONTINUE;
@Override
public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes basicFileAttributes) throws IOException
String zipDir = dirToZip.getParent().relativize(path).toString().replace("\\", "/");
// Zip directory entries should end with a forward slash
ZipEntry entry = new ZipEntry(zipDir + "/");
zipEntries.add(entry);
return FileVisitResult.CONTINUE;
@Override
public FileVisitResult visitFileFailed(Path path, IOException e) throws IOException
System.err.format("Could not visit file %s while creating a file list from file tree", path);
return FileVisitResult.TERMINATE;
public List<ZipEntry> getZipEntries()
return zipEntries;
【讨论】:
【参考方案8】:我发现这个解决方案对我来说非常有效。不需要任何第三方api
'test'实际上是一个文件夹,里面有很多文件。
String folderPath= "C:\Users\Desktop\test";
String zipPath = "C:\Users\Desktop\test1.zip";
private boolean zipDirectory(String folderPath, String zipPath) throws IOException
byte[] buffer = new byte[1024];
FileInputStream fis = null;
ZipOutputStream zos = null;
try
zos = new ZipOutputStream(new FileOutputStream(zipPath));
updateSourceFolder(new File(folderPath));
if (sourceFolder == null)
zos.close();
return false;
generateFileAndFolderList(new File(folderPath));
for (String unzippedFile: fileList)
System.out.println(sourceFolder + unzippedFile);
ZipEntry entry = new ZipEntry(unzippedFile);
zos.putNextEntry(entry);
if ((unzippedFile.substring(unzippedFile.length()-1)).equals(File.separator))
continue;
try
fis = new FileInputStream(sourceFolder + unzippedFile);
int len=0;
while ((len = fis.read(buffer))>0)
zos.write(buffer,0,len);
catch(IOException e)
return false;
finally
if (fis != null)
fis.close();
zos.closeEntry();
catch(IOException e)
return false;
finally
zos.close();
fileList = null;
sourceFolder = null;
return true;
private void generateFileAndFolderList(File node)
if (node.isFile())
fileList.add(generateZipEntry(node.getAbsoluteFile().toString()));
if (node.isDirectory())
String dir = node.getAbsoluteFile().toString();
fileList.add(dir.substring(sourceFolder.length(), dir.length()) + File.separator);
String[] subNode = node.list();
for (String fileOrFolderName : subNode)
generateFileAndFolderList(new File(node, fileOrFolderName));
private void updateSourceFolder(File node)
if (node.isFile() || node.isDirectory())
String sf = node.getAbsoluteFile().toString();
sourceFolder = sf.substring(0, (sf.lastIndexOf("/") > 0 ? sf.lastIndexOf("/") : sf.lastIndexOf("\\")));
sourceFolder += File.separator;
else
sourceFolder = null;
private String generateZipEntry(String file)
return file.substring(sourceFolder.length(), file.length());
【讨论】:
【参考方案9】:这是 Java 8+ 示例:
public static void pack(String sourceDirPath, String zipFilePath) throws IOException
Path p = Files.createFile(Paths.get(zipFilePath));
try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(p)))
Path pp = Paths.get(sourceDirPath);
Files.walk(pp)
.filter(path -> !Files.isDirectory(path))
.forEach(path ->
ZipEntry zipEntry = new ZipEntry(pp.relativize(path).toString());
try
zs.putNextEntry(zipEntry);
Files.copy(path, zs);
zs.closeEntry();
catch (IOException e)
System.err.println(e);
);
【讨论】:
小改进:将 ZipOutpuStream 放入 try (...) ;对于 sp,我添加了 .replace("\\", "/");将 sp + "/" + path... 替换为 sp + path... 获取ZipEntry的相对路径可以简化为new ZipEntry(pp.relativize(path).toString())
如果文件很大,Files.readAllBytes() 不会导致内存膨胀吗?
@Keshav 你是对的,你能提出任何解决方案吗?
throw new RuntimeException(e)
优于 System.err.println(e)
【参考方案10】:
Java 6 +
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Zip
private static final FileFilter FOLDER_FILTER = new FileFilter()
@Override
public boolean accept(File pathname)
return pathname.isDirectory();
;
private static final FileFilter FILE_FILTER = new FileFilter()
@Override
public boolean accept(File pathname)
return pathname.isFile();
;
private static void compress(File file, ZipOutputStream outputStream, String path) throws IOException
if (file.isDirectory())
File[] subFiles = file.listFiles(FILE_FILTER);
if (subFiles != null)
for (File subFile : subFiles)
compress(subFile, outputStream, new File(path, subFile.getName()).getAbsolutePath());
File[] subDirs = file.listFiles(FOLDER_FILTER);
if (subDirs != null)
for (File subDir : subDirs)
compress(subDir, outputStream, new File(path, subDir.getName()).getAbsolutePath());
else if (file.exists())
outputStream.putNextEntry(new ZipEntry(path));
FileInputStream inputStream = new FileInputStream(file);
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) >= 0)
outputStream.write(buffer, 0, len);
outputStream.closeEntry();
public static void compress(String dirPath, String zipFilePath) throws IOException
File file = new File(dirPath);
final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zipFilePath));
compress(file, outputStream, "/");
outputStream.close();
【讨论】:
【参考方案11】:这是一个非常简洁的 Java 7+ 解决方案,它完全依赖于普通 JDK 类,不需要第三方库:
public static void pack(final Path folder, final Path zipFilePath) throws IOException
try (
FileOutputStream fos = new FileOutputStream(zipFilePath.toFile());
ZipOutputStream zos = new ZipOutputStream(fos)
)
Files.walkFileTree(folder, new SimpleFileVisitor<Path>()
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
zos.putNextEntry(new ZipEntry(folder.relativize(file).toString()));
Files.copy(file, zos);
zos.closeEntry();
return FileVisitResult.CONTINUE;
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
zos.putNextEntry(new ZipEntry(folder.relativize(dir).toString() + "/"));
zos.closeEntry();
return FileVisitResult.CONTINUE;
);
它复制folder
中的所有文件,包括空目录,并在zipFilePath
创建一个zip 存档。
【讨论】:
如果你在window上创建zip并在linux上使用这个zip,这个方法会有问题! 你能说得更具体点吗?你遇到了什么样的问题? Method Fonder.relativize(dir) 使用系统分隔符,在 Windows 的情况下是 \,当您在窗口系统上生成 zip 并在 unix 系统上使用它时,这是一个问题。您将看到其文件夹之外的文件。还有其他问题,在 Linux previsitDirectory 上也访问“”目录,这样,如果您在 Linux 系统上将文件夹压缩到名为“test.zip”的 zip 中,您将在该 zip 中找到所有文件以及一个名为“的空文件夹”测试” 您可以尝试将zos.putNextEntry(new ZipEntry(folder.relativize(file).toString()));
替换为zos.putNextEntry(new ZipEntry(folder.relativize(file).toString().replace("\\","/")));
,看看是否可以解决Windows 路径问题?如果是这样,我会更新答案。我没有设法重现您的其他问题。
这对我有用,在 Windows 上没有问题,只是跳过对“preVisitDirectory”的覆盖对我有用(因为它是相对的,所以会生成所有子文件夹)。据我所知,只有一个缺点:空文件夹被跳过【参考方案12】:
您尝试过Zeroturnaround Zip 库吗?真的很整洁!压缩文件夹只是一个衬里:
ZipUtil.pack(new File("D:\\reports\\january\\"), new File("D:\\reports\\january.zip"));
(感谢Oleg Šelajev的例子)
【讨论】:
如果我需要再添加一个文件,在文件夹级别如何使用这个库来完成 创建一个文件数组 ZipEntrySource[] arr 然后使用这个方法: ZipUtil.pack(arr, new File(outZipPath))【参考方案13】:试试这个:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Zip
public static void main(String[] a) throws Exception
zipFolder("D:\\reports\\january", "D:\\reports\\january.zip");
static public void zipFolder(String srcFolder, String destZipFile) throws Exception
ZipOutputStream zip = null;
FileOutputStream fileWriter = null;
fileWriter = new FileOutputStream(destZipFile);
zip = new ZipOutputStream(fileWriter);
addFolderToZip("", srcFolder, zip);
zip.flush();
zip.close();
static private void addFileToZip(String path, String srcFile, ZipOutputStream zip)
throws Exception
File folder = new File(srcFile);
if (folder.isDirectory())
addFolderToZip(path, srcFile, zip);
else
byte[] buf = new byte[1024];
int len;
FileInputStream in = new FileInputStream(srcFile);
zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
while ((len = in.read(buf)) > 0)
zip.write(buf, 0, len);
static private void addFolderToZip(String path, String srcFolder, ZipOutputStream zip)
throws Exception
File folder = new File(srcFolder);
for (String fileName : folder.list())
if (path.equals(""))
addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip);
else
addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip);
【讨论】:
【参考方案14】:Java 7+,commons.io
public final class ZipUtils
public static void zipFolder(final File folder, final File zipFile) throws IOException
zipFolder(folder, new FileOutputStream(zipFile));
public static void zipFolder(final File folder, final OutputStream outputStream) throws IOException
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream))
processFolder(folder, zipOutputStream, folder.getPath().length() + 1);
private static void processFolder(final File folder, final ZipOutputStream zipOutputStream, final int prefixLength)
throws IOException
for (final File file : folder.listFiles())
if (file.isFile())
final ZipEntry zipEntry = new ZipEntry(file.getPath().substring(prefixLength));
zipOutputStream.putNextEntry(zipEntry);
try (FileInputStream inputStream = new FileInputStream(file))
IOUtils.copy(inputStream, zipOutputStream);
zipOutputStream.closeEntry();
else if (file.isDirectory())
processFolder(file, zipOutputStream, prefixLength);
【讨论】:
如果你想移除对 commons.io 的依赖,IOUtils.copy
方法很简单:byte [] buffer = new byte[1024 * 4]; int read = 0; while ((read = input.read(buffer)) != -1) output.write(buffer, 0, read);
【参考方案15】:
我通常使用我曾经为此任务编写的辅助类:
import java.util.zip.*;
import java.io.*;
public class ZipExample
public static void main(String[] args)
ZipHelper zippy = new ZipHelper();
try
zippy.zipDir("folderName","test.zip");
catch(IOException e2)
System.err.println(e2);
class ZipHelper
public void zipDir(String dirName, String nameZipFile) throws IOException
ZipOutputStream zip = null;
FileOutputStream fW = null;
fW = new FileOutputStream(nameZipFile);
zip = new ZipOutputStream(fW);
addFolderToZip("", dirName, zip);
zip.close();
fW.close();
private void addFolderToZip(String path, String srcFolder, ZipOutputStream zip) throws IOException
File folder = new File(srcFolder);
if (folder.list().length == 0)
addFileToZip(path , srcFolder, zip, true);
else
for (String fileName : folder.list())
if (path.equals(""))
addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip, false);
else
addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip, false);
private void addFileToZip(String path, String srcFile, ZipOutputStream zip, boolean flag) throws IOException
File folder = new File(srcFile);
if (flag)
zip.putNextEntry(new ZipEntry(path + "/" +folder.getName() + "/"));
else
if (folder.isDirectory())
addFolderToZip(path, srcFile, zip);
else
byte[] buf = new byte[1024];
int len;
FileInputStream in = new FileInputStream(srcFile);
zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
while ((len = in.read(buf)) > 0)
zip.write(buf, 0, len);
【讨论】:
您应该在完成后关闭 FileInputStream 的输入流。【参考方案16】:我会使用Apache Ant,它有一个 API 可以从 Java 代码而不是 XML 构建文件调用任务。
Project p = new Project();
p.init();
Zip zip = new Zip();
zip.setProject(p);
zip.setDestFile(zipFile); // a java.io.File for the zip you want to create
zip.setBasedir(new File("D:\\reports"));
zip.setIncludes("january/**");
zip.perform();
这里我告诉它从基本目录D:\reports
开始,然后压缩january
文件夹和其中的所有内容。生成的 zip 文件中的路径将与相对于 D:\reports
的原始路径相同,因此它们将包含 january
前缀。
【讨论】:
我的org.apache.ant.compress.taskdefs.Zip
版本(org.apache.ant:ant-compress:1.5
)没有setBasedir
以上是关于如何使用java压缩文件夹本身的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java 压缩/解压缩文件夹及其所有文件和子目录?
java - 如何从不同的文件夹压缩多个文件并使用java将压缩文件存储在一个文件夹中