11

我想压缩一些数据,所以我遇到了 DeflatorInputStream 和 DeflatorOutputStream 类。但是,以下示例表明,在使用这些类时,我似乎无法重建原始数据。

当我切换到 ZipInputStream 和 ZipOutputStream 时,它确实可以工作,但由于我本身不需要 zip 文件,我认为通用压缩会更好。主要是我有兴趣了解为什么这个例子不起作用。

//Create some "random" data
int bytesLength = 1024;
byte[] bytes = new byte[bytesLength];
for(int i = 0; i < bytesLength; i++) {
     bytes[i] = (byte) (i % 10);
}

//Compress the data, and write it to somewhere (a byte array for this example)
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
DeflaterOutputStream outputStream = new DeflaterOutputStream(arrayOutputStream);
outputStream.write(bytes);

//Read and decompress the data
byte[] readBuffer = new byte[5000];
ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(arrayOutputStream.toByteArray());
DeflaterInputStream inputStream = new DeflaterInputStream(arrayInputStream);
int read = inputStream.read(readBuffer);

//Should hold the original (reconstructed) data
byte[] actuallyRead = Arrays.copyOf(readBuffer, read);

//Results differ - will print false
System.out.println(Arrays.equals(bytes, actuallyRead));
4

3 回答 3

22

责备历史先例。在 Unix 上,用于反转 a 的函数deflate称为inflate. 因此,与许多其他 Java IO 类不同,输入和输出流对没有(显然)匹配的名称。

DeflaterOutputStream 实际上不允许您反转通货紧缩,而是在字节从接收器传递到源时对其进行压缩。DeflaterInputStream也会放气,但它会在数据从源流向接收器时执行其操作。

为了以未压缩(膨胀)格式读取数据,您需要使用InflaterInputStream

InflaterInputStream inputStream = new InflaterInputStream(arrayInputStream);

此外,由于一次调用可能无法从流中获取所有压缩数据read,因此您需要使用循环。像这样的东西:

int read;
byte[] finalBuf = new byte[0], swapBuf;
byte[] readBuffer = new byte[5012];

ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(
        compressed);
InflaterInputStream inputStream = new InflaterInputStream(
        arrayInputStream);
while ((read = inputStream.read(readBuffer)) != -1) {
    System.out.println("Intermediate read: " + read);
    swapBuf = finalBuf;
    finalBuf = new byte[swapBuf.length + read];
    System.arraycopy(swapBuf, 0, finalBuf, 0, swapBuf.length);
    System.arraycopy(readBuffer, 0, finalBuf, swapBuf.length, read);
}

最后,确保在检索压缩字节之前刷新你的 deflater 输出流(或者关闭流)。

于 2013-04-01T11:37:30.377 回答
1

只有 2 个小改动可以使您的代码正常工作。

//Compress the data, and write it to somewhere (a byte array for this example)
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
DeflaterOutputStream outputStream = new DeflaterOutputStream(arrayOutputStream);
outputStream.write(bytes);
outputStream.close();

首先,您必须close()输出流。通货紧缩器必须完成一些最后的步骤才能完成他的工作。

InflaterInputStream inputStream = new InflaterInputStream(arrayInputStream);

如果您使用Deflator InputStream,您将再次压缩压缩数据。用Inflator InputStream 替换它,您的代码就可以正常工作了。

于 2013-04-01T11:38:56.620 回答
0

好的,我正在添加此响应,因为我有一个非常痛苦的实现,即使用 Java 和 SpringBoot (RestTemplate) 使用 IBM DataStage v11.7 中的一个 API。我不确定其他人是否会在使用此类产品或任何其他产品时遇到类似问题……但是这些天我遇到了 PKIX 问题、API 暴露于自签名证书以及最后一个 deflate 标头的问题。很抱歉用这三个回复写了一篇大文章,但您将能够找到前两个问题(PKIX 或具有自签名证书和 RestTemplate 对象的端点)的类似回复。我在这里添加这个是因为 Inflater 还没有很多方法。

其他一些答案说,使用带有 resttemplate 的自定义 httpclient 可能会有所帮助,或者可能已经包含自动“解压缩”,但不知何故,这种情况并不相同。

解压缩或膨胀响应有点棘手,最后添加一个额外的字节是神秘的“蛋糕上的樱桃”。经过一番挣扎后,我设法得到了响应,但它正在打印奇怪/特殊的字符。我认为这是一些编码问题,但实际上是 Inflater 的一些问题。我必须调试以确定响应大小(长度)实际上与我在 Postman 中收到的相同。所以我说,好吧,长度是相似的,所以可能会发生什么。因此,感谢为我们的解决方案做出贡献的所有其他开发人员。

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.SSLContext;

import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.TrustStrategy;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

    private static Map<String, String> headersMap = new HashMap<String, String>() {
        {
            put("Authorization", "Basic XYZ");
            put("Accept", "*/*");
            put("Accept-Encoding", "deflate");
            put("Connection", "keep-alive");
        }
    };

    private static final String baseUrlDesigner = "https://{HOST}/ibm/iis/api/dscdesignerapi";

    private static final String designerGetAllJobs = "?api=getJobList&hostName={HOST}&projectName={PROJECT}&compress=nowrap9";

    public static RestTemplate getRestTemplateNoSSLValidation( ) {
        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
        SSLContext sslContext = null;
        try {
            sslContext = org.apache.http.ssl.SSLContexts.custom()
                    .loadTrustMaterial(null, acceptingTrustStrategy)
                    .build();
        } catch (KeyManagementException e2) {
            e2.printStackTrace();
        } catch (NoSuchAlgorithmException e2) {
            e2.printStackTrace();
        } catch (KeyStoreException e2) {
            e2.printStackTrace();
        }

        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

        HttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)               
                .disableContentCompression()
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory();

        requestFactory.setHttpClient(httpClient);

        RestTemplate restTemplate = new RestTemplate(requestFactory);
        return restTemplate;
    }

    public static <T> ResponseEntity<T> sendRequest(String url, HttpMethod method, Map<String,String> headerList, Class<T> responseType) {    
        RestTemplate restTemplate = getRestTemplateNoSSLValidation(); 
        HttpHeaders headers = new HttpHeaders();        
        for (Entry<String,String> e:headerList.entrySet())          
            headers.add(e.getKey(),e.getValue());
        
        HttpEntity<String> request = new HttpEntity<String>(null, headers);     

        ResponseEntity<T> result = (ResponseEntity<T>) restTemplate.exchange(url, method, request, responseType);       
        
        return result;      
    }       

    public static String decompress(byte[] bytes) {
        Inflater inf = new Inflater(true);
        InputStream in = new InflaterInputStream(new ByteArrayInputStream(bytes), inf);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = in.read(buffer)) > 0)
                baos.write(buffer, 0, len);
            baos.write(new byte[1], 0, 1); // FIX. Had to force one extra byte, otherwise the body string was not read properly :shrug:
            return new String(baos.toByteArray(), "UTF-8");
        } catch (IOException e) {
            throw new AssertionError(e);
        }
    }


    public static void main(String[] args) throws IOException {
        ResponseEntity<byte[]> responseBytes =
          sendRequest(baseUrlDesigner + designerGetAllJobs,
                HttpMethod.GET, headersMap, byte[].class);
        String respString = decompress(responseBytes.getBody());
        System.out.println("Response lenght: " + respString.length());
        System.out.println("Response body: " + respString);
    }
于 2022-01-11T06:14:42.690 回答