我想压缩一些数据,所以我遇到了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));
归咎于历史先例。在Unix上,用于反转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输出流(或者关闭该流)。
只要做两个小改动就可以让你的代码正常工作。
//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();
首先,您必须关闭()输出流。平减者必须做一些最后的步骤来完成他的工作。
InflaterInputStream inputStream = new InflaterInputStream(arrayInputStream);
如果您使用Deflator InputStream,您将再次压缩已压缩的数据。将其替换为Inflator InputStream,您的代码将正常工作。
我添加这个响应是因为我有一个非常痛苦的实现,使用Java和SpringBoot (RestTemplate)使用IBM DataStage v11.7的一个API。我不确定其他人是否会在使用这种产品或其他产品时遇到类似的问题……但是这些天我遇到了PKIX的问题,API暴露于自签名证书,最后是一个deflate头。很抱歉对这三个问题写了一篇大文章,但您将能够找到前两个问题(带有自签名证书和RestTemplate对象的PKIX或端点)的类似回答。我在这里添加这一点,因为Inflater还没有很多方法。
一些其他的回答说,使用一个自定义的httpclient与resttemplate可能会有所帮助,或者可能已经包含了一个自动"解压";但不知怎的,这次却不一样了。
解压缩或膨胀响应有点棘手,在末尾添加一个额外的字节是神秘的"蛋糕上的樱桃"。经过一番努力,我终于得到了回应,但它打印的是奇怪/特殊的字符。我认为这是一些编码问题,但它实际上是一些问题与膨胀。我必须调试以确定响应大小(长度)实际上与我在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);
}