Files
gr_bi_web/GZIP_COMPRESSION_GUIDE.md
2026-04-29 09:54:27 +08:00

7.1 KiB

大屏保存 Gzip 压缩 - 后端处理示例

前端实现说明

当前端保存数据超过 100KB 时,会自动启用 Gzip 压缩,请求格式如下:

压缩数据请求格式

{
  "compressed": true,
  "content": "<base64 编码的 Gzip 压缩数据>"
}

请求头

Content-Encoding: gzip
X-Data-Original-Size: 123456
X-Data-Compressed-Size: 34567
Authorization: <token>

Java Spring Boot 后端处理示例

Controller 层

@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;
}

实体类示例

@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 后端处理示例

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 处理示例

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 反向代理,需要调整请求体大小限制:

server {
    # ... 其他配置
    
    # 允许最大请求体大小(根据实际需求调整)
    client_max_body_size 50M;
    
    # 超时时间设置
    client_body_timeout 120s;
    proxy_read_timeout 120s;
}

测试验证

使用 curl 测试压缩数据

# 构造测试数据
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. 性能监控:监控解压耗时,避免性能瓶颈