Java java.io.IOException: не в формате GZIP
Я искал пример того, как сжать строку в Java.
У меня есть функция, чтобы сжать, а затем распаковать. Компресс, кажется, работает нормально:
public static String encStage1(String str)
{
String format1 = "ISO-8859-1";
String format2 = "UTF-8";
if (str == null || str.length() == 0)
{
return str;
}
System.out.println("String length : " + str.length());
ByteArrayOutputStream out = new ByteArrayOutputStream();
String outStr = null;
try
{
GZIPOutputStream gzip = new GZIPOutputStream(out);
gzip.write(str.getBytes());
gzip.close();
outStr = out.toString(format2);
System.out.println("Output String lenght : " + outStr.length());
} catch (Exception e)
{
e.printStackTrace();
}
return outStr;
}
Но наоборот - жалоба на то, что строка не в формате GZIP, даже когда я передаю возврат из encStage1 обратно в decStage3:
public static String decStage3(String str)
{
if (str == null || str.length() == 0)
{
return str;
}
System.out.println("Input String length : " + str.length());
String outStr = "";
try
{
String format1 = "ISO-8859-1";
String format2 = "UTF-8";
GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(str.getBytes(format2)));
BufferedReader bf = new BufferedReader(new InputStreamReader(gis, format2));
String line;
while ((line = bf.readLine()) != null)
{
outStr += line;
}
System.out.println("Output String lenght : " + outStr.length());
} catch (Exception e)
{
e.printStackTrace();
}
return outStr;
}
Я получаю эту ошибку при вызове со строкой возврата из encStage1:
public String encIDData(String idData)
{
String tst = "A simple test string";
System.out.println("Enc 0: " + tst);
String stg1 = encStage1(tst);
System.out.println("Enc 1: " + toHex(stg1));
String dec1 = decStage3(stg1);
System.out.println("unzip: " + toHex(dec1));
}
Выход /Error:
Enc 0: A simple test string
String length : 20
Output String lenght : 40
Enc 1: 1fefbfbd0800000000000000735428efbfbdefbfbd2defbfbd495528492d2e51282e29efbfbdefbfbd4b07005aefbfbd21efbfbd14000000
Input String length : 40
java.io.IOException: Not in GZIP format
at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:137)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:58)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:68)
2 ответа
Небольшая ошибка:
gzip.write(str.getBytes());
принимает кодировку платформы по умолчанию, которая в Windows никогда не будет ISO-8859-1. Лучше:
gzip.write(str.getBytes(format1));
Вы можете рассмотреть вариант "Cp1252", Windows Latin-1 (для некоторых европейских языков) вместо "ISO-8859-1", Latin-1. Это добавляет запятую, как кавычки и тому подобное.
Основная ошибка - преобразование сжатых байтов в строку. Java отделяет двоичные данные (byte[], InputStream, OutputStream) от текста (String, char, Reader, Writer), который всегда хранится в Unicode. Последовательность байтов не обязательно должна быть действительной UTF-8. Вы можете уйти, преобразовав байты в однобайтовую кодировку (например, ISO-8859-1).
Лучший способ будет
gzip.write(str.getBytes(StandardCharsets.UTF_8));
Таким образом, у вас есть полный Unicode, каждый скрипт может быть объединен.
И распаковка ByteArrayOutputStream
а также new String(baos.toByteArray(), StandardCharsets.UTF_8)
, Использование BufferedReader на InputStreamReader с UTF-8 тоже хорошо, но readLine отбрасывает символы новой строки
outStr += line + "\r\n"; // Or so.
Чистый ответ:
public static byte[] encStage1(String str) throws IOException
{
try (ByteArrayOutputStream out = new ByteArrayOutputStream())
{
try (GZIPOutputStream gzip = new GZIPOutputStream(out))
{
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return out.toByteArray();
//return out.toString(StandardCharsets.ISO_8859_1);
// Some single byte encoding
}
}
public static String decStage3(byte[] str) throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(str)))
{
int b;
while ((b = gis.read()) != -1) {
baos.write((byte) b);
}
}
return new String(baos.toByteArray(), StandardCharset.UTF_8);
}
Использование toString/getBytes для кодирования / декодирования является неправильным способом. попробуйте использовать что-то вроде кодировки BASE64 для этой цели (java.util.Base64 в jdk 1.8)
в качестве доказательства попробуйте этот простой тест:
import org.testng.annotations.Test;
import java.io.ByteArrayOutputStream;
import static org.testng.Assert.assertEquals;
public class SimpleTest {
@Test
public void test() throws Exception {
final String CS = "utf-8";
byte[] b0 = {(byte) 0xff};
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(b0);
out.close();
byte[] b1 = out.toString(CS).getBytes(CS);
assertEquals(b0, b1);
}
}