-
Notifications
You must be signed in to change notification settings - Fork 328
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
402 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
## web-res-gzip | ||
### 项目说明 | ||
|
||
本项目主要介绍Spring返回结果压缩的使用姿势 | ||
|
||
1. 压缩配置 | ||
|
||
```yaml | ||
server: | ||
compression: | ||
enabled: true # 开启支持gzip压缩 | ||
min-response-size: 128 # 当响应长度超过128时,才执行压缩 | ||
``` | ||
2. 返回JsonObject无效场景 | ||
当接口返回的是 application/json 时,上面配置的 min-response-size 无效,全部都走压缩,原因是因为无法计算 content-length,可行的解决方案: | ||
- 设置统一的Filter, 通过`ContentCachingResponseWrapper`包装返回结果 -> 见:Application中的配置 | ||
|
||
3. 返回压缩的静态资源 | ||
|
||
```java | ||
public class Application implements WebMvcConfigurer { | ||
/** | ||
* 配置返回的静态资源的压缩与缓存方式 | ||
* | ||
* @param registry | ||
*/ | ||
@Override | ||
public void addResourceHandlers(ResourceHandlerRegistry registry) { | ||
registry.addResourceHandler("/static/**") | ||
.addResourceLocations("classpath:/static/") | ||
.setCacheControl(CacheControl.maxAge(7, TimeUnit.DAYS).cachePrivate()) | ||
.resourceChain(true) | ||
// https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/resource/EncodedResourceResolver.html | ||
.addResolver(new EncodedResourceResolver()) | ||
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**")); | ||
} | ||
} | ||
``` | ||
|
||
上面配置了静态资源文件的缓存策略,压缩方式,需要注意 EncodedResourceResolver 需要在 VersionResourceResolver 之前(上面Spring的连接说明) | ||
|
||
### 博文说明 | ||
|
||
本项目对应的博文内容为 | ||
|
||
[231108-SpringBoot系列之压缩返回结果实例演示 | 一灰灰Blog](http://spring.hhui.top/spring-blog/2023/11/08/231108-SpringBoot%E7%B3%BB%E5%88%97%E4%B9%8B%E5%8E%8B%E7%BC%A9%E8%BF%94%E5%9B%9E%E7%BB%93%E6%9E%9C%E5%AE%9E%E4%BE%8B%E6%BC%94%E7%A4%BA/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>com.git.hui.boot</groupId> | ||
<artifactId>spring-boot</artifactId> | ||
<version>0.0.1-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>207-web-res-gzip</artifactId> | ||
|
||
<properties> | ||
<maven.compiler.source>8</maven.compiler.source> | ||
<maven.compiler.target>8</maven.compiler.target> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
</properties> | ||
|
||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.httpcomponents</groupId> | ||
<artifactId>httpclient</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
85 changes: 85 additions & 0 deletions
85
spring-boot/207-web-res-gzip/src/main/java/com/git/hui/boot/web/Application.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package com.git.hui.boot.web; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.http.CacheControl; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
import org.springframework.web.servlet.resource.EncodedResourceResolver; | ||
import org.springframework.web.servlet.resource.VersionResourceResolver; | ||
import org.springframework.web.util.ContentCachingResponseWrapper; | ||
|
||
import javax.servlet.FilterChain; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* @author YiHui | ||
* @date 2023/11/6 | ||
*/ | ||
@SpringBootApplication | ||
public class Application implements WebMvcConfigurer { | ||
|
||
/** | ||
* 配置返回的静态资源的压缩与缓存方式 | ||
* | ||
* @param registry | ||
*/ | ||
@Override | ||
public void addResourceHandlers(ResourceHandlerRegistry registry) { | ||
registry.addResourceHandler("/static/**") | ||
.addResourceLocations("classpath:/static/") | ||
.setCacheControl(CacheControl.maxAge(7, TimeUnit.DAYS).cachePrivate()) | ||
.resourceChain(true) | ||
.addResolver(new EncodedResourceResolver()) | ||
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**")); | ||
} | ||
|
||
/** | ||
* 所有的返回结果,包装一个 content-length 返回 | ||
* | ||
* @return | ||
*/ | ||
@Bean | ||
public FilterRegistrationBean filterRegistrationBean() { | ||
FilterRegistrationBean filterBean = new FilterRegistrationBean(); | ||
filterBean.setFilter(new AddContentLengthFilter()); | ||
filterBean.setUrlPatterns(Arrays.asList("*")); | ||
return filterBean; | ||
} | ||
|
||
/** | ||
* 现象:当返回的是json对象时, server.compression.min-response-size不起作用,不管这个对象的大小,默认全部做gzip压缩。 | ||
* 原因: | ||
* - 当返回的是字符串,即Content-Type: text/plain 时,会设置Content-Length,则会根据实际返回的大小来判断是否需要进行gzip压缩 | ||
* - 而当返回的是对象,即Content-Type: application/json时,不会设置Content-Length,服务端无法判断长度,并且是通过Transfer-Encoding: chunked的方式发送给客户端,因此一定会做压缩。 | ||
* 解决方案: | ||
* - 加上全局的 content-length | ||
*/ | ||
class AddContentLengthFilter extends OncePerRequestFilter { | ||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { | ||
// ContentCachingResponseWrapper会缓存所有写给OutputStream的数据,并且因为缓存了内容,所以可以获取Content-Length并帮忙设置 | ||
ContentCachingResponseWrapper cacheResponseWrapper; | ||
if (response instanceof ContentCachingResponseWrapper) { | ||
cacheResponseWrapper = (ContentCachingResponseWrapper) response; | ||
} else { | ||
cacheResponseWrapper = new ContentCachingResponseWrapper(response); | ||
} | ||
|
||
filterChain.doFilter(request, cacheResponseWrapper); | ||
cacheResponseWrapper.copyBodyToResponse(); | ||
} | ||
} | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(Application.class); | ||
} | ||
} |
146 changes: 146 additions & 0 deletions
146
spring-boot/207-web-res-gzip/src/main/java/com/git/hui/boot/web/rest/RspController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package com.git.hui.boot.web.rest; | ||
|
||
import com.alibaba.fastjson.JSON; | ||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
import org.springframework.http.HttpEntity; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.util.MultiValueMap; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.ResponseBody; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.UUID; | ||
|
||
/** | ||
* 返回结果 | ||
* | ||
* @author YiHui | ||
* @date 2023/11/6 | ||
*/ | ||
@Controller | ||
public class RspController { | ||
|
||
@Data | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public static final class UserInfo { | ||
private String name; | ||
private Integer code; | ||
} | ||
|
||
private List<UserInfo> allUsers(int size) { | ||
List<UserInfo> list = new ArrayList<>(); | ||
for (int i = 0; i < size; i++) { | ||
list.add(new UserInfo(UUID.randomUUID() + "_用户", i)); | ||
} | ||
return list; | ||
} | ||
|
||
/** | ||
* 对象方式返回, 用于模拟不管返回的内容多小,都会进行gzip压缩 | ||
* | ||
* @return | ||
*/ | ||
@ResponseBody | ||
@GetMapping(path = "list") | ||
public List<UserInfo> list(@RequestParam(name = "size", defaultValue = "128", required = false) Integer size) { | ||
List<UserInfo> list = allUsers(size); | ||
return list.subList(0, size); | ||
} | ||
|
||
/** | ||
* 字符串方式返回, 会根据设置的最小长度,来确定是否会对返回结果进行gzip压缩 | ||
* | ||
* @return | ||
*/ | ||
@ResponseBody | ||
@GetMapping(path = "strList") | ||
public String strList(@RequestParam(name = "size", defaultValue = "128", required = false) Integer size) { | ||
List<UserInfo> list = allUsers(size); | ||
return JSON.toJSONString(list); | ||
} | ||
|
||
|
||
/** | ||
* 模拟返回文件的case | ||
* | ||
* @param size | ||
* @return | ||
*/ | ||
@ResponseBody | ||
@GetMapping(path = "file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) | ||
public byte[] fileResp(int size) { | ||
List<UserInfo> list = allUsers(size); | ||
String ans = JSON.toJSONString(list); | ||
return ans.getBytes(); | ||
} | ||
|
||
|
||
@Data | ||
@JsonFormat(with = {JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES}) | ||
public static class TT { | ||
String pOrder; | ||
String SOrderNum; | ||
Long c2Num; | ||
} | ||
|
||
public static void main(String[] args) throws JsonProcessingException { | ||
ObjectMapper mapper = new ObjectMapper(); | ||
TT t = new TT(); | ||
t.setPOrder("1230981217834143"); | ||
t.setSOrderNum("1230981212334143"); | ||
t.setC2Num(12L); | ||
String str = mapper.writeValueAsString(t); | ||
TT t2 = mapper.readValue(str, TT.class); | ||
System.out.println(t2); | ||
|
||
|
||
|
||
// 下面这个主要用于解决gzip解压 | ||
// RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create().build())); | ||
RestTemplate restTemplate = new RestTemplate(); | ||
HttpEntity<String> ans = restTemplate.getForEntity("http://localhost:8080/strList?size=1", String.class); | ||
System.out.println(ans); | ||
|
||
HttpEntity<ArrayList> ans2 = restTemplate.getForEntity("http://localhost:8080/list?size=1", ArrayList.class); | ||
System.out.println(ans2); | ||
|
||
HttpEntity<ArrayList> a3 = restTemplate.getForEntity("http://localhost:8080/static/txt.json", ArrayList.class); | ||
System.out.println(a3); | ||
|
||
|
||
// 以下时解决京东开放接口的数据,返回中文乱码情况 | ||
|
||
HttpHeaders DEFAULT_HEAD = new HttpHeaders(); | ||
// DEFAULT_HEAD.set("Accept", "*/*"); | ||
// 支持gzip压缩传输 | ||
// gzip 压缩,默认的RestTemplate不支持,需要借助 HttpComponentsClientHttpRequestFactory | ||
// deflate zlib格式压缩, Brotli压缩 默认的RestTemplate都可以 | ||
DEFAULT_HEAD.set("Accept-Encoding", "deflate,br,gzip"); | ||
DEFAULT_HEAD.setAcceptCharset(Arrays.asList(StandardCharsets.UTF_8)); | ||
DEFAULT_HEAD.setContentType(MediaType.APPLICATION_FORM_URLENCODED); | ||
//封装请求头 | ||
HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<MultiValueMap<String, Object>>(DEFAULT_HEAD); | ||
|
||
HttpEntity<String> ans3 = restTemplate.exchange("https://api-iop.jd.com/oauth2/accessToken", HttpMethod.POST, formEntity, String.class); | ||
System.out.println(ans3); | ||
|
||
DEFAULT_HEAD.set("Accept", "*/*"); | ||
formEntity = new HttpEntity<>(DEFAULT_HEAD); | ||
HttpEntity<String> ans4 = restTemplate.exchange("https://api-iop.jd.com/oauth2/accessToken", HttpMethod.POST, formEntity, String.class); | ||
System.out.println(ans4); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
spring-boot/207-web-res-gzip/src/main/resources/application.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
server: | ||
compression: | ||
enabled: true # 开启支持gzip压缩 | ||
min-response-size: 128 # 当响应长度超过128时,才执行压缩 |
Oops, something went wrong.