压缩包含子文件夹的文件夹

18
public static void main(String argv[]) {
    try {
        String date = new java.text.SimpleDateFormat("MM-dd-yyyy")
                .format(new java.util.Date());
        File inFolder = new File("Output/" + date + "_4D");
        File outFolder = new File("Output/" + date + "_4D" + ".zip");
        ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
                new FileOutputStream(outFolder)));
        BufferedInputStream in = null;
        byte[] data = new byte[1000];
        String files[] = inFolder.list();
        for (int i = 0; i < files.length; i++) {
            in = new BufferedInputStream(new FileInputStream(
                    inFolder.getPath() + "/" + files[i]), 1000);
            out.putNextEntry(new ZipEntry(files[i]));
            int count;
            while ((count = in.read(data, 0, 1000)) != -1) {
                out.write(data, 0, count);
            }
            out.closeEntry();
        }
        out.flush();
        out.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

我正在尝试压缩一个包含子文件夹的文件夹。我正在尝试压缩名为10-18-2010_4D的文件夹。上述程序以以下异常结束。请指导如何解决此问题。

java.io.FileNotFoundException: Output\10-18-2010_4D\4D (Access is denied)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at java.io.FileInputStream.<init>(Unknown Source)
    at ZipFile.main(ZipFile.java:17)

异常中的文件夹名称和您提到的不同。 - ivorykoder
9个回答

34

这是创建ZIP归档文件的代码。创建的归档文件会保留原始目录结构(如果有的话)。

public static void addDirToZipArchive(ZipOutputStream zos, File fileToZip, String parrentDirectoryName) throws Exception {
    if (fileToZip == null || !fileToZip.exists()) {
        return;
    }

    String zipEntryName = fileToZip.getName();
    if (parrentDirectoryName!=null && !parrentDirectoryName.isEmpty()) {
        zipEntryName = parrentDirectoryName + "/" + fileToZip.getName();
    }

    if (fileToZip.isDirectory()) {
        System.out.println("+" + zipEntryName);
        for (File file : fileToZip.listFiles()) {
            addDirToZipArchive(zos, file, zipEntryName);
        }
    } else {
        System.out.println("   " + zipEntryName);
        byte[] buffer = new byte[1024];
        FileInputStream fis = new FileInputStream(fileToZip);
        zos.putNextEntry(new ZipEntry(zipEntryName));
        int length;
        while ((length = fis.read(buffer)) > 0) {
            zos.write(buffer, 0, length);
        }
        zos.closeEntry();
        fis.close();
    }
}

调用此方法后,不要忘记关闭输出流。以下是示例:

public static void main(String[] args) throws Exception {
    FileOutputStream fos = new FileOutputStream("C:\\Users\\vebrpav\\archive.zip");
    ZipOutputStream zos = new ZipOutputStream(fos);
    addDirToZipArchive(zos, new File("C:\\Users\\vebrpav\\Downloads\\"), null);
    zos.flush();
    fos.flush();
    zos.close();
    fos.close();
}

8

你可以使用这个库 Zeroturnaround Zip library 来压缩文件夹。
然后,只需要一行代码就能实现压缩:

ZipUtil.pack(new File("D:\\sourceFolder\\"), new File("D:\\generatedZipFile.zip"));

2
对我的目的来说很有效,一行代码加1分! - Michael Dally
1
这是唯一能够压缩空子文件夹的解决方案。谢谢! - Roberto Rodriguez
1
让我的一天更加美好了。这是一个如此优雅的解决方案。 - Justin Tilson
不错的解决方案。你知道那个库是否仍在积极维护吗? - Alexis Dufrenoy
1
@AlexisDufrenoy 是的,该项目仍在进行中。您可以看到最后一次提交是在2022年3月5日。 - Ahmed Nabil

6

您需要检查文件是否为目录,因为不能将目录传递给zip方法。

请查看此页面,其中显示了如何递归压缩给定目录。


我认为@dogbane是正确的。我使用仅包含文件的目录运行了您的代码,它按预期工作。但是,一旦我添加了一个嵌套目录,就会出现FNF(访问被拒绝)异常。 - Greg Case

