Аннотация
Мы все знаем, что значит архивировать файл с помощью zip или gzip. Но использовать сжатые файлы в Java не так просто, как хотелось бы думать, особенно если вы работаете не с файлами напрямую, а с компрессией потоковых данных. Мы пойдем, хотя:
- как преобразовать строку в сжатый / сжатый байтовый массив и наоборот
- создавать служебные функции для чтения и записи файлов без необходимости заранее знать, является ли файл или поток gzip или нет.
Основы
Так почему вы хотите что-то застегнуть? Просто потому, что это отличный способ сократить объем данных, которые вам нужно передавать по сети или сохранить на диск, и, следовательно, увеличить скорость выполнения операции. Типичный текстовый файл или сообщение может быть уменьшено в 10 и более раз в зависимости от характера вашего документа. Конечно, вам придется учитывать стоимость архивирования и разархивирования, но когда у вас большой объем данных, маловероятно, что эти затраты будут значительными.
Java поддерживает это?
Да, Java поддерживает чтение и запись файлов gzip в пакете java.util.zip . Он также поддерживает zip-файлы, а также раздувание и выкачивание данных из популярной библиотеки сжатия ZLIB.
Как мне сжать / распаковать строку Java?
Вот пример того, как сжимать и распаковывать строку, используя DeflaterOutputStream.
Вот два метода использования встроенного в Java компрессора, а также метод, использующий GZIP:
- Использование DeflaterOutputStream — самый простой способ:
01020304050607080910111213141516171819202122232425262728
enumStringCompressor {;publicstaticbyte[] compress(String text) {ByteArrayOutputStream baos =newByteArrayOutputStream();try{OutputStream out =newDeflaterOutputStream(baos);out.write(text.getBytes("UTF-8"));out.close();}catch(IOException e) {thrownewAssertionError(e);}returnbaos.toByteArray();}publicstaticString decompress(byte[] bytes) {InputStream in =newInflaterInputStream(newByteArrayInputStream(bytes));ByteArrayOutputStream baos =newByteArrayOutputStream();try{byte[] buffer =newbyte[8192];intlen;while((len = in.read(buffer))>0)baos.write(buffer,0, len);returnnewString(baos.toByteArray(),"UTF-8");}catch(IOException e) {thrownewAssertionError(e);}}} - Если вы хотите использовать Deflater / Inflater напрямую:
01020304050607080910111213141516171819202122232425
enumStringCompressor2 {;publicstaticbyte[] compress(String text)throwsException{byte[] output =newbyte;Deflater compresser =newDeflater();compresser.setInput(text.getBytes("UTF-8"));compresser.finish();intcompressedDataLength = compresser.deflate(output);byte[] dest =newbyte[compressedDataLength];System.arraycopy(output,0, dest,0, compressedDataLength);returndest;}publicstaticString decompress(byte[] bytes)throwsException{Inflater decompresser =newInflater();decompresser.setInput(bytes,0, bytes.length);byte[] result =newbyte[bytes.length *10];intresultLength = decompresser.inflate(result);decompresser.end();// Decode the bytes into a StringString outputString =newString(result,0, resultLength,"UTF-8");returnoutputString;}} - Вот как это сделать с помощью GZIP:
010203040506070809101112131415161718192021
enumStringGZipper {;privatestaticString ungzip(byte[] bytes)throwsException{InputStreamReader isr =newInputStreamReader(newGZIPInputStream(newByteArrayInputStream(bytes)), StandardCharsets.UTF_8);StringWriter sw =newStringWriter();char[] chars =newchar[1024];for(intlen; (len = isr.read(chars)) >0; ) {sw.write(chars,0, len);}returnsw.toString();}privatestaticbyte[] gzip(String s)throwsException{ByteArrayOutputStream bos =newByteArrayOutputStream();GZIPOutputStream gzip =newGZIPOutputStream(bos);OutputStreamWriter osw =newOutputStreamWriter(gzip, StandardCharsets.UTF_8);osw.write(s);osw.close();returnbos.toByteArray();}}
Как декодировать поток байтов, чтобы разрешить как GZip, так и обычные потоки:
Приведенный ниже код превратит поток байтов в строку (дамп) независимо от того, нужно ли заранее знать, был ли этот поток сжатым или нет.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
if (isGZIPStream(bytes)) { InputStreamReader isr = new InputStreamReader(new GZIPInputStream(new ByteArrayInputStream(bytes)), StandardCharsets.UTF_8); StringWriter sw = new StringWriter(); char[] chars = new char[1024]; for (int len; (len = isr.read(chars)) > 0; ) { sw.write(chars, 0, len); } dump = sw.toString(); } else { dump = new String(bytes, 0, length, StandardCharsets.UTF_8); }} |
Это реализация метода isGZIPStream. Раскрывает правду о том, что стоит за GZIP_MAGIC!
|
1
2
3
4
|
public static boolean isGZIPStream(byte[] bytes) { return bytes[0] == (byte) GZIPInputStream.GZIP_MAGIC && bytes[1] == (byte) (GZIPInputStream.GZIP_MAGIC >>> 8);} |
Это простой способ прочитать файл, не зная, был ли он заархивирован или нет (полагаясь на расширение .gz).
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
static Stream<String> getStream(String dir, @NotNull String fileName) throws IOException { File file = new File(dir, fileName); InputStream in; if (file.exists()) { in = new FileInputStream(file); } else { file = new File(dir, fileName + ".gz"); in = new GZIPInputStream(new FileInputStream(file)); } return new BufferedReader(new InputStreamReader(in)).lines();} |
| Ссылка: | Работа с GZIP и сжатыми данными нашего партнера по JCG Даниэля Шая в блоге Rational Java . |