В Java 8 java.util.zip.ZipFile был оснащен stream методом, который позволяет очень легко перемещаться по записям ZIP-файла. В этом посте я покажу несколько примеров, показывающих, как быстро мы можем перемещаться по записям в ZIP-файле.
Примечание. Для целей данного сообщения в блоге я загрузил одно из своих репозиториев GitHub в виде файла ZIP и скопировал его в c:/tmp .
До Java 7
Чтение записей ZIP-файлов в Java до Java 7 — это что-то вроде хм … сложно? Вот как можно начать ненавидеть Java, глядя на этот код:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class Zipper { public void printEntries(PrintStream stream, String zip) { ZipFile zipFile = null; try { zipFile = new ZipFile(zip); Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); stream.println(zipEntry.getName()); } } catch (IOException e) { // error while opening a ZIP file } finally { if (zipFile != null) { try { zipFile.close(); } catch (IOException e) { // do something } } } }} |
Java 7
С Java 7 то же самое может быть намного проще — благодаря try-with-resources но мы все еще «вынуждены» использовать Enumeration для навигации по записям в ZIP-файле:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class Zipper { public void printEntries(PrintStream stream, String zip) { try (ZipFile zipFile = new ZipFile(zip)) { Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); stream.println(zipEntry.getName()); } } catch (IOException e) { // error while opening a ZIP file } }} |
Использование Stream API
Самое интересное начинается с Java 8. Начиная с Java 8, в java.util.zip.ZipFile появился новый метод stream который возвращает упорядоченный поток по записям в файле ZIP. Это дает много возможностей при работе с ZIP-файлами в Java. Предыдущие примеры можно просто написать следующим образом в Java 8:
|
01
02
03
04
05
06
07
08
09
10
|
public class Zipper { public void printEntries(PrintStream stream, String zip) { try (ZipFile zipFile = new ZipFile(zip)) { zipFile.stream() .forEach(stream::println); } catch (IOException e) { // error while opening a ZIP file } }} |
С Stream API мы можем играть с ZipFile разными способами. См. ниже…
Фильтрация и сортировка содержимого ZIP файлов
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
public void printEntries(PrintStream stream, String zip) { try (ZipFile zipFile = new ZipFile(zip)) { Predicate<ZipEntry> isFile = ze -> !ze.isDirectory(); Predicate<ZipEntry> isJava = ze -> ze.getName().matches(".*java"); Comparator<ZipEntry> bySize = (ze1, ze2) -> Long.valueOf(ze2.getSize() - ze1.getSize()).intValue(); zipFile.stream() .filter(isFile.and(isJava)) .sorted(bySize) .forEach(ze -> print(stream, ze)); } catch (IOException e) { // error while opening a ZIP file }}private void print(PrintStream stream, ZipEntry zipEntry) { stream.println(zipEntry.getName() + ", size = " + zipEntry.getSize());} |
Выполняя итерации по записям ZIP, я проверяю, является ли запись файлом и соответствует ли оно заданному имени (для простоты в этом примере это закодировано), а затем сортирую его по размеру с помощью данного компаратора.
Создать индекс файлов ZIP-файла
В этом примере я группирую записи ZIP по первой букве имени файла, чтобы создать индекс Map<String, List<ZipEntry>> . Ожидаемый результат должен выглядеть примерно так:
|
1
2
|
a = [someFile/starting/with/an/A]u = [someFile/starting/with/an/U, someOtherFile/starting/with/an/U] |
Опять же, с Stream API это действительно легко:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public void printEntries(PrintStream stream, String zip) { try (ZipFile zipFile = new ZipFile(zip)) { Predicate<ZipEntry> isFile = ze -> !ze.isDirectory(); Predicate<ZipEntry> isJava = ze -> ze.getName().matches(".*java"); Comparator<ZipEntry> bySize = (ze1, ze2) -> Long.valueOf(ze2.getSize()).compareTo(Long.valueOf(ze1.getSize())); Map<String, List<ZipEntry>> result = zipFile.stream() .filter(isFile.and(isJava)) .sorted(bySize) .collect(groupingBy(this::fileIndex)); result.entrySet().stream().forEach(stream::println); } catch (IOException e) { // error while opening a ZIP file }}private String fileIndex(ZipEntry zipEntry) { Path path = Paths.get(zipEntry.getName()); Path fileName = path.getFileName(); return fileName.toString().substring(0, 1).toLowerCase();} |
Найти текст в записи файла ZIP
В последнем примере я ищу @Test текста @Test во всех файлах с расширением java . На этот раз я буду использовать метод lines BufferedReader который возвращает поток строк.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public void printEntries(PrintStream stream, String zip) { try (ZipFile zipFile = new ZipFile(zip)) { Predicate<ZipEntry> isFile = ze -> !ze.isDirectory(); Predicate<ZipEntry> isJava = ze -> ze.getName().matches(".*java"); List<ZipEntry> result = zipFile.stream() .filter(isFile.and(isJava)) .filter(ze -> containsText(zipFile, ze, "@Test")) .collect(Collectors.toList()); result.forEach(stream::println); } catch (IOException e) { // error while opening a ZIP file }}private boolean containsText(ZipFile zipFile, ZipEntry zipEntry, String needle) { try (InputStream inputStream = zipFile.getInputStream(zipEntry); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { Optional<String> found = reader.lines() .filter(l -> l.contains(needle)) .findFirst(); return found.isPresent(); } catch (IOException e) { return false; }} |
Резюме
Stream API в Java 8 является мощным решением, которое помогает легко решать относительно простые задачи. И это его сила, на мой взгляд.
Примеры, представленные в этой статье, относительно просты и созданы только для наглядности. Но я надеюсь, что вы их любите и найдете их полезными.
Ресурсы
| Ссылка: | Перечисление содержимого файла ZIP с Stream API в Java 8 от нашего партнера JCG Рафаля Боровца в блоге Codeleak.pl . |