문제

현재 전쟁 파일의 내용을 추출한 다음 디렉토리 구조에 새 파일을 추가 한 다음 새 전쟁 파일을 만듭니다.

이것은 모두 Java에서 프로그래밍 방식으로 이루어졌지만 전쟁 파일을 복사하는 것이 더 효율적이지 않은지 궁금합니다. 그런 다음 파일을 추가 한 다음 전쟁이 확장 된 한 기다릴 필요가 없습니다. 다시 압축하십시오.

문서 나 온라인 예제 에서이 작업을 수행 할 수있는 방법을 찾을 수없는 것 같습니다.

누구든지 몇 가지 팁이나 포인터를 줄 수 있습니까?

업데이트:

답변 중 하나에 언급 된대로 TrueZip은 ZIV 파일에 추가하기에 매우 좋은 Java 라이브러리 인 것 같습니다 (이를 수행 할 수 없다는 다른 답변에도 불구하고).

누구든지 TrueZip에 대한 경험이나 피드백이 있거나 다른 유사한 Libaries를 추천 할 수 있습니까?

도움이 되었습니까?

해결책

Java 7에서 우리는 얻었습니다 zip 파일 시스템 이를 통해 수동 재 포장없이 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");
    }
}

다른 팁

다른 사람들이 언급했듯이, 기존 지퍼 (또는 전쟁)에 내용을 추가 할 수는 없습니다. 그러나 추출 된 콘텐츠를 디스크에 일시적으로 작성하지 않고도 새로운 지퍼를 즉시 만들 수 있습니다. 이것이 얼마나 빠를 것인지 추측하기는 어렵지만 표준 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();
    }
}

언젠가 비슷한 요구 사항이 있었지만 Zip Archives를 읽고 쓰는 것이 었습니다 (.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 라이브러리를 모릅니다. 그러나 당신이 묘사 한 것은 실용적입니다. .NET에서 사용할 수 있습니다 dotnetzip.

Michael Krauklis는 단순히 전쟁 파일 또는 ZIP 파일에 데이터를 "추가"할 수 없지만 전쟁 파일에 "파일의 끝"표시가 없기 때문이 아닙니다. 전쟁 (ZIP) 형식에는 일반적으로 파일 끝에 존재하는 디렉토리가 포함되어 있으며 전쟁 파일의 다양한 항목에 대한 메타 데이터가 포함되어 있기 때문입니다. 전쟁 파일에 순진하게 추가되면 디렉토리에 대한 업데이트가 없으므로 정크가 추가 된 전쟁 파일 만 있습니다.

필요한 것은 형식을 이해하고 디렉토리를 포함하여 전쟁 파일 또는 zip 파일을 읽을 수있는 지능형 클래스입니다. dotnetzip은 설명하거나 원하는대로 변경되지 않은 항목을 압박하거나 재개하지 않고이를 수행합니다.

Cheeso가 말했듯이, 그렇게 할 방법은 없습니다. afaik Zip 프론트 엔드는 내부적으로 정확히 동일하게 수행하고 있습니다.

어쨌든 모든 것을 추출/압축하는 속도가 걱정된다면 Sevenzipjbindings 도서관.

나는이 도서관을 내에서 다룬다 블로그 몇 달 전에 (자동 프로모션에 대해 죄송합니다). 예를 들어, java.util.zip을 사용하여 104MB ZIP 파일을 추출하는 데 12 초가 걸렸고이 라이브러리를 사용하는 데 4 초가 걸렸습니다.

두 링크에서 사용 방법에 대한 예제를 찾을 수 있습니다.

도움이되기를 바랍니다.

이것 좀 봐 버그 보고서.

ZIP 파일이나 TAR 파일과 같은 모든 종류의 구조화 된 데이터에서 Append 모드를 사용하는 것은 실제로 작동 할 수있는 것이 아닙니다. 이 파일 형식에는 데이터 형식에 내장 된 "파일 끝"표시가 있습니다.

비전없는/재발의 중간 단계를 건너 뛰고 싶다면 전쟁 파일 파일을 읽고 모든 zip 항목을 가져온 다음 새 전쟁 파일을 "추가하고자하는"새로운 항목에 쓸 수 있습니다. 완벽하지는 않지만 최소한 더 자동화 된 솔루션.

또 다른 해결책 : 다른 상황에서도 아래 코드가 유용 할 수 있습니다. 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);
    }
}

이것은 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);
            }

다음은 리소스와 Apache Commons IO와 함께 시도하는 Liam Answer의 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();
          }
        }
      }

    }

  }

여분의 libs를 사용하고 싶지 않다면 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에 취약합니다 큰 입력에.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top