Frage

Wenn ich ein ZIP-Archiv über java.util.zip.* schaffen, ist es eine Möglichkeit, das entstandene Archiv in mehrere Volumes aufgeteilt?

Lassen Sie sich sagen, dass mein gesamtes Archiv ein filesize von 24 MB hat und ich möchte es in 3 Dateien auf einem Limit von 10 MB pro Datei teilen.
Gibt es einen Zip-API, die über diese Funktion verfügt? Oder jede andere schöne Möglichkeiten, um dies zu erreichen?

Danke Thollsten

War es hilfreich?

Lösung

Überprüfen Sie: http: // Salon. javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=38&t=004618

Ich bin nicht bekannt, dass öffentliche API, die Sie tun helfen. (Obwohl, wenn Sie nicht wollen, es programmatisch zu tun, gibt es Dienstprogramme wie WinSplitter, die es tun wird)

Ich habe es nicht versucht aber, jedes ZipEntry während ZippedInput / Output mit einer komprimierten Größe hat. Sie können eine grobe Schätzung der Größe der ZIP-Datei erhalten, während es zu schaffen. Wenn Sie 2 MB ZIP-Dateien benötigen, dann können Sie nach der kumulativen Größe der Einträge werden 1,9 MB in einer Datei stoppen Schreiben unter .1MB für Manifest-Datei und andere ZIP-Datei bestimmte Elemente. Also, kurz gesagt, können Sie einen Wrapper über die ZippedInputStream schreiben wie folgt:

import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
import java.io.FileOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ChunkedZippedOutputStream {

    private ZipOutputStream zipOutputStream;

    private String path;
    private String name;

    private long currentSize;
    private int currentChunkIndex;
    private final long MAX_FILE_SIZE = 16000000; // Whatever size you want
    private final String PART_POSTFIX = ".part.";
    private final String FILE_EXTENSION = ".zip";

    public ChunkedZippedOutputStream(String path, String name) throws FileNotFoundException {
        this.path = path;
        this.name = name;
        constructNewStream();
    }

    public void addEntry(ZipEntry entry) throws IOException {
        long entrySize = entry.getCompressedSize();
        if((currentSize + entrySize) > MAX_FILE_SIZE) {
            closeStream();
            constructNewStream();
        } else {
            currentSize += entrySize;
            zipOutputStream.putNextEntry(entry);
        }
    }

    private void closeStream() throws IOException {
        zipOutputStream.close();
    }

    private void constructNewStream() throws FileNotFoundException {
        zipOutputStream = new ZipOutputStream(new FileOutputStream(new File(path, constructCurrentPartName())));
        currentChunkIndex++;
        currentSize = 0;
    }

    private String constructCurrentPartName() {
        // This will give names is the form of <file_name>.part.0.zip, <file_name>.part.1.zip, etc.
        StringBuilder partNameBuilder = new StringBuilder(name);
        partNameBuilder.append(PART_POSTFIX);
        partNameBuilder.append(currentChunkIndex);
        partNameBuilder.append(FILE_EXTENSION);
        return partNameBuilder.toString();
    }
}

Das obige Programm ist nur ein Hinweis des Ansatzes und nicht um eine endgültige Lösung mit irgendwelchen Mitteln .

Andere Tipps

