2014-05-25 7 views
9

ma piękny sposób obsługi plików zip, traktując je jako systemy plików. Dzięki temu możemy traktować zawartość pliku ZIP jak zwykłe pliki. W ten sposób skompresowanie całego folderu można uzyskać, po prostu za pomocą Files.copy, aby skopiować wszystkie pliki do pliku zip. Ponieważ podfoldery również mają być kopiowane, potrzebujemy użytkownika:Załadowanie ogromnego folderu za pomocą pliku ZipFileSystem powoduje, że pakiet OutOfMemoryError

private static class CopyFileVisitor extends SimpleFileVisitor<Path> { 
    private final Path targetPath; 
    private Path sourcePath = null; 
    public CopyFileVisitor(Path targetPath) { 
     this.targetPath = targetPath; 
    } 

    @Override 
    public FileVisitResult preVisitDirectory(final Path dir, 
    final BasicFileAttributes attrs) throws IOException { 
     if (sourcePath == null) { 
      sourcePath = dir; 
     } else { 
     Files.createDirectories(targetPath.resolve(sourcePath 
        .relativize(dir).toString())); 
     } 
     return FileVisitResult.CONTINUE; 
    } 

    @Override 
    public FileVisitResult visitFile(final Path file, 
    final BasicFileAttributes attrs) throws IOException { 
    Files.copy(file, 
     targetPath.resolve(sourcePath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING); 
    return FileVisitResult.CONTINUE; 
    } 
} 

To jest prosty "kopia katalogowa rekurencyjnie" odwiedzający. Służy do rekurencyjnego kopiowania katalogu. Jednak z ZipFileSystem, możemy również użyć go skopiować katalog w pliku zip, tak:

public static void zipFolder(Path zipFile, Path sourceDir) throws ZipException, IOException 
{ 
    // Initialize the Zip Filesystem and get its root 
    Map<String, String> env = new HashMap<>(); 
    env.put("create", "true"); 
    URI uri = URI.create("jar:" + zipFile.toUri());  
    FileSystem fileSystem = FileSystems.newFileSystem(uri, env); 
    Iterable<Path> roots = fileSystem.getRootDirectories(); 
    Path root = roots.iterator().next(); 

    // Simply copy the directory into the root of the zip file system 
    Files.walkFileTree(sourceDir, new CopyFileVisitor(root)); 
} 

To jest to, co nazywam elegancki sposób skompresowanie cały folder. Jednak podczas korzystania z tej metody w dużym folderze (około 3 GB) otrzymuję OutOfMemoryError (przestrzeń sterty). Podczas korzystania ze zwykłej biblioteki obsługi zip, ten błąd nie jest zgłaszany. Wydaje się więc, że sposób, w jaki ZipFileSystem obsługuje kopie, jest bardzo nieefektywny: zbyt wiele plików, które mają być zapisane, jest przechowywane w pamięci, więc występuje OutOfMemoryError.

Dlaczego tak się dzieje? Czy używanie ZipFileSystem generalnie jest uważane za nieefektywne (jeśli chodzi o zużycie pamięci), czy też robię coś nie tak?

Odpowiedz

-2

Musisz przygotować jvm, aby zezwolić na taką ilość pamięci za pomocą -Xms {memory} -Xmx {memory}.

Polecam sprawdzić katalog obliczania miejsca na dysku i umieścić limit, w ramach 1GB pamięci system plików, ponad 1 GB użyć systemu plików na dysku.

Inną rzeczą, należy sprawdzić współbieżność metody, będziesz nie lubią więcej niż 1 Temat skompresowanie 3Gb plików

+2

Niestety, ale ta odpowiedź nie pomaga w ogóle. 1) Wiem, jak zwiększyć wielkość sterty, to nie jest pytanie. 2) Co to jest "system plików pamięci" a "system plików na dysku"? 3) Metoda nie jest równoległa, jak powinieneś zobaczyć z kodu – gexicide

+0

@gexicide Proszę sprawdzić moją odpowiedź i jeśli to rozwiąże twój problem (jak to zrobił dla innych), proszę oznaczyć ją jako poprawną odpowiedź. Dzięki. –

17

Spojrzałem na ZipFileSystem.java i wierzę, że znalazłem źródło zużycie pamięci . Domyślnie implementacja używa ByteArrayOutputStream jako bufora do kompresowania plików, co oznacza, że ​​jest ograniczona ilością pamięci przypisanej do JVM.

Istnieje zmienna środowiskowa (nieudokumentowana), której możemy użyć do wykonania plików tymczasowych implementacji ("useTempFile"). To działa tak:

Map<String, Object> env = new HashMap<>(); 
env.put("create", "true"); 
env.put("useTempFile", Boolean.TRUE); 

Więcej szczegółów tutaj: http://www.docjar.com/html/api/com/sun/nio/zipfs/ZipFileSystem.java.html, ciekawe linie 96, 1358 i 1362

+2

Dziękuję bardzo za dochodzenie w tej sprawie. Obserwowanie katalogu tymczasowego, gdy 'useTempFile = TRUE' podczas kompresowania plików równolegle (przy użyciu http://goo.gl/woa0Ab) wydaje się, że każdy plik jest spakowany niezależnie równolegle do oddzielnego skompresowanego pliku tymczasowego, a wszystkie są następnie połączone w jeden plik. Plik ten jest następnie przemianowywany atomowo na nazwę archiwum. Co za wstyd, że nie jest to udokumentowane, a co więcej, nie ma jeszcze strumieniowego równoległego zip w standardowej bibliotece java. –

Powiązane problemy