Аннотация
Мы все знаем, что значит архивировать файл с помощью zip или gzip. Но использовать сжатые файлы в Java не так просто, как хотелось бы думать, особенно если вы работаете не с файлами напрямую, а с компрессией потоковых данных. Мы пойдем, хотя:
- как преобразовать строку в сжатый / сжатый байтовый массив и наоборот
- создавать служебные функции для чтения и записи файлов без необходимости заранее знать, является ли файл или поток gzip или нет.
Основы
Так почему вы хотите что-то застегнуть? Просто потому, что это отличный способ сократить объем данных, которые вам нужно передавать по сети или сохранить на диск, и, следовательно, увеличить скорость выполнения операции. Типичный текстовый файл или сообщение может быть уменьшено в 10 и более раз в зависимости от характера вашего документа. Конечно, вам придется учитывать стоимость архивирования и разархивирования, но когда у вас большой объем данных, маловероятно, что эти затраты будут значительными.
Java поддерживает это?
Да, Java поддерживает чтение и запись файлов gzip в пакете java.util.zip . Он также поддерживает zip-файлы, а также раздувание и выкачивание данных из популярной библиотеки сжатия ZLIB.
Как мне сжать / распаковать строку Java?
Вот пример того, как сжимать и распаковывать строку, используя DeflaterOutputStream.
Вот два метода использования встроенного в Java компрессора, а также метод, использующий GZIP:
- Использование DeflaterOutputStream — самый простой способ:
01020304050607080910111213141516171819202122232425262728
enum
StringCompressor {
;
public
static
byte
[] compress(String text) {
ByteArrayOutputStream baos =
new
ByteArrayOutputStream();
try
{
OutputStream out =
new
DeflaterOutputStream(baos);
out.write(text.getBytes(
"UTF-8"
));
out.close();
}
catch
(IOException e) {
throw
new
AssertionError(e);
}
return
baos.toByteArray();
}
public
static
String decompress(
byte
[] bytes) {
InputStream in =
new
InflaterInputStream(
new
ByteArrayInputStream(bytes));
ByteArrayOutputStream baos =
new
ByteArrayOutputStream();
try
{
byte
[] buffer =
new
byte
[
8192
];
int
len;
while
((len = in.read(buffer))>
0
)
baos.write(buffer,
0
, len);
return
new
String(baos.toByteArray(),
"UTF-8"
);
}
catch
(IOException e) {
throw
new
AssertionError(e);
}
}
}
- Если вы хотите использовать Deflater / Inflater напрямую:
01020304050607080910111213141516171819202122232425
enum
StringCompressor2 {
;
public
static
byte
[] compress(String text)
throws
Exception{
byte
[] output =
new
byte
;
Deflater compresser =
new
Deflater();
compresser.setInput(text.getBytes(
"UTF-8"
));
compresser.finish();
int
compressedDataLength = compresser.deflate(output);
byte
[] dest =
new
byte
[compressedDataLength];
System.arraycopy(output,
0
, dest,
0
, compressedDataLength);
return
dest;
}
public
static
String decompress(
byte
[] bytes)
throws
Exception{
Inflater decompresser =
new
Inflater();
decompresser.setInput(bytes,
0
, bytes.length);
byte
[] result =
new
byte
[bytes.length *
10
];
int
resultLength = decompresser.inflate(result);
decompresser.end();
// Decode the bytes into a String
String outputString =
new
String(result,
0
, resultLength,
"UTF-8"
);
return
outputString;
}
}
- Вот как это сделать с помощью GZIP:
010203040506070809101112131415161718192021
enum
StringGZipper {
;
private
static
String ungzip(
byte
[] bytes)
throws
Exception{
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);
}
return
sw.toString();
}
private
static
byte
[] gzip(String s)
throws
Exception{
ByteArrayOutputStream bos =
new
ByteArrayOutputStream();
GZIPOutputStream gzip =
new
GZIPOutputStream(bos);
OutputStreamWriter osw =
new
OutputStreamWriter(gzip, StandardCharsets.UTF_8);
osw.write(s);
osw.close();
return
bos.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 . |