Подтвердить что ты не робот

Добавление файлов в zip файл с помощью Java

В настоящее время я извлекаю содержимое военного файла, а затем добавляю некоторые новые файлы в структуру каталогов, а затем создаю новый военный файл.

Все это делается программно с Java, но мне интересно, не будет ли более эффективно копировать военный файл, а затем просто добавлять файлы - тогда мне не придется ждать, пока война расширится и затем необходимо снова сжать.

Я не могу найти способ сделать это в документации, хотя или в любых онлайн-примерах.

Кто-нибудь может дать несколько советов или указателей?

UPDATE:

TrueZip, как упоминается в одном из ответов, кажется очень хорошей библиотекой java для добавления в zip файл (несмотря на другие ответы, которые говорят, что это невозможно).

У кого-нибудь есть опыт или отзывы на TrueZip или вы можете рекомендовать другие подобные библиотеки?

4b9b3361

Ответ 1

В 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");
    }
}

Ответ 2

Как уже упоминалось, невозможно добавить контент к существующему zip (или войне). Тем не менее, можно создать новый zip "на лету" без временного написания извлеченного содержимого на диск. Трудно догадаться, насколько это будет быстрее, но это самое быстрое, что вы можете получить (по крайней мере, насколько мне известно) со стандартной Java. Как уже упоминал Карлос Тасада, 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();
    }
}

Ответ 3

У меня было подобное требование когда-то назад - но это было для чтения и записи zip-архивов (формат .war должен быть похож). Я попытался сделать это с существующими потоками Java Zip, но обнаружил, что записывающая часть громоздка - особенно в тех каталогах, где они задействованы.

Я порекомендую вам попробовать TrueZIP (библиотека с открытым исходным кодом - в стиле Apache), которая предоставляет любой архив в качестве виртуальную файловую систему, в которую вы можете читать и писать как обычную файловую систему. Это работало как прелесть для меня и значительно упростило мое развитие.

Ответ 4

Вы можете использовать этот бит кода, который я написал

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();
    }
}

Ответ 5

Я не знаю о библиотеке Java, которая делает то, что вы описываете. Но то, что вы описали, практично. Вы можете сделать это в .NET, используя DotNetZip.

Майкл Крауклис прав, что вы не можете просто "добавить" данные в военный файл или zip файл, но это происходит не потому, что есть "конец файла", строго говоря, в военном файле. Это потому, что формат войны (zip) включает в себя каталог, который обычно присутствует в конце файла, который содержит метаданные для различных записей в военном файле. Наивное добавление к военному файлу не приводит к обновлению каталога, и поэтому у вас есть только файл войны с добавлением к нему нежелательной почты.

Что необходимо - это интеллектуальный класс, который понимает формат и может читать + обновлять военный файл или zip файл, включая соответствующий каталог. DotNetZip делает это, без разжимания/повторного сжатия неизмененных записей, так же, как вы описали или хотели.

Ответ 6

Как говорит Чисо, нет способа сделать это. AFAIK для лицевых частей zip делает то же самое, что и вы внутри.

В любом случае, если вы беспокоитесь о скорости извлечения/сжатия всего, вы можете попробовать SevenZipJBindings.

Я рассмотрел эту библиотеку в своем blog несколько месяцев назад (извините за автоматическую рекламу). Как пример, извлечение zip файла размером 104 МБ с помощью java.util.zip заняло у меня 12 секунд, а использование этой библиотеки заняло 4 секунды.

В обеих ссылках вы можете найти примеры того, как его использовать.

Надеюсь, что это поможет.

Ответ 7

См. этот отчет отчет об ошибках.

Использование режима добавления в любом виде структурированные данные, такие как zip файлы или tar файлы не могут быть действительно ожидать работать. Эти форматы файлов имеют встроенный "конец файла", индикация, встроенная в формат данных.

Если вы действительно хотите пропустить промежуточный шаг безотчетного/повторного использования, вы можете прочитать файл военного файла, получить все записи в zip, а затем записать в новый военный файл "добавление" новых записей, которые вы хотели добавить. Не идеальное, но, по крайней мере, более автоматизированное решение.

Ответ 8

Еще одно решение: вы можете найти код, полезный и в других ситуациях. Я использовал ant таким образом, чтобы скомпилировать каталоги 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);
    }
}

Ответ 9

этот простой код, чтобы получить ответ с использованием сервлета и отправить ответ

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);
            }

Ответ 10

Вот версия ответа на Liam от Java 1.7, в которой используется попытка с ресурсами и Apache Commons IO.

Выход записывается в новый 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();
          }
        }
      }

    }

  }

Ответ 11

это работает на 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());

Ответ 12

Вот примеры того, как легко файлы могут быть добавлены к существующему 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()));

TrueVFS, преемник TrueZIP, использует функции Java 7 NIO 2 под капотом, когда это уместно, но предлагает гораздо больше функций, таких как потокобезопасные асинхронное параллельное сжатие.

Остерегайтесь также, что Java 7 ZipFileSystem по умолчанию уязвима для OutOfMemoryError на огромных входах.