我有一个由 7zip 程序创建的文件。我使用 deflate 方法来压缩它。现在我想在
java
中创建相同的存档(具有相同的 MD5sum)。当我创建 zip 文件时,我使用了在互联网上找到的算法,例如 http://www.kodejava.org/examples/119.html 但是当我用这种方法创建 zip 文件时,压缩大小高于未压缩文件的大小那么发生了什么?这不是一个非常有用的压缩。那么如何创建与使用 7zip 程序创建的 zip 文件完全相同的 zip 文件呢?如果有帮助的话,我有关于我在 7zip 程序中创建的 zip 文件的所有信息。
// simplified code for zip creation in java
import java.io.*;
import java.util.zip.*;
public class ZipCreateExample {
public static void main(String[] args) throws Exception {
// input file
FileInputStream in = new FileInputStream("F:/sometxt.txt");
// out put file
ZipOutputStream out = new ZipOutputStream(new FileOutputStream("F:/tmp.zip"));
// name the file inside the zip file
out.putNextEntry(new ZipEntry("zippedjava.txt"));
// buffer size
byte[] b = new byte[1024];
int count;
while ((count = in.read(b)) > 0) {
out.write(b, 0, count);
}
out.close();
in.close();
}
}
澄清一下,您在原版中使用了 7zip 中的 ZIP 算法吗? 7zip 还声称其压缩比比其他供应商高 2-10%。我大胆猜测,Java 中内置的 ZIP 算法远没有 7zip 中的优化那么好。如果您想要类似的压缩文件,最好是从命令行调用 7zip。
您是否尝试解压 ZIP 文件,更改其中的文件,然后重新压缩它以使其具有相同的 MD5 哈希值?哈希值旨在防止您这样做。
ZipOutputStream 有几种调整压缩的方法:
设置默认压缩方法 用于后续条目。这个默认的 每当压缩时都会使用 没有指定方法 单独的 ZIP 文件条目,并且是 最初设置为 DEFLATED。
public void setLevel(int level)
设置压缩级别 后续条目被压缩。 默认设置是 DEFAULT_COMPRESSION。 level - 压缩级别 (0-9)
当您在以下内容之后添加:
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(target));
zos.setMethod( ZipOutputStream.DEFLATED );
zos.setLevel( 5 );
...
它不会改善你的压缩吗?
这是一个函数,您传递绝对路径,它将创建一个与目录同名的 zip 文件(您想要在该目录下压缩所有子文件夹和文件,一切!),并在成功时返回 true,在成功时返回 false如有例外。
public class FileUtil {
final static int BUFFER = 2048;
private static Logger log = Logger.getLogger(FileUtil.class);
public static boolean createZipArchive(String srcFolder) {
try {
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(new File(srcFolder+ ".zip"));
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
byte data[] = new byte[BUFFER];
File subDir = new File(srcFolder);
String subdirList[] = subDir.list();
for(String sd:subdirList)
{
// get a list of files from current directory
File f = new File(srcFolder+"/"+sd);
if(f.isDirectory())
{
String files[] = f.list();
for (int i = 0; i < files.length; i++) {
System.out.println("Adding: " + files[i]);
FileInputStream fi = new FileInputStream(srcFolder + "/"+sd+"/" + files[i]);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(sd +"/"+files[i]);
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
out.flush();
}
}
}
else //it is just a file
{
FileInputStream fi = new FileInputStream(f);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(sd);
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
out.flush();
}
}
}
origin.close();
out.flush();
out.close();
} catch (Exception e) {
log.info("createZipArchive threw exception: " + e.getMessage());
return false;
}
return true;
}
}
要从同一源文件生成两个相同的 zip 文件(包括相同的 md5sum),我建议使用相同的 zip 实用程序 - 要么始终使用相同的 Java 程序,要么始终使用 7zip。
例如,7zip 实用程序有很多选项,其中许多只是可以自定义的默认值(或者版本之间有所不同?),并且任何 Java zip 实现都必须显式设置这些选项。 如果您的 Java 应用程序可以简单地调用外部“7z”程序,那么您可能会获得比自定义 Java zip 实现更好的性能。 (这也是映射缩减问题的一个很好的例子,您可以轻松地扩展实现。)但是,如果您有服务器端生成的 zip 文件和客户端生成的 zip 文件,您将遇到的主要问题是 zip 文件除了原始文件之外还存储两件事:(1)文件名, (2) 文件时间戳。 如果其中任何一个已更改,则生成的 zip 文件将具有不同的 md5sum:
$ ls tst1/
foo.tar
$ cp -r tst1 tst2
$ ( cd tst1; zip foo.zip foo.tar ) ; ( cd tst2; zip foo.zip foo.tar ) ; md5sum tst?/foo.zip
updating: foo.tar (deflated 20%)
updating: foo.tar (deflated 20%)
359b82678a2e17c1ddbc795ceeae7b60 tst1/foo.zip
b55c33c0414ff987597d3ef9ad8d1d08 tst2/foo.zip
但是,使用“cp -p”(保留时间戳):
$ cp -p -r tst1 tst2
$ ( cd tst1; zip foo.zip foo.tar ) ; ( cd tst2; zip foo.zip foo.tar ) ; md5sum tst?/foo.zip
updating: foo.tar (deflated 20%)
updating: foo.tar (deflated 20%)
359b82678a2e17c1ddbc795ceeae7b60 tst1/foo.zip
359b82678a2e17c1ddbc795ceeae7b60 tst2/foo.zip
即使 zip 内的文件相同,您也会发现不同的文件名和路径会出现相同的问题。
package comm;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;*emphasized text*
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Zip1 {
public static void main( String[] args )
{
byte[] buffer = new byte[1024];
try{
File f= new File("E:\\");
f.mkdirs();
File origFile= new File(f,"MyZipFile2.zip");
FileOutputStream fos = new FileOutputStream(origFile);
ZipOutputStream zos = new ZipOutputStream(fos);
ZipEntry ze= new ZipEntry("test.pdf");
zos.putNextEntry(ze);
FileInputStream in = new FileInputStream("D:\\Test.pdf");
int len;
while ((len = in.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
in.close();
zos.closeEntry();
//remember close it
zos.close();
System.out.println("Done");
}catch(IOException ex){
ex.printStackTrace();
}
}
}
unzip功能的代码。希望它可以帮助某人。
package com.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* @author dinesh.lomte
*
*/
public class ZipUtil {
/**
*
* @param source
* @param destination
*/
public static void unZip(String source, String destination) {
String method = "unZip(String source, String destination)";
ZipInputStream zipInputStream = null;
try {
// Creating the ZipInputStream instance from the source file
zipInputStream = new ZipInputStream(new FileInputStream(source));
// Getting the zipped file list entry
ZipEntry zipEntry = zipInputStream.getNextEntry();
// Iterating through the file list entry
while (zipEntry != null) {
String fileName = zipEntry.getName();
File file = new File(new StringBuilder(destination)
.append(File.separator)
.append(AppUtil.getFileNameWithoutExtension(
AppUtil.getNameFromPath(source)))
.append(File.separator).append(fileName).toString());
// Creating non existing folders to avoid any FileNotFoundException
// for compressed folder
new File(file.getParent()).mkdirs();
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int length;
while ((length = zipInputStream.read(buffer)) > 0) {
fileOutputStream.write(buffer, 0, length);
}
fileOutputStream.close();
zipEntry = zipInputStream.getNextEntry();
}
} catch (IOException iOException) {
System.out.println("Failed to unzip the ''{0}'' file located in ''{1}'' folder. Due to, {2}");
} finally {
// Validating if zipInputStream instance in not null
if (zipInputStream != null) {
try {
zipInputStream.closeEntry();
zipInputStream.close();
} catch (IOException iOException) {
}
}
}
}
/**
* Traverse a directory from the source folder location and get all files,
* and add the file into files list.
*
* @param node
*/
public static void generateFileList(
String source, File node, List<String> files) {
// Validating if the node is a file
if (node.isFile()) {
files.add(generateZipEntry(
source, node.getPath().toString()));
}
// Validating if the node is a directory
if (node.isDirectory()) {
String[] subNote = node.list();
for (String filename : subNote) {
generateFileList(source, new File(node, filename), files);
}
}
}
/**
* Format the file path to zip
* @param source
* @param file
* @return
*/
private static String generateZipEntry(String source, String file) {
return file.substring(source.length(), file.length());
}
/**
*
* @param source
* @param destination
*/
public static void zip(String source, String destination) {
String method = "zip(String source, String destination)";
ZipOutputStream zipOutputStream = null;
try {
// Creating the zipOutputStream instance
zipOutputStream = new ZipOutputStream(
new FileOutputStream(destination));
List<String> files = new ArrayList<>();
generateFileList(source, new File(source), files);
// Iterating the list of file(s) to zip/compress
for (String file : files) {
// Adding the file(s) to the zip
ZipEntry zipEntry = new ZipEntry(file);
zipOutputStream.putNextEntry(zipEntry);
FileInputStream fileInputStream = new FileInputStream(
new StringBuilder(source).append(File.separator)
.append(file).toString());
int length;
byte[] buffer = new byte[1024];
while ((length = fileInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, length);
}
// Closing the fileInputStream instance
fileInputStream.close();
// De-allocating the memory by assigning the null value
fileInputStream = null;
}
} catch (IOException iOException) {
System.out.println("Failed to zip the file(s) located in ''{0}'' folder. Due to, {1}");
} finally {
// Validating if zipOutputStream instance in not null
if (zipOutputStream != null) {
try {
zipOutputStream.closeEntry();
zipOutputStream.close();
} catch (IOException iOException) {
}
}
}
}
}
,专门做OP要求的事情——完全控制 Zip 输出。 我编写这个库是因为我找不到 Zip 替代品,它可以让我对文件头或中央目录条目中的元数据进行细粒度控制。这是一个老问题,但在过去的一个月里,我一直在使用我的
SimpleZip Java 包
有一个 ZipFileCopy.java
示例程序,它读取 zip 并再次将其写出,而不更改字节。 它有点作弊,因为它不会对数据进行膨胀和收缩,但元数据会被精确地再现。当我用这种方法创建 zip 文件时,压缩后的大小大于未压缩文件的大小
类,我不确定窗口大小等是否与 7zip 程序匹配。 尽管我的库使用每个文件的 Zip 标志来确定每个文件条目的压缩级别,但它们似乎并不总是由 Zip 实现分配。 如果没有它们,我的图书馆将使用默认级别(我认为是 6)。对于我的库,最困难的部分是确定使用的压缩级别并配置用于生成相同字节的压缩器算法。 我只是委托给 JDK 内部
java.util.zip.Deflater
虽然复制代码有一些注释,但也有一些SimpleZip 的在线文档。