java gzip rest_RestTemplate与Gzip压缩
Gzip 是一種壓縮算法,服務(wù)器經(jīng)常通過(guò)這個(gè)算法來(lái)壓縮響應(yīng)體,再響應(yīng)給客戶端,從而減少數(shù)據(jù)體積,提高傳輸速度。客戶端再通過(guò)Gzip解壓縮,獲取到原始的數(shù)據(jù)。因?yàn)樾枰獕嚎s計(jì)算,所以會(huì)耗費(fèi)額外的CPU資源。
Gzip 與 HttpHeader
對(duì)于壓縮,這個(gè)行為來(lái)說(shuō),客戶端與服務(wù)器都要經(jīng)過(guò)協(xié)商。只有使用了同一種壓縮算法,才能正確的解碼出數(shù)據(jù)。http協(xié)議中定義了相關(guān)的header
Content-Encoding
是一個(gè)實(shí)體消息首部,用于對(duì)特定媒體類型的數(shù)據(jù)進(jìn)行壓縮。當(dāng)這個(gè)首部出現(xiàn)的時(shí)候,它的值表示消息主體進(jìn)行了何種方式的內(nèi)容編碼轉(zhuǎn)換。這個(gè)消息首部用來(lái)告知客戶端應(yīng)該怎樣解碼才能獲取在 Content-Type 中標(biāo)示的媒體類型內(nèi)容。
一般建議對(duì)數(shù)據(jù)盡可能地進(jìn)行壓縮,因此才有了這個(gè)消息首部的出現(xiàn)。不過(guò)對(duì)于特定類型的文件來(lái)說(shuō),比如jpeg圖片文件,已經(jīng)是進(jìn)行過(guò)壓縮的了。有時(shí)候再次進(jìn)行額外的壓縮無(wú)助于負(fù)載體積的減小,反而有可能會(huì)使其增大。
客戶端和服務(wù)器都可以使用,表示body中的數(shù)據(jù)采用了什么編碼(壓縮算法)
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Encoding
Accept-Encoding
HTTP 請(qǐng)求頭 Accept-Encoding 會(huì)將客戶端能夠理解的內(nèi)容編碼方式——通常是某種壓縮算法——進(jìn)行通知(給服務(wù)端)。通過(guò)內(nèi)容協(xié)商的方式,服務(wù)端會(huì)選擇一個(gè)客戶端提議的方式,使用并在響應(yīng)頭 Content-Encoding 中通知客戶端該選擇。
即使客戶端和服務(wù)器都支持相同的壓縮算法,在 identity 指令可以被接受的情況下,服務(wù)器也可以選擇對(duì)響應(yīng)主體不進(jìn)行壓縮。導(dǎo)致這種情況出現(xiàn)的兩種常見的情形是:
要發(fā)送的數(shù)據(jù)已經(jīng)經(jīng)過(guò)壓縮,再次進(jìn)行壓縮不會(huì)導(dǎo)致被傳輸?shù)臄?shù)據(jù)量更小。一些圖像格式的文件會(huì)存在這種情況;
服務(wù)器超載,無(wú)法承受壓縮需求導(dǎo)致的計(jì)算開銷。通常,如果服務(wù)器使用超過(guò)80%的計(jì)算能力,微軟建議不要壓縮。
只要 identity —— 表示不需要進(jìn)行任何編碼——沒(méi)有被明確禁止使用(通過(guò) identity;q=0 指令或是 *;q=0 而沒(méi)有為 identity 明確指定權(quán)重值),則服務(wù)器禁止返回表示客戶端錯(cuò)誤的 406 Not Acceptable 響應(yīng)。
一般是客戶端使用,表示給服務(wù)器說(shuō)明,客戶端支持的壓縮算法列表。服務(wù)從中選擇一個(gè)對(duì)響應(yīng)體進(jìn)行壓縮。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Encoding
演示一個(gè)手動(dòng)編/解碼的Demo
服務(wù)端手動(dòng)進(jìn)行Gzip編碼
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController {
private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
@GetMapping
public void test(HttpServletRequest request, HttpServletResponse reponse) throws IOException {
// 響應(yīng)體
String content = "昔日齷齪不足夸,今朝放蕩思無(wú)涯。春風(fēng)得意馬蹄疾,一日看盡長(zhǎng)安花。";
String acceptEncooding = request.getHeader(HttpHeaders.ACCEPT_ENCODING);
/**
* 獲取客戶端支持的編碼格式,程序可以根據(jù)這個(gè)header判斷是否要對(duì)響應(yīng)體進(jìn)行編碼
*/
LOGGER.info(acceptEncooding);
// 響應(yīng)體使用 gzip 編碼
reponse.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
// 響應(yīng)體類型是字符串
reponse.setContentType(MediaType.TEXT_PLAIN_VALUE);
// 編碼是utf-8
reponse.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
// Gzip壓縮后響應(yīng)
reponse.getOutputStream().write(gZip(content.getBytes(StandardCharsets.UTF_8)));
}
/**
* Gzip壓縮數(shù)據(jù)
* @param data
* @return
* @throws IOException
*/
public static byte[] gZip(byte[] data) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
gzipOutputStream.write(data);
gzipOutputStream.finish();
return byteArrayOutputStream.toByteArray();
}
}
}
客戶端手動(dòng)解碼
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
public class Main {
public static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) throws Exception {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders httpHeaders = new HttpHeaders();
// Accept 表示客戶端支持什么格式的響應(yīng)體
httpHeaders.set(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN_VALUE);
// Accept-Encoding 頭,表示客戶端接收gzip格式的壓縮
httpHeaders.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
ResponseEntity responseEntity = restTemplate.exchange("http://localhost/test", HttpMethod.GET, new HttpEntity<>(httpHeaders), byte[].class);
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
// TODO 非200響應(yīng)
}
// 獲取服務(wù)器響應(yīng)體編碼
String contentEncoding = responseEntity.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
if ("gzip".equals(contentEncoding)) { // gzip編碼
// gzip解壓服務(wù)器的響應(yīng)體
byte[] data = unGZip(new ByteArrayInputStream(responseEntity.getBody()));
LOGGER.info(new String(data, StandardCharsets.UTF_8));
} else {
// TODO 其他的編碼
}
}
/**
* Gzip解壓縮
* @param inputStream
* @return
* @throws IOException
*/
public static byte[] unGZip(InputStream inputStream) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
byte[] buf = new byte[4096];
int len = -1;
while ((len = gzipInputStream.read(buf, 0, buf.length)) != -1) {
byteArrayOutputStream.write(buf, 0, len);
}
return byteArrayOutputStream.toByteArray();
} finally {
byteArrayOutputStream.close();
}
}
}
客戶端執(zhí)行日志,準(zhǔn)確的解碼了響應(yīng)體
20:36:54.129 [main] INFO - 昔日齷齪不足夸,今朝放蕩思無(wú)涯。春風(fēng)得意馬蹄疾,一日看盡長(zhǎng)安花。
SpringBoot的響應(yīng)體壓縮配置
實(shí)際上,并不需要自己手動(dòng)去寫這種響應(yīng)體的壓縮代碼。springboot提供了相關(guān)的配置。
SpringBoot2開啟響應(yīng)壓縮
最后
使用RestTemplate請(qǐng)求文本數(shù)據(jù)接口,發(fā)現(xiàn)解碼后的字符串是亂碼。此時(shí)就可以懷疑是不是服務(wù)器響應(yīng)了壓縮后的數(shù)據(jù)。解決這個(gè)問(wèn)題,先嘗試移除Accept-Encoding請(qǐng)求頭,告訴服務(wù)器,客戶端不需要壓縮響應(yīng)體。如果服務(wù)器還是響應(yīng)壓縮后的數(shù)據(jù),嘗試讀取服務(wù)器的Content-Encoding頭,根據(jù)服務(wù)器的壓縮編碼,自己再進(jìn)行解壓縮。
原文: https://springboot.io/t/topic/2868
?
總結(jié)
以上是生活随笔為你收集整理的java gzip rest_RestTemplate与Gzip压缩的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 史上最全jmeter逻辑控制器案例详解
- 下一篇: Python Pytest调用fixtu