거대한 zip 파일을 여러 볼륨으로 분할하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/243992

  •  04-07-2019
  •  | 
  •  

문제

다음을 통해 zip 아카이브를 만들 때 java.util.zip.*, 결과 아카이브를 여러 볼륨으로 분할하는 방법이 있습니까?

내 전체 아카이브에 filesize ~의 24 MB 파일당 10MB로 제한하여 3개의 파일로 분할하고 싶습니다.
이 기능이 포함된 zip API가 있나요?아니면 이것을 달성하는 다른 좋은 방법이 있습니까?

Thollsten 감사합니다

도움이 되었습니까?

해결책

확인하다: http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=38&t=004618

나는 당신이 그렇게하는 데 도움이 될 공개 API를 알지 못합니다. (프로그래밍 방식으로하고 싶지 않다면 Winsplitter와 같은 유틸리티가 있습니다).

나는 그것을 시도하지 않았지만, zipperinput/outputstream을 사용하는 동안의 모든 zipentry는 압축 크기를 가지고 있습니다. Zipped 파일의 크기를 생성하는 동안 대략적인 추정치를 얻을 수 있습니다. 2MB의 지퍼 파일이 필요한 경우, 누적 된 항목 크기가 1.9MB가 된 후 파일에 쓰기를 중지하여 Manifest 파일 및 기타 ZIP 파일 특정 요소의 경우 .1MB를 사용합니다. 따라서 간단히 말해서 다음과 같이 zipperinputstream에 래퍼를 쓸 수 있습니다.

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

위의 프로그램은 접근 방식의 힌트 일 뿐이며 어떤 식 으로든 최종 솔루션이 아닙니다..

다른 팁

출력이 pkzip 및 winzip과 호환되도록 하는 것이 목표라면 이를 수행하는 오픈 소스 라이브러리는 없습니다.우리 앱 중 하나에 대해 비슷한 요구 사항이 있었고 결국 자체 구현(zip 표준과 호환 가능)을 작성하게 되었습니다.제 기억에 따르면, 우리에게 가장 어려웠던 점은 개별 파일을 즉석에서 생성해야 한다는 것이었습니다. (대부분의 zip 유틸리티가 작동하는 방식은 큰 zip 파일을 생성한 다음 나중에 돌아가서 분할하는 것입니다. 훨씬 쉽습니다. 구현하다.작성하는데 1일, 디버깅하는데 2일정도 걸렸습니다.

zip 표준은 파일 형식이 어떻게 생겼는지 설명합니다.소매를 조금 걷어 올리는 것을 두려워하지 않는다면 이것은 확실히 가능합니다.zip 파일 생성기를 직접 구현해야 하지만 Java의 Deflator 클래스를 사용하여 압축된 데이터에 대한 세그먼트 스트림을 생성할 수 있습니다.파일과 섹션 헤더를 직접 생성해야 하지만 이는 단지 바이트일 뿐이므로 일단 뛰어들면 그다지 어렵지 않습니다.

여기에 우편번호 사양 - 섹션 K에는 구체적으로 찾고 있는 정보가 있지만 A, B, C, F도 읽어야 합니다.정말 큰 파일을 처리하는 경우(저희는 그랬습니다) Zip64 파일에도 들어가야 합니다. 하지만 24MB라면 괜찮습니다.

자세히 알아보고 시도해 보고 싶으시면 질문이 있으시면 다시 게시해 주세요. 제가 몇 가지 지침을 제공할 수 있는지 확인해 보겠습니다.

아래 코드는 디렉토리 구조의 zip 파일을 원하는 크기에 따라 청크로 분할하는 솔루션입니다. 나는 이전 답변이 유용하다는 것을 알았으므로 비슷하지만 거의 깔끔한 접근 방식에 기여하고 싶었습니다. 이 코드는 저의 특정 요구에 맞게 나에게 효과가 있으며 개선의 여지가 있다고 생각합니다.

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

가치가있는 것에 대해 나는 사용하는 것을 좋아합니다 재료를 시도하십시오 어디에나. 당신이 그 디자인 패턴에 빠졌다면, 당신은 이것을 좋아할 것입니다. 또한 항목이 원하는 부품 크기보다 큰 경우 빈 부품의 문제를 해결합니다. 당신은 할 것입니다 적어도 최악의 경우 항목만큼 많은 부분이 있습니다.

안에:

my-archive.zip

밖으로:

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

참고 : 나는 로깅과 아파치 커먼즈 filenameutils를 사용하고 있지만 툴킷에있는 것을 자유롭게 사용하십시오.

/**
 * 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
        }
    }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top