Сжатие и декомпрессия строковых данных в java
Я использую следующий код для сжатия и распаковки строковых данных, но проблема, с которой я столкнулся, заключается в том, что она легко сжимается без ошибок, но при распаковке метода возникает следующая ошибка.
Исключение в потоке "main" java.io.IOException: не в формате GZIP
public static void main(String[] args) throws Exception {
String string = "I am what I am hhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+ "bjggujhhhhhhhhh"
+ "rggggggggggggggggggggggggg"
+ "esfffffffffffffffffffffffffffffff"
+ "esffffffffffffffffffffffffffffffff"
+ "esfekfgy enter code here`etd`enter code here wdd"
+ "heljwidgutwdbwdq8d"
+ "skdfgysrdsdnjsvfyekbdsgcu"
+"jbujsbjvugsduddbdj";
System.out.println("after compress:");
String compressed = compress(string);
System.out.println(compressed);
System.out.println("after decompress:");
String decomp = decompress(compressed);
System.out.println(decomp);
}
public static String compress(String str) throws Exception {
if (str == null || str.length() == 0) {
return str;
}
System.out.println("String length : " + str.length());
ByteArrayOutputStream obj=new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(obj);
gzip.write(str.getBytes("UTF-8"));
gzip.close();
String outStr = obj.toString("UTF-8");
System.out.println("Output String length : " + outStr.length());
return outStr;
}
public static String decompress(String str) throws Exception {
if (str == null || str.length() == 0) {
return str;
}
System.out.println("Input String length : " + str.length());
GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(str.getBytes("UTF-8")));
BufferedReader bf = new BufferedReader(new InputStreamReader(gis, "UTF-8"));
String outStr = "";
String line;
while ((line=bf.readLine())!=null) {
outStr += line;
}
System.out.println("Output String lenght : " + outStr.length());
return outStr;
}
Все еще не удалось выяснить, как исправить эту проблему!!!
Ответы
Ответ 1
Это из-за
String outStr = obj.toString("UTF-8");
Отправьте byte[]
, который вы можете получить из своего ByteArrayOutputStream
, и используйте его как таковой в своем ByteArrayInputStream
для создания GZIPInputStream
. Ниже приведены изменения, которые необходимо выполнить в вашем коде.
byte[] compressed = compress(string); //In the main method
public static byte[] compress(String str) throws Exception {
...
...
return obj.toByteArray();
}
public static String decompress(byte[] bytes) throws Exception {
...
GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
...
}
Ответ 2
Приведенный выше ответ решает нашу проблему, но в дополнение к этому.
если мы пытаемся распаковать несжатый ( "не zip-формат" ) байт [].
мы получим сообщение об ошибке "Not in GZIP format".
Для решения этого вопроса мы можем добавить код добавления в нашем классе.
public static boolean isCompressed(final byte[] compressed) {
return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8));
}
Мой полный класс сжатия с сжатием/распаковкой будет выглядеть так:
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class GZIPCompression {
public static byte[] compress(final String str) throws IOException {
if ((str == null) || (str.length() == 0)) {
return null;
}
ByteArrayOutputStream obj = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(obj);
gzip.write(str.getBytes("UTF-8"));
gzip.flush();
gzip.close();
return obj.toByteArray();
}
public static String decompress(final byte[] compressed) throws IOException {
final StringBuilder outStr = new StringBuilder();
if ((compressed == null) || (compressed.length == 0)) {
return "";
}
if (isCompressed(compressed)) {
final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(compressed));
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gis, "UTF-8"));
String line;
while ((line = bufferedReader.readLine()) != null) {
outStr.append(line);
}
} else {
outStr.append(compressed);
}
return outStr.toString();
}
public static boolean isCompressed(final byte[] compressed) {
return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8));
}
}
Ответ 3
Если вам когда-либо понадобится переносить сжатый контент через сеть или хранить его в виде текста, вы должны использовать кодировщик Base64 (например, apache commons codec Base64) для преобразования массива байтов в строку Base64 и декодировать строку обратно массив байтов на удаленном клиенте.
Нашел пример в Использовать Zip Stream и Base64 Encoder для сжатия больших данных строки!
Ответ 4
Проблема заключается в этой строке:
String outStr = obj.toString("UTF-8");
Байт-массив obj
содержит произвольные двоичные данные. Вы не можете "декодировать" произвольные двоичные данные, как если бы это был UTF-8. Если вы попробуете, вы получите строку, которая затем не может быть "закодирована" обратно в байты. По крайней мере, полученные вами байты будут отличаться от того, с чего вы начали... в той мере, в какой они больше не являются допустимым потоком GZIP.
Исправление состоит в том, чтобы сохранить или передать содержимое массива байтов как есть. Не пытайтесь преобразовать его в строку. Это двоичные данные, а не текст.
Ответ 5
Еще один пример правильной компрессии и декомпрессии:
@Slf4j
public class GZIPCompression {
public static byte[] compress(final String stringToCompress) {
if (isNull(stringToCompress) || stringToCompress.length() == 0) {
return null;
}
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final GZIPOutputStream gzipOutput = new GZIPOutputStream(baos)) {
gzipOutput.write(stringToCompress.getBytes(UTF_8));
gzipOutput.finish();
return baos.toByteArray();
} catch (IOException e) {
throw new UncheckedIOException("Error while compression!", e);
}
}
public static String decompress(final byte[] compressed) {
if (isNull(compressed) || compressed.length == 0) {
return null;
}
try (final GZIPInputStream gzipInput = new GZIPInputStream(new ByteArrayInputStream(compressed));
final StringWriter stringWriter = new StringWriter()) {
IOUtils.copy(gzipInput, stringWriter, UTF_8);
return stringWriter.toString();
} catch (IOException e) {
throw new UncheckedIOException("Error while decompression!", e);
}
}
}
Ответ 6
Вы не можете преобразовать двоичные данные в String. В качестве решения вы можете кодировать двоичные данные, а затем преобразовывать в String. Например, посмотрите на Как преобразовать двоичные данные в строки и обратно в Java?
Ответ 7
Клиенту необходимо отправить несколько сообщений, а сервер (кафка) распаковать строку meesage
Ниже мой образец:
сжать:
public static String compress(String str, String inEncoding) {
if (str == null || str.length() == 0) {
return str;
}
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(out);
gzip.write(str.getBytes(inEncoding));
gzip.close();
return URLEncoder.encode(out.toString("ISO-8859-1"), "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
распаковывать:
public static String decompress(String str, String outEncoding) {
if (str == null || str.length() == 0) {
return str;
}
try {
String decode = URLDecoder.decode(str, "UTF-8");
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(decode.getBytes("ISO-8859-1"));
GZIPInputStream gunzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = gunzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toString(outEncoding);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}