3
我会包含 ant task for zipping - 这样更容易使用。
任务类可以在这里找到:org.apache.tools.ant.taskdefs.Zip(以编程方式使用)。

1
你能列举一些关于为什么我们必须优先选择ant任务来压缩文件的理由吗? - ivorykoder
1
它只用了三行代码完成,并且它能够正常运行。与上面进行比较。 - Bozho
@Bozho,我在最近版本的Ant中找到了许多JAR文件。哪一个应该用于压缩文件夹? - LGAP
@LGAP - 请看我的更新。@jassuncao - 你可以编程使用它。 - Bozho
我们不需要添加任何jar文件吗?只导入上述的类就足够了吗? - LGAP
显示剩余4条评论

2
private void zipFiles (ArrayList listWithFiles, String zipName) {
    try {

        byte[] buffer = new byte[1024];

        // create object of FileOutputStream
        FileOutputStream fout = new FileOutputStream(zipName);

        // create object of ZipOutputStream from FileOutputStream
        ZipOutputStream zout = new ZipOutputStream(fout);

        for (String currentFile : listWithFiles) {

            // create object of FileInputStream for source file
            FileInputStream fin = new FileInputStream(currentFile);

            // add files to ZIP
            zout.putNextEntry(new ZipEntry(currentFile ));

            // write file content
            int length;

            while ((length = fin.read(buffer)) > 0) {
                zout.write(buffer, 0, length);
            }

            zout.closeEntry();

            // close the InputStream
            fin.close();
        }

        // close the ZipOutputStream
        zout.close();
    } catch (IOException ioe) {
        System.out.println("IOException :" + ioe);
    }
}

2

以下是我在解决压缩问题时想出的另一个示例。它与其他示例类似,但我在需要更多解释的地方添加了很多注释。Java SE9

