将GZIP压缩转换为字节数组

23

我正在尝试编写一个可以压缩数据的类。下面的代码失败了(没有抛出异常,但目标.gz文件为空)。 另外:我不想像所有示例中所做的那样直接生成.gz文件。我只想获取压缩后的数据,这样我就可以在将数据写入文件之前加密它。

如果我直接写入文件,一切都运行良好:

import java.io.*;
import java.util.zip.*;
import java.nio.charset.*;

public class Zipper
{
  public static void main(String[] args)
  {    
    byte[] dataToCompress = "This is the test data."
      .getBytes(StandardCharsets.ISO_8859_1);

    GZIPOutputStream zipStream = null;
    FileOutputStream fileStream = null;
    try
    {
      fileStream = new FileOutputStream("C:/Users/UserName/Desktop/zip_file.gz");
      zipStream = new GZIPOutputStream(fileStream);
      zipStream.write(dataToCompress);

      fileStream.write(compressedData);
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
    finally
    {
      try{ zipStream.close(); }
        catch(Exception e){ }
      try{ fileStream.close(); }
        catch(Exception e){ }
    }
  }
}

但是,如果我想要“绕过”它并将其传输到字节数组流中,它不会产生一个字节 - compressedData 总是空的。

import java.io.*;
import java.util.zip.*;
import java.nio.charset.*;

public class Zipper
{
  public static void main(String[] args)
  {    
    byte[] dataToCompress = "This is the test data."
      .getBytes(StandardCharsets.ISO_8859_1);
    byte[] compressedData = null;

    GZIPOutputStream zipStream = null;
    ByteArrayOutputStream byteStream = null;
    FileOutputStream fileStream = null;
    try
    {
      byteStream = new ByteArrayOutputStream(dataToCompress.length);
      zipStream = new GZIPOutputStream(byteStream);
      zipStream.write(dataToCompress);

      compressedData = byteStream.toByteArray();

      fileStream = new FileOutputStream("C:/Users/UserName/Desktop/zip_file.gz");
      fileStream.write(compressedData);
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
    finally
    {
      try{ zipStream.close(); }
        catch(Exception e){ }
      try{ byteStream.close(); }
        catch(Exception e){ }
      try{ fileStream.close(); }
        catch(Exception e){ }
    }
  }
}

4
The below code fails” 不是一个有效的 Java 错误信息。 - user330315
1
它只是生成了一个不是ZIP归档文件的文件。没有错误消息。 - Master-Jimmy
1
当你没有在异常中记录任何内容时,没有错误消息是很正常的。 - njzk2
它不会产生任何可以记录的异常。 ZIP文件的“组装”必须出了问题。 - Master-Jimmy
3
要获取仅包含压缩数据的流,请使用 DeflaterOutputStreamGZipOutputStream - millimoose
显示剩余4条评论
7个回答

39

问题在于你没有关闭GZIPOutputStream。在你关闭它之前,输出将不完整。

你只需要在读取字节数组之前关闭它即可。需要重新排列finally块以实现此目的。

import java.io.*;
import java.util.zip.*;
import java.nio.charset.*;

public class Zipper
{
  public static void main(String[] args)
  {    
    byte[] dataToCompress = "This is the test data."
      .getBytes(StandardCharsets.ISO_8859_1);

    try
    {
      ByteArrayOutputStream byteStream =
        new ByteArrayOutputStream(dataToCompress.length);
      try
      {
        GZIPOutputStream zipStream =
          new GZIPOutputStream(byteStream);
        try
        {
          zipStream.write(dataToCompress);
        }
        finally
        {
          zipStream.close();
        }
      }
      finally
      {
        byteStream.close();
      }

      byte[] compressedData = byteStream.toByteArray();

      FileOutputStream fileStream =
        new FileOutputStream("C:/Users/UserName/Desktop/zip_file.gz");
      try
      {
        fileStream.write(compressedData);
      }
      finally
      {
        try{ fileStream.close(); }
          catch(Exception e){ /* We should probably delete the file now? */ }
      }
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
  }
}

我建议不要将流变量初始化为null,因为这意味着您的finally块也可能会抛出NullPointerException异常。

此外,请注意您可以声明main方法抛出IOException(这样您就不需要最外层的try语句)。

没有必要在zipStream.close();中捕获并忽略异常,因为如果它抛出异常,您将无法得到一个有效的.gz文件(因此您不应继续写入)。

同时,我也不建议从byteStream.close();中捕获并忽略异常,但原因不同 - 它们不应该被抛出(即您的JRE中存在错误,您需要知道这一点)。


你需要关闭GZIPOutputStream,否则会得到部分数据。 - zertyz

32

我改进了JITHINRAJ的代码-使用try-with-resources:

private static byte[] gzipCompress(byte[] uncompressedData) {
        byte[] result = new byte[]{};
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream(uncompressedData.length);
             GZIPOutputStream gzipOS = new GZIPOutputStream(bos)) {
            gzipOS.write(uncompressedData);
            // You need to close it before using bos
            gzipOS.close();
            result = bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

private static byte[] gzipUncompress(byte[] compressedData) {
        byte[] result = new byte[]{};
        try (ByteArrayInputStream bis = new ByteArrayInputStream(compressedData);
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             GZIPInputStream gzipIS = new GZIPInputStream(bis)) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = gzipIS.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            result = bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

3
这里不需要使用gzipOS.close();,因为GZIPOutputStreamAutoCloseable的。 - Eric Wendelin
3
gzipOS.close(); 仍然是必需的!没有它,Gzip 压缩将无效。请尝试修复。 - Yanislav Kornev
@YanislavKornev 你说得完全正确!如果我不关闭 gzipOS,那么我将得到不正确的数组。 - Vladislav Kysliy
@slartidan 如果出现异常会发生什么? - Vladislav Kysliy
真的...忘记我说的吧 :) - slartidan
显示剩余3条评论

6
如果您仍在寻找答案,可以使用以下代码使用deflater获取压缩的byte[],并使用inflater进行解压缩。
public static void main(String[] args) {
        //Some string for testing
        String sr = new String("fsdfesfsfdddddddsfdsfssdfdsfdsfdsfdsfdsdfggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghghghghggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggfsdfesfsfdddddddsfdsfssdfdsfdsfdsfdsfdsdfggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghghghghggggggggggggggggggggggggggggggggggggggggg");
        byte[] data = sr.getBytes();
        System.out.println("src size "+data.length);
        try {
            compress(data);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static byte[] compress(byte[] data) throws IOException { 
        Deflater deflater = new Deflater(); 
        deflater.setInput(data); 
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);  

        deflater.finish(); 
        byte[] buffer = new byte[1024];  
        while (!deflater.finished()) { 
        int count = deflater.deflate(buffer);  
        outputStream.write(buffer, 0, count);  
        } 
        outputStream.close(); 
        byte[] output = outputStream.toByteArray(); 

        System.out.println("Original: " + data.length  ); 
        System.out.println("Compressed: " + output.length ); 
        return output; 
        }   

这是一个无效的答案,因为Inflater和Deflater使用Zlib而不是GZip。 - Henk Schurink

2

压缩

private static byte[] compress(byte[] uncompressedData) {
        ByteArrayOutputStream bos = null;
        GZIPOutputStream gzipOS = null;
        try {
            bos = new ByteArrayOutputStream(uncompressedData.length);
            gzipOS = new GZIPOutputStream(bos);
            gzipOS.write(uncompressedData);
            gzipOS.close();
            return bos.toByteArray();

        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                assert gzipOS != null;
                gzipOS.close();
                bos.close();
            }
            catch (Exception ignored) {
            }
        }
        return new byte[]{};
    }

解压缩

private byte[] uncompress(byte[] compressedData) {
        ByteArrayInputStream bis = null;
        ByteArrayOutputStream bos = null;
        GZIPInputStream gzipIS = null;

        try {
            bis = new ByteArrayInputStream(compressedData);
            bos = new ByteArrayOutputStream();
            gzipIS = new GZIPInputStream(bis);

            byte[] buffer = new byte[1024];
            int len;
            while((len = gzipIS.read(buffer)) != -1){
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                assert gzipIS != null;
                gzipIS.close();
                bos.close();
                bis.close();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        return new byte[]{};
    }

2
大多数示例的异常处理有误。
public static byte[] gzipBytes(byte[] payload) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try (GZIPOutputStream gzip = new GZIPOutputStream(baos)) {
        gzip.write(payload);
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
    // note: toByteArray should be called after try-with-resources, not inside
    return baos.toByteArray();
}

public static byte[] gunzipBytes(byte[] gzPayload) {
    ByteArrayInputStream bais = new ByteArrayInputStream(gzPayload);
    try (GZIPInputStream gzip = new GZIPInputStream(bais)) {
        // java 9+ required for this method
        return gzip.readAllBytes();
    } catch (IOException e) {
        throw new UncheckedIOException("Error while unpacking gzip content", e);
    }
}

1
你可以使用下面的函数,它经过测试并且运行良好。
一般来说,你的代码存在严重的问题,即忽略了异常!在catch块中返回null或者什么都不打印将使调试变得非常困难。
如果你想进一步处理(例如加密)而不必将zip输出写入文件,则可以轻松修改代码以将输出写入内存流。
public static String zip(File inFile, File zipFile) throws IOException {        
    FileInputStream fis = new FileInputStream(inFile);
    FileOutputStream fos = new FileOutputStream(zipFile);
    ZipOutputStream zout = new ZipOutputStream(fos);

    try {
        zout.putNextEntry(new ZipEntry(inFile.getName()));
        byte[] buffer = new byte[BUFFER_SIZE];
        int len;
        while ((len = fis.read(buffer)) > 0) {
            zout.write(buffer, 0, len);
        }
        zout.closeEntry();
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    } finally {
        try{zout.close();}catch(Exception ex){ex.printStackTrace();}
        try{fis.close();}catch(Exception ex){ex.printStackTrace();}         
    }
    return zipFile.getAbsolutePath();
}

可以在 try/catch 块中添加每个 close() 语句并忽略错误。 - iTech

0

尝试使用这段代码...

try {
    String inputFileName = "test.txt";  //may use your file_Path
    String zipFileName = "compressed.zip";

    //Create input and output streams
    FileInputStream inStream = new FileInputStream(inputFileName);
    ZipOutputStream outStream = new ZipOutputStream(new FileOutputStream(zipFileName));

    // Add a zip entry to the output stream
    outStream.putNextEntry(new ZipEntry(inputFileName));

    byte[] buffer = new byte[1024];
    int bytesRead;

    //Each chunk of data read from the input stream
    //is written to the output stream
    while ((bytesRead = inStream.read(buffer)) > 0) {
        outStream.write(buffer, 0, bytesRead);
    }

    //Close zip entry and file streams
    outStream.closeEntry();

    outStream.close();
    inStream.close();

} catch (IOException ex) {
    ex.printStackTrace();
}

这个也可能有帮助..


这段代码直接写入了ZIP文件,这不是我想要的。但还是谢谢你。 - Master-Jimmy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接