我目前正在提取war文件的内容,然后将一些新文件添加到目录结构中,然后创建一个新的war文件。
这一切都是以编程方式从Java完成的 - 但我想知道复制war文件然后只是附加文件是否效率更高 - 那么只要战争扩展并且我就不必等待然后必须再次压缩。
我似乎无法在文档或任何在线示例中找到这样做的方法。
任何人都可以提供一些提示或指示吗?
更新:
其中一个答案中提到的TrueZip似乎是一个非常好的java库附加到一个zip文件(尽管其他答案说它不可能这样做)。
任何人都有关于TrueZip的经验或反馈,或者可以推荐其他类似的图书馆吗?
答案 0 :(得分:78)
在Java 7中,我们得到Zip File System,允许在zip(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");
}
}
答案 1 :(得分:43)
正如其他人所提到的,不可能将内容附加到现有的zip(或战争)。但是,可以动态创建新的zip,而无需将提取的内容临时写入磁盘。很难猜测它会有多快,但它是用标准Java获得的最快(至少据我所知)。正如Carlos Tasada所提到的,SevenZipJBindings可能会挤出你一些额外的时间,但是将这种方法移植到SevenZipJBindings仍然比使用具有相同库的临时文件更快。
这里有一些编写现有zip(war.zip)内容的代码,并将一个额外的文件(answer.txt)附加到一个新的zip(append.zip)。所需要的只是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();
}
}
答案 2 :(得分:25)
我曾经有过类似的要求 - 但它是用于读取和编写zip档案(.war格式应该类似)。我尝试使用现有的Java Zip流程,但发现写入部分很麻烦 - 尤其是涉及到的目录时。
我建议您试用TrueZIP(开源 - apache样式许可)库,将任何存档公开为虚拟文件系统,您可以像普通文件系统一样将其读写。它对我来说就像一个魅力,并大大简化了我的发展。
答案 3 :(得分:14)
您可以使用我写的这段代码
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();
}
}
答案 4 :(得分:2)
我不知道Java库是否符合您的描述。但你所描述的是实用的。您可以使用DotNetZip在.NET中执行此操作。
Michael Krauklis是正确的,你不能简单地将数据“附加”到war文件或zip文件中,但这并不是因为严格来说,在war文件中存在“文件结束”指示。这是因为war(zip)格式包括一个目录,该目录通常出现在文件的末尾,其中包含war文件中各种条目的元数据。天真地附加到war文件导致没有更新目录,所以你只需要一个带有垃圾的war文件。
什么是必要的智能类,了解格式,并可以读取+更新war文件或zip文件,包括适当的目录。 DotNetZip执行此操作,无需解压缩/重新压缩未更改的条目,就像您描述或期望的那样。
答案 5 :(得分:2)
正如Cheeso所说,没有办法做到这一点。拉链前端的AFAIK与你内部完全一样。
无论如何,如果您担心提取/压缩所有内容的速度,您可能需要尝试SevenZipJBindings库。
几个月前我在我的blog覆盖了这个库(对不起自动推广)。举个例子,使用java.util.zip提取一个104MB的zip文件花了我12秒,而使用这个库需要4秒。
在这两个链接中,您可以找到有关如何使用它的示例。
希望它有所帮助。
答案 6 :(得分:1)
请参阅此bug report。
在任何类型上使用追加模式 结构化数据,如zip文件或tar 文件不是你真的可以 期待工作。这些文件格式 有一个内在的“文件结束” 内置于数据格式的指示。
如果你真的想跳过un-waring / re-waring的中间步骤,你可以阅读war文件文件,获取所有的zip条目,然后写入一个新的war文件“追加”你想要的新条目加上。不完美,但至少是一个更自动化的解决方案。
答案 7 :(得分:1)
又一个解决方案:您可能会发现以下代码在其他情况下也很有用。我用这种方式编译Java目录,生成jar文件,更新zip文件,......
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);
}
}
答案 8 :(得分:1)
这是一个使用servlet获取响应并发送响应的简单代码
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);
}
答案 9 :(得分:0)
以下是使用try with resources和Apache Commons IO的Liam答案的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();
}
}
}
}
}
答案 10 :(得分:0)
如果您不想使用额外的库,这可以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());
答案 11 :(得分:0)
以下是使用TrueVFS:
将文件轻松附加到现有zip的示例// 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()));
TrueVFS是TrueZIP的继承者,在适当的时候使用Java 7 NIO 2功能,但提供much more features类似线程安全的异步并行压缩。
请注意Java 7 ZipFileSystem在巨大输入上默认为vulnerable to OutOfMemoryError。
答案 12 :(得分:0)
根据上面@sfussenegger给出的答案,以下代码用于附加到jar文件并下载:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Resource resourceFile = resourceLoader.getResource("WEB-INF/lib/custom.jar");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos, StandardCharsets.ISO_8859_1);) {
try (ZipFile zin = new ZipFile(resourceFile.getFile(), StandardCharsets.ISO_8859_1);) {
zin.stream().forEach((entry) -> {
try {
zos.putNextEntry(entry);
if (!entry.isDirectory()) {
zin.getInputStream(entry).transferTo(zos);
}
zos.closeEntry();
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
/* build file records to be appended */
....
for (FileContents record : records) {
zos.putNextEntry(new ZipEntry(record.getFileName()));
zos.write(record.getBytes());
zos.closeEntry();
}
zos.flush();
}
response.setContentType("application/java-archive");
response.setContentLength(baos.size());
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"custom.jar\"");
try (BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream())) {
baos.writeTo(out);
}
}