1. 业务场景
业务前提是公司的项目有一个【广告】保存接口,现要把 ADX 将投放的广告数据存储在我们系统,方便后续的使用。
传输JSON数据结构示例:
{
"adName":"",
"adTag":""
}
所以传输JSON数据这么大的情况下,接口不优化的情况下,就带来了这几个弊端:
2. 方案设计
试想一下有什么办法可以解决这样的问题?
我们在请求方发起请求前先对数据进行压缩,然后再发起请求到我们系统的保存接口数组转json数组转json,当然在我们的接收接口后再保存到系统之前对请求数据进行解压,还原成 JSON 对象就完成了整个 压缩数据的请求以及处理流程。
实际上这种解决方案也是有弊端的,例如:
当然这些问题也不要担心,具体还是需要根据你们公司具体业务场景。我司业务场景为了解决这问题做了一点调整:
3. 方案实现前置知识:方案流程示例图:
源代码:
搭建一个SpringBoot项目(很简单,这里就不多聊了)。
Controller层编写一个保存广告数据的接口,参数只需要接收 JSON 对象即可。
/**
* @ClassName: AdvertisementController
* @Author hf
* @Date 2022/3/29 20:41
*/
@Slf4j
@RestController
public class AdvertisementController {
@PostMapping("/saveAdvertisement")
public Advertising saveAdvertisement(@RequestBody Advertisement params) {
log.info("接收到了请求内容:"+ params");
return advertising;
}
}
Dao实体
/**
* @ClassName: Advertisement
* @Author hf
* @Date 2022/3/29 20:42
*/
@Data
public class Advertisement {
private String adName;
private String adTag;
}
自定义拦截器
/**
* @ClassName: MyGZIPFilter
* @Author hf
* @Date 2022/3/29 21:00
*/
@Component
@Slf4j
public class MyGZIPFilter implements Filter {
private static final String CONTENT_ENCODING = "Content-Encoding";
private static final String CONTENT_ENCODING_TYPE = "gzip";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("init MyGZIPFilter");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
StopWatch stopWatch = new StopWatch();//用来计算耗时,前面有章多种代码耗时统计总结,可以去翻一下
stopWatch.start();//开始计时
HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
String encodeType = httpServletRequest.getHeader(CONTENT_ENCODING);
if (CONTENT_ENCODING_TYPE.equals(encodeType)) {
log.info("请求:{} 需要解压", httpServletRequest.getRequestURI());
UnZIPRequestWrapper unZIPRequestWrapper = new UnZIPRequestWrapper(httpServletRequest);
filterChain.doFilter(unZIPRequestWrapper,servletResponse);
}else {
log.info("请求:{} 无需解压", httpServletRequest.getRequestURI());
filterChain.doFilter(servletRequest,servletResponse);
}
stopWatch.stop();//结束计时
log.info("耗时:{}ms", stopWatch.getTotalTimeMillis());
}
@Override
public void destroy() {
log.info("destroy GZIPFilter");
}
}
注册自定义拦截器
/**
* @ClassName: MyFilterRegistration
* @Author hf
* @Date 2022/3/29 21:20
*/
@Configuration
public class MyFilterRegistration {
@Resource
private MyGZIPFilter myGZIPFilter;
@Bean
public FilterRegistrationBean gzipFilterRegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(gzipFilter);//Filter可以new,也可以使用依赖注入Bean
registration.setName("myGZIPFilter");//自定义过滤器名称
registration.addUrlPatterns("/*");//拦截路径,我这里设置的所有
registration.setOrder(1);//设置顺序
return registration;
}
}
RequestWrapper 实现请求数据解压,并将解压后的请求数据回写到 Body 中。
/**
* @ClassName: UnZIPRequestWrapper
* @Author hf
* @Date 2022/3/29 21:30
* @Description: JsonString请求数据压缩后保存为二进制文件 -> 解压缩后还原成JsonString转换成byte[] 回写body中
*/
@Slf4j
public class UnZIPRequestWrapper extends HttpServletRequestWrapper {
private final byte[] bytes;
public UnZIPRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
try (BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
final byte[] body;
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
body = baos.toByteArray();
if (body.length == 0) {
log.info("Body无内容,无需解压");
bytes = body;
return;
}
this.bytes = GZIPUtils.uncompressToByteArray(body);
} catch (IOException ex) {
log.info("GZIP解压缩异常!");
ex.printStackTrace();
throw ex;
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
最后就是编写我们的GZIP工具类了。
/**
* @ClassName: MyGZIPUtils
* @Author hf
* @Date 2022/3/29 21:45
*/
public class MyGZIPUtils {
public static final String GZIP_ENCODE_UTF_8 = "UTF-8";
/**
* GZIP解压缩
* @param bytes
* @return
*/
public static byte[] uncompress(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
try {
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
/**
* GZIP解压并返回String
* @param bytes
* @return
*/
public static String uncompressToString(byte[] bytes) throws IOException {
return uncompressToString(bytes, GZIP_ENCODE_UTF_8);
}
/**
* GZIP解压并返回字节数组
* @param bytes
* @return
*/
public static byte[] uncompressToByteArray(byte[] bytes) throws IOException {
return uncompressToByteArray(bytes, GZIP_ENCODE_UTF_8);
}
/**
* GZIP解压成字符串并返回字符串
* @param bytes 压缩后的字节数组
* @param encoding 编码方式
* @return 解压后的字符串
*/
public static String uncompressToString(byte[] bytes, String encoding) throws IOException {
byte[] result = uncompressToByteArray(bytes, encoding);
return new String(result);
}
/**
* GZIP解压成字节数组
* @param bytes
* @param encoding
* @return
*/
public static byte[] uncompressToByteArray(byte[] bytes, String encoding) throws IOException {
if (bytes == null || bytes.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
try {
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
throw new IOException("GZIP解压失败!");
}
}
/**
* 将字节流转换成文件
* @param filename
* @param data
* @throws Exception
*/
public static void saveFile(String filename,byte [] data)throws Exception{
if(data != null){
String filepath ="/" + filename;
File file = new File(filepath);
if(file.exists()){
file.delete();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(data,0,data.length);
fos.flush();
fos.close();
System.out.println(file);
}
}
/**
* 把字符串压缩为GZIP字节数组
* @param str
* @return
*/
public static byte[] compress(String str) {
return compress(str, GZIP_ENCODE_UTF_8);
}
/**
* 把字符串压缩为GZIP字节数组
* @param str
* @param encoding
* @return
*/
public static byte[] compress(String str, String encoding) {
if (str == null || str.length() == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip;
try {
gzip = new GZIPOutputStream(out);
gzip.write(str.getBytes(encoding));
gzip.close();
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
4. 结果测试
一定要注意这个坑:
千万千万不要直接把压缩后的 byte[] 用来传输,不信你试试会发现这样做比没压缩的强请求包还要大得多!
压缩后的 byte[]两种传输法:
接下来用 POSTMAN 测试一下 GZIP 压缩数据请求效果:
大家可以收藏一下,后续会持续更新更多的干货知识。
限时特惠:本站每日持续更新海量设计资源,一年会员只需29.9元,全站资源免费下载
站长微信:ziyuanshu688
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。