Wenn das Ziel der Ausgang sein kompatibel mit pkzip zu haben ist und Winzip, bin ich nicht bekannt, dass Open-Source-Bibliotheken, die dies tun. Wir hatten eine ähnliche Anforderung für ein unsere Anwendungen, und ich Liquidation unsere eigene Implementierung schriftlich (kompatibel mit dem Zip-Standard). Wenn ich mich erinnere, war das härteste, was für uns, dass wir die einzelnen Dateien im laufenden Betrieb (die Art und Weise, dass die meisten Zip-Dienstprogramme arbeiten, ist sie schaffen die große Zip-Datei, dann gehen Sie zurück zu generieren hatte und teilen Sie es später -, dass viel einfacher zu implementieren. Es dauerte etwa einen Tag zu schreiben und 2 Tage zu debuggen.

Der ZIP-Standard erklärt, was das Dateiformat wie auszusehen hat. Wenn Sie keine Angst vor dem Aufrollen Ärmel ein bisschen sind, ist dies auf jeden Fall machbar. Sie haben einen Zip-Datei-Generator selbst zu implementieren, aber Sie können Java-Deflator-Klasse verwenden, um die Segmentströme für die komprimierten Daten zu erzeugen. Hier finden Sie die Datei und Abschnittsüberschriften selbst erzeugen müssen, aber sie sind nur die Bytes -. Nichts zu schwer, wenn Sie tauchen in

Hier ist der zip Spezifikation - Abschnitt K die Info hat Sie suchen insbesondere aber Sie müssen A, B, C und F als auch lesen. Wenn Sie mit wirklich großen Dateien handelt (wir waren), werden Sie auch in die Zip64 Sachen bekommen müssen -. Aber für 24 MB, sind Sie fein

Wenn Sie möchten, tauchen und versuchen Sie es -., Wenn Sie Fragen laufen, Post zurück und ich werde sehen, ob ich einige Hinweise liefern kann

Im Folgenden Code ist meine Lösung Zip-Datei in Verzeichnisstruktur Stücke auf die gewünschte Größe basierend aufzuspalten. Ich fand die bisherigen Antworten nützlich, so wollte mit ähnlichem, aber etwas mehr ordentlich Ansatz beizutragen. Dieser Code funktioniert für mich für meine Bedürfnisse, und ich glaube, es gibt Raum für Verbesserungen.

private final static long MAX_FILE_SIZE = 1000 * 1000 * 1024; //  around 1GB 
private final static String zipCopyDest =  "C:\\zip2split\\copy";

public static void splitZip(String zipFileName, String zippedPath, String coreId) throws IOException{

    System.out.println("process whole zip file..");
    FileInputStream fis  = new FileInputStream(zippedPath);
    ZipInputStream zipInputStream = new ZipInputStream(fis);
    ZipEntry entry = null;
    int currentChunkIndex = 0;
    //using just to get the uncompressed size of the zipentries
    long entrySize = 0;
    ZipFile zipFile = new ZipFile(zippedPath);
    Enumeration enumeration = zipFile.entries();

    String copDest = zipCopyDest + "\\" + coreId + "_" + currentChunkIndex +".zip";

    FileOutputStream fos = new FileOutputStream(new File(copDest));
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    ZipOutputStream zos = new ZipOutputStream(bos);
    long currentSize = 0; 

    try {
        while ((entry = zipInputStream.getNextEntry()) != null && enumeration.hasMoreElements()) {

            ZipEntry zipEntry = (ZipEntry) enumeration.nextElement();
            System.out.println(zipEntry.getName());
            System.out.println(zipEntry.getSize());
            entrySize = zipEntry.getSize();

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            //long entrySize = entry.getCompressedSize();
            //entrySize = entry.getSize(); //gives -1

            if((currentSize + entrySize) > MAX_FILE_SIZE) {
                zos.close();
                //construct a new stream
                //zos = new ZipOutputStream(new FileOutputStream(new File(zippedPath, constructCurrentPartName(coreId))));
                currentChunkIndex++;
                zos = getOutputStream(currentChunkIndex, coreId);
                currentSize = 0;

            }else{
                currentSize += entrySize;
                zos.putNextEntry(new ZipEntry(entry.getName()));
                byte[] buffer = new byte[8192];
                int length = 0;
                while ((length = zipInputStream.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, length);
                }

                byte[] unzippedFile = outputStream.toByteArray();
                zos.write(unzippedFile);
                unzippedFile = null;
                outputStream.close();
                zos.closeEntry();
            }
            //zos.close();
        }
    } finally {
        zos.close();
    }


}

 public static ZipOutputStream getOutputStream(int i, String coreId) throws IOException {
     System.out.println("inside of getOutputStream()..");
     ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipCopyDest + "\\" + coreId + "_" +  i +".zip"));   
    // out.setLevel(Deflater.DEFAULT_COMPRESSION);
     return out;
 }

public static void main(String args[]) throws IOException{
    String zipFileName = "Large_files _for_testing.zip";
    String zippedPath= "C:\\zip2split\\Large_files _for_testing.zip";
    String coreId = "Large_files _for_testing";
    splitZip(zipFileName, zippedPath, coreId);
}

Für das, was es wert ist, Ich mag verwenden, versuchen-mit-Ressourcen überall. Wenn Sie in diesem Design-Muster sind, dann mögen Sie diese. Auch dies löst das Problem der leeren Teile, wenn die Einträge sind größer als die gewünschte Teilgröße. Sie werden mindestens haben so viele Teile wie Einträge im schlimmsten Fall.

  

my-archive.zip

Out:

  

my-archive.part1of3.zip
  my-archive.part2of3.zip
  my-archive.part3of3.zip

. Hinweis: Ich verwende die Protokollierung und Apache Commons FilenameUtils, aber das Gefühl frei zu verwenden, was Sie in Ihrem Toolkit haben

/**
 * Utility class to split a zip archive into parts (not volumes)
 * by attempting to fit as many entries into a single part before
 * creating a new part. If a part would otherwise be empty because
 * the next entry won't fit, it will be added anyway to avoid empty parts.
 *
 * @author Eric Draken, 2019
 */
public class Zip
{
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

    private static final String ZIP_PART_FORMAT = "%s.part%dof%d.zip";

    private static final String EXT = "zip";

