Java 7zip压缩文件过大。

8
我有一个Java程序,它搜索昨天日期的文件夹,将其压缩为7zip文件并在最后删除它。现在我注意到我的程序生成的7zip归档文件太大了。当我使用像7-Zip文件管理器这样的程序来压缩我的文件时,它会生成一个大小为5 kb的归档文件,而我的程序为相同的文件生成一个大小为737 kb的归档文件(具有873 kb的大小)。现在我担心我的程序没有将其压缩为7zip文件,而是做了普通的zip文件。是否有一种方法可以在我的代码中更改某些内容,以便生成像7-Zip文件管理器那样更小的7zip文件?
package SevenZip;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;

import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;

public class SevenZipUtils {

    public static void main(String[] args) throws InterruptedException, IOException {

        String sourceFolder = "C:/Users/Ferid/Documents/Dates/";
        String outputZipFile = "/Users/Ferid/Documents/Dates";
        int sleepTime = 0;
        compress(sleepTime, outputZipFile, sourceFolder);
    }

    public static boolean deleteDirectory(File directory, int sleepTime) throws InterruptedException {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (null != files) {
                for (int i = 0; i < files.length; i++) {
                    if (files[i].isDirectory()) {
                        deleteDirectory(files[i], sleepTime);
                        System.out.println("Folder deleted: " + files[i]);
                    } else {
                        files[i].delete();
                        System.out.println("File deleted: " + files[i]);
                    }
                }
            }
        }
        TimeUnit.SECONDS.sleep(sleepTime);
        return (directory.delete());
    }

    public static void compress(int sleepTime, String outputZipFile, String sourceFolder)
            throws IOException, InterruptedException {

        // finds folder of yesterdays date
        final Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -1); // date of yesterday
        String timeStamp = new SimpleDateFormat("yyyyMMdd").format(cal.getTime()); // format the date
        System.out.println("Yesterday was " + timeStamp);

        if (sourceFolder.endsWith("/")) { // add yesterday folder to sourcefolder path
            sourceFolder = sourceFolder + timeStamp;
        } else {
            sourceFolder = sourceFolder + "/" + timeStamp;
        }

        if (outputZipFile.endsWith("/")) { // add yesterday folder name to outputZipFile path
            outputZipFile = outputZipFile + " " + timeStamp + ".7z";
        } else {
            outputZipFile = outputZipFile + "/" + timeStamp + ".7z";
        }

        File file = new File(sourceFolder);

        if (file.exists()) {
            try (SevenZOutputFile out = new SevenZOutputFile(new File(outputZipFile))) {
                addToArchiveCompression(out, file, ".");
                System.out.println("Files sucessfully compressed");

                deleteDirectory(new File(sourceFolder), sleepTime);
            }
        } else {
            System.out.println("Folder does not exist");
        }
    }

    private static void addToArchiveCompression(SevenZOutputFile out, File file, String dir) throws IOException {
        String name = dir + File.separator + file.getName();
        if (file.isFile()) {
            SevenZArchiveEntry entry = out.createArchiveEntry(file, name);
            out.putArchiveEntry(entry);

            FileInputStream in = new FileInputStream(file);
            byte[] b = new byte[1024];
            int count = 0;
            while ((count = in.read(b)) > 0) {
                out.write(b, 0, count);
            }
            out.closeArchiveEntry();
            in.close();
            System.out.println("File added: " + file.getName());
        } else if (file.isDirectory()) {
            File[] children = file.listFiles();
            if (children != null) {
                for (File child : children) {
                    addToArchiveCompression(out, child, name);
                }
            }
            System.out.println("Directory added: " + file.getName());
        } else {
            System.out.println(file.getName() + " is not supported");
        }
    }
}

我正在使用Apache Commons Compress库
编辑:这里是一链接,其中包含部分Apache Commons Compress代码。

