【问题标题】:GZIP compression to a byte arrayGZIP 压缩到字节数组
【发布时间】:2013-01-24 12:39:48
【问题描述】:

我正在尝试编写一个可以压缩数据的类。以下代码失败(不抛出异常,但目标 .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){ }
    }
  }
}

【问题讨论】:

  • 以下代码失败”不是有效的 Java 错误消息。
  • 请同时包含错误的堆栈跟踪。
  • 它只是生成一个不是 ZIP 存档的文件。没有错误信息。
  • 呃。没有错误消息,因为您没有在异常中记录任何内容。
  • 另外,要获取压缩数据流,请使用DeflaterOutputStreamGZipOutputStream

标签: java android stream gzip


【解决方案1】:

问题是您没有关闭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 否则会得到部分数据
【解决方案2】:

我改进了 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;
    }

【讨论】:

  • gzipOS.close(); 在这里没有必要,因为GZIPOutputStreamAutoCloseable
  • gzipOS.close(); 仍然是必需的!没有它,Gzip 压缩将无效。请尝试修复
  • @YanislavKornev 你说得对!如果我不关闭 gzipOS 我会得到不正确的数组
  • 我如何知道压缩数组的长度? toByteArray 压缩我的数组,但不删除尾随零。
  • @cansik 你可以使用 result.length。您可以在此处获取有关 gzip alg-m 的更多信息 - en.wikipedia.org/wiki/Gzip
【解决方案3】:

如果您仍在寻找答案,您可以使用下面的代码使用 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。
【解决方案4】:

压缩

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[]{};
    }

【讨论】:

    【解决方案5】:

    您可以使用以下功能,它已经过测试并且可以正常工作。

    一般来说,您的代码存在忽略异常的严重问题!返回null 或者根本不打印catch 块中的任何内容都会使调试变得非常困难

    如果您想进一步处理它(例如加密),您不必将 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() 语句并忽略错误
    【解决方案6】:

    试试这个代码..

    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 文件;这是我不想要的。不过还是谢谢你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多