public final class Zipper {

private static FileOutputStream fos;
private static ZipOutputStream zos;
private static BufferedOutputStream bos;
private static ZipEntry entry;

private static FileInputStream fis;
private static BufferedInputStream bis;

private static final int BUFFER_CAPACITY = 1024;
private static byte[] buffer;           // The actual buffer a byte array with a capacity of 1024
private static int buffer_size; // The buffer size (not capacity) used by the read()-method of the BufferedInputStream. 



/**
 * This is the method to start things with.
 * @param source File object referencing the unzipped folder to be turned into a zip file.
 * @param target File object referencing the yet to be written zip-file that will contain the entire folder (source)
 * @param compression_level The level of compression expressed by an int-value. See Deflater class for constant details. 
 * @return True if everything worked as planned, false if not.
 */
public static boolean zipFile(File source, File target, int compression_level) {
    boolean check = true;
    try {
        fos = new FileOutputStream(target); // Primary output stream connecting to the file to be zipped
        zos = new ZipOutputStream(fos); // Secondary zip-stream that writes zipped data to the primary stream
        zos.setMethod(ZipOutputStream.DEFLATED);// Method of compression this expression is the default setting 
        zos.setLevel(compression_level);    // Sets the level of compression 0 = no compression 
        bos = new BufferedOutputStream(zos);// Secondary buffered stream that writes to the Zip-stream 
    } catch (IOException e) {
        System.out.println("Zipper.zipFile() says: " + e);
        check = false;
    }
    if (source.isDirectory()) { 
        buffer = new byte[BUFFER_CAPACITY];
        if (manageFolder(source, ""))//Because of recursive character of the called method the second argument
                        //must be empty, if the method is called for the first time.
            check = false;
    } else {                                                    
        buffer = new byte[BUFFER_CAPACITY];
        if (writeFileToZipStream(source, ""))                   
            check = false;
    }
    try {
        zos.finish();
        bos.close();
        zos.close();
        fos.close();
    } catch (Exception e) {
        System.out.println("While closing streams (final), the following happend: " + e);
    }
    return true;
} // end of zipFile()

/**
 * Receives a folder path and extracts all content (files and subfolders) into a File[] if it then detects 
 * another folder it calls itself and passes on the new folder path as the first parameter. 
 * The second parameter is the relative path/name of the current sub-folder
 * seen from the perspective of the root or base folder. As folders get written as part of a file, you don't have to
 * care for folders, just make sure your files carry the all folders in their file name and these file names 
 * are passed on to the ZipEntry.
 * @param source_folder The current folder to be written to the ZipOutputStream. Absolute folder
 * @param name The relative path to the current folder. Empty at first and building up
 * as it goes deeper down the folder hierarchy. 
 * @return True if everything worked as planned, false if not.
 */
private static boolean manageFolder(File source_folder, String name) {
    boolean check = true;       
    File[] all_files = source_folder.listFiles();//Array containing all files and folders of the current folder tier
    for (File single_file : all_files) {        // Iteration over all the files and folders         
        if (single_file.isDirectory()) {     // If a sub-folder is encountered ...
            manageFolder(single_file, name + File.separator + single_file.getName()); // Call the current method with: Arg1 absolute path to current sub-folder, Arg2 name of current folder(s) + "/" + name of current sub-folder  
        } else {            // If a simple file is encountered
            if (!writeFileToZipStream(single_file, name +File.separator + single_file.getName()))   // Call the writeFileToZip()-method with Arg1: absolute path to source file, Arg2 subfolder(s) + "/" + file name 
                check = false;
        }
    }
    return check;
} // end of manageFolder()

/**
 * Writes a file to the BufferedOutputStream.
 * @param source_file Absloute path a file in the source folder
 * @param entry_name Relative path to a file starting at the root or base folder level.
 * @return True if everything worked as planned, false if not.
 */
private static boolean writeFileToZipStream(File source_file, String entry_name) {
    entry_name = entry_name.equals("") ? entry_name : entry_name.substring(1); // Deletes initial "\"   
    boolean check = true;
    try {
        fis = new FileInputStream(source_file);
        bis = new BufferedInputStream(fis, BUFFER_CAPACITY);
        entry = new ZipEntry(entry_name.equals("") ? source_file.getName() : entry_name);   //Reacts to an empty argument 
        zos.putNextEntry(entry);
        while ((buffer_size = bis.read(buffer, 0, BUFFER_CAPACITY)) != -1) { 
            bos.write(buffer, 0, buffer_size);
        }
    } catch (IOException e) {
        System.out.println("Zipper.writeFileToZipStream() says: " + e);
        check = false;
    }
    try {
        bos.flush();            // Don't forget to flush the stream .
        zos.closeEntry();       // Close every entry before you open a new one.
        bis.close();            // The input streams will be attached to every file, so it must be closed after each run.
        fis.close();            // Same here.
    } catch (IOException e) {
        System.out.println("While closing streams (file), the following happend: " + e);
    }
    return check;
 } // end of writeImageFileToZioStream()    
} // end of class

2

以下是我写的内容。 这个例子保留了文件的结构,从而避免了重复条目异常。

