245 lines
7.1 KiB
Markdown
245 lines
7.1 KiB
Markdown
|
|
# 大屏保存 Gzip 压缩 - 后端处理示例
|
||
|
|
|
||
|
|
## 前端实现说明
|
||
|
|
|
||
|
|
当前端保存数据超过 100KB 时,会自动启用 Gzip 压缩,请求格式如下:
|
||
|
|
|
||
|
|
### 压缩数据请求格式
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"compressed": true,
|
||
|
|
"content": "<base64 编码的 Gzip 压缩数据>"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 请求头
|
||
|
|
```
|
||
|
|
Content-Encoding: gzip
|
||
|
|
X-Data-Original-Size: 123456
|
||
|
|
X-Data-Compressed-Size: 34567
|
||
|
|
Authorization: <token>
|
||
|
|
```
|
||
|
|
|
||
|
|
## Java Spring Boot 后端处理示例
|
||
|
|
|
||
|
|
### Controller 层
|
||
|
|
```java
|
||
|
|
@PostMapping("/reportDashboard")
|
||
|
|
public Result saveDashboard(@RequestBody Map<String, Object> params,
|
||
|
|
@RequestHeader(value = "Content-Encoding", required = false) String encoding,
|
||
|
|
@RequestHeader(value = "X-Data-Original-Size", required = false) Integer originalSize,
|
||
|
|
@RequestHeader(value = "X-Data-Compressed-Size", required = false) Integer compressedSize) {
|
||
|
|
|
||
|
|
try {
|
||
|
|
DashboardData dashboardData;
|
||
|
|
|
||
|
|
// 判断是否为压缩数据
|
||
|
|
if ("gzip".equalsIgnoreCase(encoding) && params.containsKey("compressed")) {
|
||
|
|
Boolean isCompressed = (Boolean) params.get("compressed");
|
||
|
|
if (isCompressed != null && isCompressed) {
|
||
|
|
String compressedContent = (String) params.get("content");
|
||
|
|
// 解压数据
|
||
|
|
dashboardData = gzipDecompress(compressedContent);
|
||
|
|
|
||
|
|
log.info("接收压缩数据:原始 {} KB -> 压缩后 {} KB",
|
||
|
|
originalSize != null ? originalSize / 1024.0 : 0,
|
||
|
|
compressedSize != null ? compressedSize / 1024.0 : 0);
|
||
|
|
} else {
|
||
|
|
// 未压缩数据直接转换
|
||
|
|
dashboardData = convertToDashboard(params);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// 普通数据直接处理
|
||
|
|
dashboardData = convertToDashboard(params);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 保存业务逻辑
|
||
|
|
return dashboardService.save(dashboardData);
|
||
|
|
|
||
|
|
} catch (Exception e) {
|
||
|
|
log.error("保存大屏失败", e);
|
||
|
|
return Result.error("保存失败:" + e.getMessage());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Gzip 解压工具方法
|
||
|
|
*/
|
||
|
|
private DashboardData gzipDecompress(String base64Data) throws IOException {
|
||
|
|
// Base64 解码
|
||
|
|
byte[] compressed = Base64.getDecoder().decode(base64Data);
|
||
|
|
|
||
|
|
// Gzip 解压
|
||
|
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||
|
|
try (GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(compressed))) {
|
||
|
|
byte[] buffer = new byte[1024];
|
||
|
|
int len;
|
||
|
|
while ((len = in.read(buffer)) > 0) {
|
||
|
|
out.write(buffer, 0, len);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 转回字符串
|
||
|
|
String json = out.toString("UTF-8");
|
||
|
|
|
||
|
|
// 解析为对象
|
||
|
|
return JSON.parseObject(json, DashboardData.class);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 转换为 Dashboard 对象
|
||
|
|
*/
|
||
|
|
private DashboardData convertToDashboard(Map<String, Object> params) {
|
||
|
|
// 根据实际业务对象转换
|
||
|
|
DashboardData data = new DashboardData();
|
||
|
|
data.setReportCode((String) params.get("reportCode"));
|
||
|
|
// ... 其他字段转换
|
||
|
|
return data;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 实体类示例
|
||
|
|
```java
|
||
|
|
@Data
|
||
|
|
public class DashboardData {
|
||
|
|
private String reportCode;
|
||
|
|
private DashboardInfo dashboard;
|
||
|
|
private List<WidgetInfo> widgets;
|
||
|
|
|
||
|
|
@Data
|
||
|
|
public static class DashboardInfo {
|
||
|
|
private String title;
|
||
|
|
private String width;
|
||
|
|
private String height;
|
||
|
|
private String backgroundColor;
|
||
|
|
private String backgroundImage;
|
||
|
|
private Integer refreshSeconds;
|
||
|
|
}
|
||
|
|
|
||
|
|
@Data
|
||
|
|
public static class WidgetInfo {
|
||
|
|
private String type;
|
||
|
|
private WidgetValue value;
|
||
|
|
|
||
|
|
@Data
|
||
|
|
public static class WidgetValue {
|
||
|
|
private SetupInfo setup;
|
||
|
|
private PositionInfo position;
|
||
|
|
private DataInfo data;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Node.js 后端处理示例
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const zlib = require('zlib');
|
||
|
|
const util = require('util');
|
||
|
|
const gunzip = util.promisify(zlib.gunzip);
|
||
|
|
|
||
|
|
app.post('/reportDashboard', async (req, res) => {
|
||
|
|
try {
|
||
|
|
let dashboardData;
|
||
|
|
const encoding = req.headers['content-encoding'];
|
||
|
|
|
||
|
|
if (encoding === 'gzip' && req.body.compressed) {
|
||
|
|
// 解压数据
|
||
|
|
const compressedBuffer = Buffer.from(req.body.content, 'base64');
|
||
|
|
const uncompressedBuffer = await gunzip(compressedBuffer);
|
||
|
|
const jsonString = uncompressedBuffer.toString('utf8');
|
||
|
|
dashboardData = JSON.parse(jsonString);
|
||
|
|
|
||
|
|
console.log(`接收压缩数据:原始 ${req.headers['x-data-original-size'] / 1024} KB -> 压缩后 ${req.headers['x-data-compressed-size'] / 1024} KB`);
|
||
|
|
} else {
|
||
|
|
// 普通数据
|
||
|
|
dashboardData = req.body;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 保存业务逻辑
|
||
|
|
await saveDashboard(dashboardData);
|
||
|
|
|
||
|
|
res.json({ code: '200', message: '保存成功' });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('保存失败:', error);
|
||
|
|
res.status(500).json({ code: '500', message: '保存失败' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Python Flask 处理示例
|
||
|
|
|
||
|
|
```python
|
||
|
|
import gzip
|
||
|
|
import base64
|
||
|
|
import json
|
||
|
|
from flask import request, jsonify
|
||
|
|
|
||
|
|
@app.route('/reportDashboard', methods=['POST'])
|
||
|
|
def save_dashboard():
|
||
|
|
try:
|
||
|
|
data = request.get_json()
|
||
|
|
encoding = request.headers.get('Content-Encoding')
|
||
|
|
|
||
|
|
if encoding == 'gzip' and data.get('compressed'):
|
||
|
|
# 解压数据
|
||
|
|
compressed_data = base64.b64decode(data['content'])
|
||
|
|
decompressed_data = gzip.decompress(compressed_data)
|
||
|
|
dashboard_data = json.loads(decompressed_data)
|
||
|
|
|
||
|
|
original_size = request.headers.get('X-Data-Original-Size')
|
||
|
|
compressed_size = request.headers.get('X-Data-Compressed-Size')
|
||
|
|
print(f"接收压缩数据:原始 {int(original_size)/1024:.2f} KB -> 压缩后 {int(compressed_size)/1024:.2f} KB")
|
||
|
|
else:
|
||
|
|
dashboard_data = data
|
||
|
|
|
||
|
|
# 保存业务逻辑
|
||
|
|
result = save_to_database(dashboard_data)
|
||
|
|
|
||
|
|
return jsonify({'code': '200', 'message': '保存成功'})
|
||
|
|
except Exception as e:
|
||
|
|
print(f"保存失败:{e}")
|
||
|
|
return jsonify({'code': '500', 'message': '保存失败'}), 500
|
||
|
|
```
|
||
|
|
|
||
|
|
## Nginx 配置建议
|
||
|
|
|
||
|
|
如果使用了 Nginx 反向代理,需要调整请求体大小限制:
|
||
|
|
|
||
|
|
```nginx
|
||
|
|
server {
|
||
|
|
# ... 其他配置
|
||
|
|
|
||
|
|
# 允许最大请求体大小(根据实际需求调整)
|
||
|
|
client_max_body_size 50M;
|
||
|
|
|
||
|
|
# 超时时间设置
|
||
|
|
client_body_timeout 120s;
|
||
|
|
proxy_read_timeout 120s;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 测试验证
|
||
|
|
|
||
|
|
### 使用 curl 测试压缩数据
|
||
|
|
```bash
|
||
|
|
# 构造测试数据
|
||
|
|
DATA='{"compressed":true,"content":"H4sIAAAAAAAAA..."}'
|
||
|
|
|
||
|
|
# 发送请求
|
||
|
|
curl -X POST http://localhost:8080/reportDashboard \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-H "Content-Encoding: gzip" \
|
||
|
|
-H "X-Data-Original-Size: 123456" \
|
||
|
|
-H "X-Data-Compressed-Size: 34567" \
|
||
|
|
-d "$DATA"
|
||
|
|
```
|
||
|
|
|
||
|
|
## 注意事项
|
||
|
|
|
||
|
|
1. **字符编码**:确保前后端都使用 UTF-8 编码
|
||
|
|
2. **Base64 安全**:考虑使用 URL安全的 Base64 编码变体
|
||
|
|
3. **错误处理**:解压失败时返回明确的错误信息
|
||
|
|
4. **日志记录**:记录压缩/解压的大小信息便于排查问题
|
||
|
|
5. **性能监控**:监控解压耗时,避免性能瓶颈
|