Java を使用して zip ファイルにファイルを追加する
質問
現在、war ファイルの内容を抽出し、いくつかの新しいファイルをディレクトリ構造に追加して、新しい war ファイルを作成しています。
これはすべて Java からプログラムで行われます。しかし、war ファイルをコピーして、そのファイルを追加するだけのほうが効率的ではないかと考えています。そうすれば、戦争が拡大してから必要になるまで待つ必要がなくなります。再び圧縮されます。
ただし、ドキュメントやオンラインの例ではこれを行う方法が見つからないようです。
誰かヒントやヒントを教えていただけますか?
アップデート:
回答の 1 つで言及されている TrueZip は、zip ファイルに追加するのに非常に優れた Java ライブラリのようです (これを行うことは不可能であるという他の回答にもかかわらず)。
TrueZip に関する経験やフィードバックを持っている人、または他の同様のライブラリを推奨できる人はいますか?
解決
のJava 7で、我々はそのこのジップファイルシステムを得ました手動再パッケージなしジッパー(JAR、WAR)内のファイルを追加して変更することができます。
私たちは、直接、次の例のようにzipファイル内のファイルに書き込むことができます。
Map<String, String> env = new HashMap<>();
env.put("create", "true");
Path path = Paths.get("test.zip");
URI uri = URI.create("jar:" + path.toUri());
try (FileSystem fs = FileSystems.newFileSystem(uri, env))
{
Path nf = fs.getPath("new.txt");
try (Writer writer = Files.newBufferedWriter(nf, StandardCharsets.UTF_8, StandardOpenOption.CREATE)) {
writer.write("hello");
}
}
他のヒント
は、既存のzip(または戦争)にコンテンツを追加することはできません。しかし、それは一時的にディスクに抽出したコンテンツを記述することなく、その場で新しいzipファイルを作成することが可能です。これがものになるかどうかもはるかに高速を推測するのは難しいが、それはあなたが(少なくとも私の知る限りでは)標準的なJavaで取得することができます最速です。カルロスTasadaで述べたように、SevenZipJBindingsはあなたのいくつかの余分な秒を絞り出すかもしれませんが、SevenZipJBindingsにこのアプローチを移植することはまだ同じライブラリと一時ファイルを使用するよりも速くなります。
ここでは、既存のzip(war.zip)の内容を書き込み、新しいファスナー(append.zip)に余分なファイル(answer.txt)を追加し、いくつかのコードです。それが取るすべては、Java 5以降で、余分なライブラリは必要ありません。
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class Main {
// 4MB buffer
private static final byte[] BUFFER = new byte[4096 * 1024];
/**
* copy input to output stream - available in several StreamUtils or Streams classes
*/
public static void copy(InputStream input, OutputStream output) throws IOException {
int bytesRead;
while ((bytesRead = input.read(BUFFER))!= -1) {
output.write(BUFFER, 0, bytesRead);
}
}
public static void main(String[] args) throws Exception {
// read war.zip and write to append.zip
ZipFile war = new ZipFile("war.zip");
ZipOutputStream append = new ZipOutputStream(new FileOutputStream("append.zip"));
// first, copy contents from existing war
Enumeration<? extends ZipEntry> entries = war.entries();
while (entries.hasMoreElements()) {
ZipEntry e = entries.nextElement();
System.out.println("copy: " + e.getName());
append.putNextEntry(e);
if (!e.isDirectory()) {
copy(war.getInputStream(e), append);
}
append.closeEntry();
}
// now append some extra content
ZipEntry e = new ZipEntry("answer.txt");
System.out.println("append: " + e.getName());
append.putNextEntry(e);
append.write("42\n".getBytes());
append.closeEntry();
// close
war.close();
append.close();
}
}
私は、同様の要件がいつかバック持っていた - しかし、それは読み取りと書き込みのzipアーカイブ(.warのフォーマットは類似していなければならない)のためでした。私は、既存のJavaのZipストリームでそれをやってみましたが、面倒な書き込み部分を発見した - 場合は特に、ディレクトリどこ関与。
私はオープン( TrueZIP に試してみることをお勧めしますよソース - あなたが読んで、通常のファイルシステムのように書くことができますそこに仮想ファイルシステムとして任意のアーカイブを公開Apacheのスタイルのライセンス)ライブラリ。それは私にとって魅力のように働いたと大幅に私の開発を簡素化。
あなたは、私が書いたコードのこのビットを使用することができます。
public static void addFilesToZip(File source, File[] files)
{
try
{
File tmpZip = File.createTempFile(source.getName(), null);
tmpZip.delete();
if(!source.renameTo(tmpZip))
{
throw new Exception("Could not make temp file (" + source.getName() + ")");
}
byte[] buffer = new byte[1024];
ZipInputStream zin = new ZipInputStream(new FileInputStream(tmpZip));
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(source));
for(int i = 0; i < files.length; i++)
{
InputStream in = new FileInputStream(files[i]);
out.putNextEntry(new ZipEntry(files[i].getName()));
for(int read = in.read(buffer); read > -1; read = in.read(buffer))
{
out.write(buffer, 0, read);
}
out.closeEntry();
in.close();
}
for(ZipEntry ze = zin.getNextEntry(); ze != null; ze = zin.getNextEntry())
{
out.putNextEntry(ze);
for(int read = zin.read(buffer); read > -1; read = zin.read(buffer))
{
out.write(buffer, 0, read);
}
out.closeEntry();
}
out.close();
tmpZip.delete();
}
catch(Exception e)
{
e.printStackTrace();
}
}
私はあなたが記述何がJavaライブラリを知りません。しかし、あなたが説明することは実用的です。あなたは DotNetZip に使用して、.NETでそれを行うことができます。
マイケルKrauklisは、あなたは、単にwarファイルまたはZIPファイルにデータを「追加」することができないということが正しいですが、厳密にwarファイルで、話す、適応症「ファイルの終わり」があるので、そうではありません。戦争(ZIP)形式はWARファイル内の様々なエントリのメタデータを含むファイルの末尾に通常存在するディレクトリを含むからです。単純にディレクトリへの更新なしでwarファイルの結果に追加すると、あなたはそれに追加ジャンクとの戦争のファイルを持っています。
どのような必要なのは、フォーマットを理解し、適切なディレクトリを含むwarファイルまたはZIPファイルを、更新+読み取ることができるインテリジェントなクラスです。 DotNetZipは、あなたが説明や希望と同じように、そのままのエントリを再圧縮/解凍せずに、これを行います。
Cheesoが言うように、それを行うための方法はありません。ジップフロントエンドは、内部的にあなたとまったく同じことをやっている私の知る限ります。
とにかく、あなたはすべてを圧縮/解凍の速度を心配している場合、あなたは SevenZipJBindings >ライブラリます。
私は<のhref = "http://ctasada.blogspot.com/2009/09/best-way-of-working-with-zip-files-in.html" のrel = "nofollowの中で、このライブラリをカバーnoreferrer ">ブログは(自動プロモーションのため申し訳ありません)いくつかのヶ月前にを。このライブラリは、4秒かかっ使用しながら、単なる例として、java.util.zipを使用して104メガバイトのzipファイルを解凍、私に12秒かかっています。
両方のリンクでは、あなたがそれを使用する方法についての例を見つけることができます。
それがお役に立てば幸いです。
このバグレポートを参照してください。
あなたが本当に非ワーリング/再ワーリングの中間ステップをスキップしたい場合は、のいずれかの種類にアペンドモードを使用します zipファイルやタールなどの構造化データ ファイルは、あなたが本当にできるものではありません 働くことを期待しています。これらのファイル形式 本来の「ファイルの終わり」を持っています 指示は、データフォーマットに組み込まれます。
、あなたは、すべてのzipエントリを取得し、warファイルのファイルを読み、あなたが望んでいた新しいエントリを「追加」の新しい戦争のファイルに書き込むことができたす。完璧な、しかし少なくとも、より自動化されたソリューションではありません。
もう一つの解決法:あなたは、他の状況で役に立つ以下のコードを見つけることができます。私はzipファイルを更新し、jarファイルを生成し、Javaのディレクトリをコンパイルするには、このよう蟻使用している、...
public static void antUpdateZip(String zipFilePath, String libsToAddDir) {
Project p = new Project();
p.init();
Target target = new Target();
target.setName("zip");
Zip task = new Zip();
task.init();
task.setDestFile(new File(zipFilePath));
ZipFileSet zipFileSet = new ZipFileSet();
zipFileSet.setPrefix("WEB-INF/lib");
zipFileSet.setDir(new File(libsToAddDir));
task.addFileset(zipFileSet);
task.setUpdate(true);
task.setProject(p);
task.init();
target.addTask(task);
target.setProject(p);
p.addTarget(target);
DefaultLogger consoleLogger = new DefaultLogger();
consoleLogger.setErrorPrintStream(System.err);
consoleLogger.setOutputPrintStream(System.out);
consoleLogger.setMessageOutputLevel(Project.MSG_DEBUG);
p.addBuildListener(consoleLogger);
try {
// p.fireBuildStarted();
// ProjectHelper helper = ProjectHelper.getProjectHelper();
// p.addReference("ant.projectHelper", helper);
// helper.parse(p, buildFile);
p.executeTarget(target.getName());
// p.fireBuildFinished(null);
} catch (BuildException e) {
p.fireBuildFinished(e);
throw new AssertionError(e);
}
}
この単純なコードは、サーブレットを使用して応答を取得し、応答を送信する
myZipPath = bla bla...
byte[] buf = new byte[8192];
String zipName = "myZip.zip";
String zipPath = myzippath+ File.separator+"pdf" + File.separator+ zipName;
File pdfFile = new File("myPdf.pdf");
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipPath));
ZipEntry zipEntry = new ZipEntry(pdfFile.getName());
out.putNextEntry(zipEntry);
InputStream in = new FileInputStream(pdfFile);
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.closeEntry();
in.close();
out.close();
FileInputStream fis = new FileInputStream(zipPath);
response.setContentType("application/zip");
response.addHeader("content-disposition", "attachment;filename=" + zipName);
OutputStream os = response.getOutputStream();
int length = is.read(buffer);
while (length != -1)
{
os.write(buffer, 0, length);
length = is.read(buffer);
}
ここで資源とApache CommonsのIOを試みる使用リアムの答えのJavaの1.7バージョンです。
出力は、新しいzipファイルに書き込まれますが、簡単に元のファイルに書き込むように変更することができます。
/**
* Modifies, adds or deletes file(s) from a existing zip file.
*
* @param zipFile the original zip file
* @param newZipFile the destination zip file
* @param filesToAddOrOverwrite the names of the files to add or modify from the original file
* @param filesToAddOrOverwriteInputStreams the input streams containing the content of the files
* to add or modify from the original file
* @param filesToDelete the names of the files to delete from the original file
* @throws IOException if the new file could not be written
*/
public static void modifyZipFile(File zipFile,
File newZipFile,
String[] filesToAddOrOverwrite,
InputStream[] filesToAddOrOverwriteInputStreams,
String[] filesToDelete) throws IOException {
try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(newZipFile))) {
// add existing ZIP entry to output stream
try (ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry entry = null;
while ((entry = zin.getNextEntry()) != null) {
String name = entry.getName();
// check if the file should be deleted
if (filesToDelete != null) {
boolean ignoreFile = false;
for (String fileToDelete : filesToDelete) {
if (name.equalsIgnoreCase(fileToDelete)) {
ignoreFile = true;
break;
}
}
if (ignoreFile) {
continue;
}
}
// check if the file should be kept as it is
boolean keepFileUnchanged = true;
if (filesToAddOrOverwrite != null) {
for (String fileToAddOrOverwrite : filesToAddOrOverwrite) {
if (name.equalsIgnoreCase(fileToAddOrOverwrite)) {
keepFileUnchanged = false;
}
}
}
if (keepFileUnchanged) {
// copy the file as it is
out.putNextEntry(new ZipEntry(name));
IOUtils.copy(zin, out);
}
}
}
// add the modified or added files to the zip file
if (filesToAddOrOverwrite != null) {
for (int i = 0; i < filesToAddOrOverwrite.length; i++) {
String fileToAddOrOverwrite = filesToAddOrOverwrite[i];
try (InputStream in = filesToAddOrOverwriteInputStreams[i]) {
out.putNextEntry(new ZipEntry(fileToAddOrOverwrite));
IOUtils.copy(in, out);
out.closeEntry();
}
}
}
}
}
これは、100%に動作します。.. 1)まず、ZIPにファイルを追加し、クラス..
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class AddZip {
public void AddZip() {
}
public void addToZipFile(ZipOutputStream zos, String nombreFileAnadir, String nombreDentroZip) {
FileInputStream fis = null;
try {
if (!new File(nombreFileAnadir).exists()) {//NO EXISTE
System.out.println(" No existe el archivo : " + nombreFileAnadir);return;
}
File file = new File(nombreFileAnadir);
System.out.println(" Generando el archivo '" + nombreFileAnadir + "' al ZIP ");
fis = new FileInputStream(file);
ZipEntry zipEntry = new ZipEntry(nombreDentroZip);
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {zos.write(bytes, 0, length);}
zos.closeEntry();
fis.close();
} catch (FileNotFoundException ex ) {
Logger.getLogger(AddZip.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(AddZip.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
2)あなたは、あなたのコントローラーでそれを呼び出すことができます..
//in the top
try {
fos = new FileOutputStream(rutaZip);
zos = new ZipOutputStream(fos);
} catch (FileNotFoundException ex) {
Logger.getLogger(UtilZip.class.getName()).log(Level.SEVERE, null, ex);
}
...
//inside your method
addZip.addToZipFile(zos, pathFolderFileSystemHD() + itemFoto.getNombre(), "foto/" + itemFoto.getNombre());
以下に、以下を使用してファイルを既存の zip に簡単に追加できる例を示します。 TrueVFS:
// append a file to archive under different name
TFile.cp(new File("existingFile.txt"), new TFile("archive.zip", "entry.txt"));
// recusively append a dir to the root of archive
TFile src = new TFile("dirPath", "dirName");
src.cp_r(new TFile("archive.zip", src.getName()));
TrueZIP の後継である TrueVFS は、必要に応じて内部で Java 7 NIO 2 機能を使用しますが、 さらに多くの機能 スレッドセーフな非同期並列圧縮と同様です。
Java 7 ZipFileSystem はデフォルトで次のとおりであることにも注意してください。 OutOfMemoryError に対して脆弱 巨大なインプットについて。