    private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );

    /**
     * Split a large archive into smaller parts
     *
     * @param zipFile             Source zip file to split (must end with .zip)
     * @param outZipFile          Destination zip file base path. The "part" number will be added automatically
     * @param approxPartSizeBytes Approximate part size
     * @throws IOException Exceptions on file access
     */
    public static void splitZipArchive(
        @NotNull final File zipFile,
        @NotNull final File outZipFile,
        final long approxPartSizeBytes ) throws IOException
    {
        String basename = FilenameUtils.getBaseName( outZipFile.getName() );
        Path basePath = outZipFile.getParentFile() != null ? // Check if this file has a parent folder
            outZipFile.getParentFile().toPath() :
            Paths.get( "" );
        String extension = FilenameUtils.getExtension( zipFile.getName() );
        if ( !extension.equals( EXT ) )
        {
            throw new IllegalArgumentException( "The archive to split must end with ." + EXT );
        }

        // Get a list of entries in the archive
        try ( ZipFile zf = new ZipFile( zipFile ) )
        {
            // Silliness check
            long minRequiredSize = zipFile.length() / 100;
            if ( minRequiredSize > approxPartSizeBytes )
            {
                throw new IllegalArgumentException(
                    "Please select a minimum part size over " + minRequiredSize + " bytes, " +
                        "otherwise there will be over 100 parts."
                );
            }

            // Loop over all the entries in the large archive
            // to calculate the number of parts required
            Enumeration<? extends ZipEntry> enumeration = zf.entries();
            long partSize = 0;
            long totalParts = 1;
            while ( enumeration.hasMoreElements() )
            {
                long nextSize = enumeration.nextElement().getCompressedSize();
                if ( partSize + nextSize > approxPartSizeBytes )
                {
                    partSize = 0;
                    totalParts++;
                }
                partSize += nextSize;
            }

            // Silliness check: if there are more parts than there
            // are entries, then one entry will occupy one part by contract
            totalParts = Math.min( totalParts, zf.size() );

            logger.debug( "Split requires {} parts", totalParts );
            if ( totalParts == 1 )
            {
                // No splitting required. Copy file
                Path outFile = basePath.resolve(
                    String.format( ZIP_PART_FORMAT, basename, 1, 1 )
                );
                Files.copy( zipFile.toPath(), outFile );
                logger.debug( "Copied {} to {} (pass-though)", zipFile.toString(), outFile.toString() );
                return;
            }

            // Reset
            enumeration = zf.entries();

            // Split into parts
            int currPart = 1;
            ZipEntry overflowZipEntry = null;
            while ( overflowZipEntry != null || enumeration.hasMoreElements() )
            {
                Path outFilePart = basePath.resolve(
                    String.format( ZIP_PART_FORMAT, basename, currPart++, totalParts )
                );
                overflowZipEntry = writeEntriesToPart( overflowZipEntry, zf, outFilePart, enumeration, approxPartSizeBytes );
                logger.debug( "Wrote {}", outFilePart );
            }
        }
    }

    /**
     * Write an entry to the to the outFilePart
     *
     * @param overflowZipEntry    ZipEntry that didn't fit in the last part, or null
     * @param inZipFile           The large archive to split
     * @param outFilePart         The part of the archive currently being worked on
     * @param enumeration         Enumeration of ZipEntries
     * @param approxPartSizeBytes Approximate part size
     * @return Overflow ZipEntry, or null
     * @throws IOException File access exceptions
     */
    private static ZipEntry writeEntriesToPart(
        @Nullable ZipEntry overflowZipEntry,
        @NotNull final ZipFile inZipFile,
        @NotNull final Path outFilePart,
        @NotNull final Enumeration<? extends ZipEntry> enumeration,
        final long approxPartSizeBytes
    ) throws IOException
    {
        try (
            ZipOutputStream zos =
                new ZipOutputStream( new FileOutputStream( outFilePart.toFile(), false ) )
        )
        {
            long partSize = 0;
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            while ( overflowZipEntry != null || enumeration.hasMoreElements() )
            {
                ZipEntry entry = overflowZipEntry != null ? overflowZipEntry : enumeration.nextElement();
                overflowZipEntry = null;

                long entrySize = entry.getCompressedSize();
                if ( partSize + entrySize > approxPartSizeBytes )
                {
                    if ( partSize != 0 )
                    {
                        return entry;    // Finished this part, but return the dangling ZipEntry
                    }
                    // Add the entry anyway if the part would otherwise be empty
                }
                partSize += entrySize;
                zos.putNextEntry( entry );

                // Get the input stream for this entry and copy the entry
                try ( InputStream is = inZipFile.getInputStream( entry ) )
                {
                    int bytesRead;
                    while ( (bytesRead = is.read( buffer )) != -1 )
                    {
                        zos.write( buffer, 0, bytesRead );
                    }
                }
            }
            return null;    // Finished splitting
        }
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top