这可能听起来像一个愚蠢的问题,但你能正确地提取5kb的存档吗? - jhamon
@jhamon 是的,我现在已经尝试过了,我的原始文件夹大小为873 kb,被提取时没有任何问题,就像当我提取由我的java程序生成的那个一样,两者都能顺利地提取而没有任何问题。 - Mad Scientist
1
无法使用7-zip,但873 kB压缩到737 kB的zip和5 kB的7-zip似乎有点不合理。该目录中有多少个文件?有多少个子目录?它们是什么类型的文件? - CristiFati
@CristiFati 7zip的压缩率非常高,所以这在7zip中很常见。在该目录中有28个xml文件和24个子目录,每个子目录都有48个xml文件。 - Mad Scientist
1
7z的性能比zip好,但并不是那么好。然而,它默认使用实体压缩,这是一个很大的节省者。我知道现在已经太晚了,但你可以使用两遍zip压缩来模拟实体压缩,详见我的回答编辑(为后人做准备;))。 - Matthieu
显示剩余2条评论
3个回答

8
Commons Compress对于每个归档条目在容器文件中开始一个新的块。请注意这里的块计数:block-per-file。但是文档中指出,它不支持“实心压缩”——将多个文件写入单个块。请参阅文档中第5段这里的说明。然而,在Java中有几个其他支持LZMA压缩的库。但是我找不到可以在7-Zip的父容器文件格式内进行压缩的库,也许还有其他人知道替代方案……似乎不可能使用普通的zip文件格式(例如通过ZipOutputStream)?

不好意思,普通的zip文件格式太大了。 - Mad Scientist
1
一个普通的压缩文件无法支持固实压缩,因为该格式不允许。 - ggf31416
@ggf31416 你可以通过运行两次来模拟实现固态压缩:第一遍先创建一个包含所有文件但是不进行压缩的zip文件,第二遍再用最大压缩率来压缩这个单一的zip文件(参见我的答案中的最后一段)。这基本上就是tgz... - Matthieu
1
@Matthieu 是的,这是一个很好的观察,但你可以使用几乎任何格式进行tar +压缩或no_compression +压缩实体模拟,并且标准zip的小32KB“字典”(其他任何东西都不再是标准zip deflate)意味着tar.bz2或tar.xz或7z无压缩+ 7z有压缩会有更好的结果。 - ggf31416

5

我没有足够的声望来发表评论了,所以这是我的想法:

  • 我没看到你在哪里设置压缩比,所以可能SevenZOutputFile使用了没有或非常低的压缩。正如@CristiFati所说,压缩差异很奇怪,特别是对于文本文件。
  • 如@df778899所指出的那样,没有支持实心压缩,这是实现最佳压缩比的方法,所以你将无法像7z命令行那样做得好。

话虽如此,如果zip真的不是一个选项,你的最后选择可能是直接在程序中调用正确的命令行

如果纯7z不是必需的,另一个选择是使用类似"tgz"的格式来模拟实心压缩:首先将所有文件压缩为一个未压缩的文件(例如tar格式或没有压缩的zip文件),然后使用标准Java Deflate算法以zip模式压缩该单个文件。当然,只有进一步使用它的过程才能识别该格式。


5
请使用7-Zip文件压缩工具,它可以将832 KB的文件轻松压缩到26.0 KB
  1. 获取其JarSDK
  2. 选择LZMA压缩.java相关文件。
  3. Run参数添加到项目属性中:e“D:\\2017ASP.pdf”“D:\\2017ASP.7z”,“e”代表“编码”,“输入路径”“输出路径”。
  4. 运行该项目[LzmaAlone.java]。

结果

Case1(.pdf文件):从33,969 KB24,645 KB

第二个案例 (.docx 文件): 从 832 KB 减少到 26.0 KB


正确的,而且这个https://commons.apache.org/proper/commons-compress/apidocs/index.html?org/apache/commons/compress/compressors/xz/XZCompressorOutputStream.html也可以使用。 - Saqib Javed

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