/**
     * Compress a directory to ZIP file including subdirectories
     * @param directoryToCompress directory to zip
     * @param outputDirectory     where to place the compress file
     */
    public void zipDirectory(File directoryToCompress, File outputDirectory){
        try {
            FileOutputStream dest = new FileOutputStream(new File(outputDirectory, directoryToCompress.getName() + ".zip"));
            ZipOutputStream zipOutputStream = new ZipOutputStream(dest);

            zipDirectoryHelper(directoryToCompress, directoryToCompress, zipOutputStream);
            zipOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
    }

    private void zipDirectoryHelper(File rootDirectory, File currentDirectory, ZipOutputStream out) throws Exception {
        byte[] data = new byte[2048];

        File[] files = currentDirectory.listFiles();
        if (files == null) {
          // no files were found or this is not a directory

        } else {
            for (File file : files) {
                if (file.isDirectory()) {
                    zipDirectoryHelper(rootDirectory, file, out);
                } else {
                    FileInputStream fi = new FileInputStream(file);
                    // creating structure and avoiding duplicate file names
                    String name = file.getAbsolutePath().replace(rootDirectory.getAbsolutePath(), "");

                    ZipEntry entry = new ZipEntry(name);
                    out.putNextEntry(entry);
                    int count;
                    BufferedInputStream origin = new BufferedInputStream(fi,2048);
                    while ((count = origin.read(data, 0 , 2048)) != -1){
                        out.write(data, 0, count);
                    }
                    origin.close();
                }
            }
        }

    }

1

使用Java 7及以上版本,利用Path、FileVisitor和AutoCloseable接口。要使用此示例,只需调用zipWalking(sourceDir,targetZipFile);即可。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import static java.nio.file.FileVisitResult.CONTINUE;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 *
 * @author deftDeveloper
 */
public class ZippingVisitor extends SimpleFileVisitor<Path> implements java.lang.AutoCloseable {

    private static final Logger LOG = Logger.getLogger(ZippingVisitor.class.getName());
    public static final int BUFFER_SIZE = 4096;
    private final Path _source;
    private final Path _target;
    private final FileOutputStream _fos;
    private final ZipOutputStream _zos;

    public static void zipWalking(Path source, Path target) {
        try (ZippingVisitor zippingVisitor = new ZippingVisitor(source, target)) {
            Files.walkFileTree(source, zippingVisitor);
        } catch (IOException ioe) {
            LOG.log(Level.SEVERE, null, ioe);
        }
    }

    public ZippingVisitor(Path source, Path target) throws FileNotFoundException {
        this._source = source;
        this._target = target;
        _fos = new FileOutputStream(_target.toFile());
        _zos = new ZipOutputStream(_fos);
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        if (!Files.exists(file)) {
            throw new IOException("File " + file.toString() + " not found.");
        }
        Path zipEntryPath = _source.relativize(file);
        LOG.log(Level.FINE, zipEntryPath.toString());
        byte[] buffer = new byte[BUFFER_SIZE];
        try (FileInputStream fis = new FileInputStream(file.toFile())) {
            _zos.putNextEntry(new ZipEntry(zipEntryPath.toString()));
            int length;
            while ((length = fis.read(buffer)) > 0) {
                _zos.write(buffer, 0, length);
            }
            _zos.closeEntry();
        } catch (IOException ioe) {
            LOG.log(Level.SEVERE, null, ioe);
        }
        return CONTINUE;
    }

    @Override
    public void close() throws IOException {
        _zos.close();
        _fos.close();
    }
}

0
以下代码在zip文件中维护了sourceFolder的目录结构。它还可以遍历到Integer.MAX_VALUE的深度。
我发现使用Files.walkFileTree(..)比Files.walk(..)更简单、更清晰。
免责声明:我没有根据问题中粘贴的代码定制答案。这是我的用例,只是为了压缩包含子目录的目录并保持目录结构。
驱动程序代码:createZipOfFolder("results/folder") 输出:results/folder.zip 实现:
public void createZipOfFolder(@NonNull String sourceFolderName) {
        Path sourceFolderPath = Paths.get(sourceFolderName);
        Path zipFilePath = Paths.get(sourceFolderPath + ".zip");
        // This baseFolderPath is upto the parent folder of sourceFolder.
        // Used to remove nesting of parent folder inside zip
        Path baseFolderPath = Paths.get(sourceFolderName.substring(0,
                sourceFolderName.indexOf(sourceFolderPath.getFileName().toString())));

        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFilePath.toFile()))) {
            Files.walkFileTree(sourceFolderPath, new SimpleFileVisitor<>() {
                @Override
                public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
                    System.out.println("Adding Dir: " + dir);
                    // Ending slash is required to persist the folder as folder else it persists as file
                    zos.putNextEntry(new ZipEntry(baseFolderPath.relativize(dir) + "/"));
                    zos.closeEntry();
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
                    System.out.println("Adding file: " + file);
                    zos.putNextEntry(new ZipEntry(baseFolderPath.relativize(file).toString()));
                    Files.copy(file, zos);
                    zos.closeEntry();
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接