سؤال

أنا الآن استخراج محتويات ملف حرب ثم قم بإضافة بعض الملفات الجديدة إلى بنية الدليل ثم إنشاء ملف حرب جديد.

يتم كل ذلك برمجيا من جافا - لكنني أتساءل عما إذا كان لن يكون أكثر كفاءة لنسخ ملف الحرب ثم فقط إلحاق الملفات - فلن أضطر الانتظار طالما أن الحرب تتوسع ثم يجب عليها أن يتم ضغط مرة أخرى.

لا يبدو لي أن أجد طريقة للقيام بذلك في الوثائق رغم ذلك أو أي أمثلة عبر الإنترنت.

يمكن لأي شخص إعطاء بعض النصائح أو المؤشرات؟

تحديث:

يبدو أن truezip كما ذكر في أحد الإجابات مكتبة جافا جيدة جدا لإلحاق ملف zip (على الرغم من الإجابات الأخرى التي تقول أنه من الممكن القيام بذلك).

أي شخص لديه خبرة أو ردود فعل على TRUEZIP أو يمكن أن يوصي بليبيارات أخرى مماثلة؟

هل كانت مفيدة؟

المحلول

في جاوة 7 وصلنا نظام ملفات مضغوط التي تسمح بإضافة وتغيير الملفات في الرمز البريدي (جرة، الحرب) دون إعادة توحيد يدوي.

يمكننا الكتابة مباشرة إلى الملفات داخل ملفات 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 جديد على الطاير دون كتابة محتوى مستخرج مؤقتا إلى القرص. من الصعب تخمين مقدار أسرع هذا، لكنه الأسرع يمكنك الحصول عليه (على الأقل كما أعرف) مع جافا القياسية. كما ذكرنا كارلوس تاسادا، قد تضغط عليك Sevenzipjbindings على بعض الثواني الإضافية، ولكن تقوم بتنفيذ هذا النهج إلى Sevenzipjbindings أسرع من استخدام الملفات المؤقتة مع نفس المكتبة.

إليك بعض الكود الذي يكتب محتويات ZIP (WAR.ZIP) الموجودة وإلحاق ملف إضافي (adjess.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();
    }
}

كان لدي شرط مماثل في وقت ما مرة أخرى - ولكن كان لقراءة وكتابة أرشيفات البريدية (يجب أن يكون تنسيق .war مماثلة). حاولت القيام بذلك مع تدفقات Java Zip الحالية ولكن العثور على جزء الكتابة مرهقة - خاصة عندما الدلائل التي ينطوي عليها.

سوف أوصي بك لتجربة truezip. (فتح المصدر - Apache Style المرخصة) المكتبة التي تنص على أي أرشيف كمنظمة ملف افتراضي يمكنك من القراءة والكتابة مثل نظام ملفات عادي. لقد عملت مثل سحر لي وبسيط كبير تطوري.

يمكنك استخدام هذا القليل من التعليمات البرمجية كتبت

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

أنا لا أعرف مكتبة جافا التي تفعل ما تصفه. ولكن ما وصفته هو عملي. يمكنك القيام بذلك في .NET، باستخدام dotnetzip..

مايكل كراكليس صحيح أنه لا يمكنك ببساطة بيانات "إلحاق" بملف حرب أو ملف مضغوط، لكنه ليس بسبب وجود مؤشر "نهاية الملف"، والتحدث بدقة، في ملف حرب. ومع ذلك، يتضمن تنسيق الحرب (الرمز البريدي) دليلا، وهو حاضر عادة في نهاية الملف، والذي يحتوي على بيانات تعريف للإدخالات المختلفة في ملف الحرب. يؤدي إلحاقا بسذاجة إلى ملفات حرب لا يؤدي إلى تحديث إلى الدليل، وبالتالي لديك ملف حرب فقط مع إلحاق به.

ما هو ضروري هو فئة ذكية تفهم التنسيق، ويمكن قراءة + تحديث ملف حرب أو ملف مضغوط، بما في ذلك الدليل حسب الاقتضاء. Dotnetzip يفعل هذا، دون ضغط غير مضغوط / إعادة تدوير الإدخالات دون تغيير، كما هو موضح أو المرغوب فيه.

كما تقول تشيزو، لا توجد طريقة للقيام بذلك. Afaik The Zip Front-Wonds يفعلان نفس الشيء تماما كما كنت داخليا.

على أي حال إذا كنت قلقا بشأن سرعة استخراج / ضغط كل شيء، فقد ترغب في تجربة sevenzipjbindings. مكتبة.

غطت هذه المكتبة في بلدي مقالات منذ بضعة أشهر (آسف للترقية التلقائية). فقط كمثال، استخراج ملف zip 104mb باستخدام java.util.zip استغرقني 12 ثانية، أثناء استخدام هذه المكتبة استغرق 4 ثوان.

في كلا الارتباطين، يمكنك العثور على أمثلة حول كيفية استخدامه.

آمل أن يساعد.

انظر الى هذا تقرير الشوائب.

باستخدام وضع إلحاق على أي نوع من البيانات الهيكلية مثل ملفات ZIP أو ملفات القطران ليس شيئا يمكنك أن تتوقعه حقا. تحتوي تنسيقات الملفات هذه على مؤشر "نهاية ملف" جوهري مضمن بتنسيق البيانات.

إذا كنت ترغب حقا في تخطي الخطوة المتوسطة في الأمم المتحدة للتحركات / إعادة التحث، يمكنك قراءة ملف ملفات الحرب، واحصل على جميع إدخالات ZIP، ثم اكتب إلى ملف حرب جديد "إلحاق" الإدخالات الجديدة التي تريد إضافتها. ليست مثالية، ولكن على الأقل حل آلي أكثر.

حل آخر: قد تجد رمز أدناه مفيد في مواقف أخرى أيضا. لقد استخدمت النملة هذه الطريقة لتجميع أدلة Java، وتوليد ملفات جرة، وتحديث ملفات 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);
            }

هنا إصدار Java 1.7 من Liam الإجابة التي تستخدم المحاولة بالموارد و Apache Commons IO.

يتم كتابة الإخراج إلى ملف مضغوط جديد ولكن يمكن تعديله بسهولة للكتابة إلى الملف الأصلي.

  /**
   * 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٪، إذا كنت لا ترغب في استخدام Libs إضافية .. 1) أولا، الفئة التي إلحاق الملفات إلى الرمز البريدي ..

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

فيما يلي أمثلة مدى سهولة إلحاق الملفات بالملفات الحالية باستخدام 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 تحت غطاء محرك السيارة عند الاقتضاء ولكن العروض المزيد من الميزات مثل آمنة آمنة ASYNC ضغط مواز.

احذر أيضا أن جافا 7 zipfilesystem افتراضيا عرضة ل OutofmemoryError. على مدخلات ضخمة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top