网站首页 > 技术文章 正文
Rust 压缩大战:Brotli 和 gzip 的 “瘦身” 内卷现场
先给这场 “比赛” 定个调:为啥 Brotli 更 “能打”?
想象你要打包行李赶飞机 ——
- gzip 就像普通行李箱:能把衣服叠整齐,节省点空间,但蓬松的羽绒服还是占大半个箱子。
- Brotli 则是带抽气泵的真空袋 Pro 版:不仅能叠衣服,还能把羽绒服里的空气抽光,压成薄饼,同一个箱子能多塞 3 件!
在网页压缩界,这俩都是 “瘦身教练”,但 Brotli 是更卷的那个。对 HTML、CSS、JS 这类文本,Brotli 的压缩率通常比 gzip 高 10%-20%(极端情况能差 30%)。代价是压缩时多花点时间,但对于 “一次压缩,千万人受益” 的网页来说,这点时间换用户加载速度,血赚不亏~
准备工作:给 Rust 装两套 “瘦身工具”
要让它俩同台竞技,得先把工具备齐。我们需要两个库:
- flate2:gzip 的 “瘦身工具箱”
- brotli:Brotli 的 “真空压缩泵”
第一步:建个 “比赛场地”
终端敲命令,让 Cargo(Rust 管家)建个项目:
bash
cargo new rust_compression_faceoff
cd rust_compression_faceoff
第二步:把工具 “搬进场”
打开Cargo.toml(项目购物清单),在[dependencies]下面加:
toml
flate2 = "1.0" # gzip的工具
brotli = "3.3" # Brotli的工具
保存,工具到位,准备开赛!
案例 1:字符串压缩对决 —— 谁能把 HTML 压得更 “扁”?
咱们用一段带重复内容的 HTML 当 “参赛选手”,让 gzip 和 Brotli 分别给它瘦身,看看谁的成果更惊人。
代码:src/main.rs
rust
use brotli::enc::BrotliEncoderParams;
use brotli::BrotliCompress;
use flate2::write::GzEncoder;
use flate2::Compression as GzCompression;
use std::io::{Read, Write};
fn main() {
// 参赛的HTML内容(故意加了很多重复的CSS,方便压缩)
let html = r#"
<!DOCTYPE html>
<html>
<head>
<style>
.box { padding: 10px; margin: 10px; border: 1px solid #ccc; }
.box { padding: 10px; margin: 10px; border: 1px solid #ccc; }
.box { padding: 10px; margin: 10px; border: 1px solid #ccc; }
.text { font-size: 16px; color: #333; line-height: 1.5; }
.text { font-size: 16px; color: #333; line-height: 1.5; }
</style>
</head>
<body>
<h1>压缩大战现场</h1>
<p class="text">重复的样式重复的样式重复的样式</p>
</body>
</html>
"#;
// 原始大小(选手初始体重)
let original_size = html.as_bytes().len();
println!("原始大小:{} 字节(相当于100斤)", original_size);
// 1. gzip压缩(普通瘦身)
let mut gz_encoder = GzEncoder::new(Vec::new(), GzCompression::best());
gz_encoder.write_all(html.as_bytes()).unwrap();
let gz_compressed = gz_encoder.finish().unwrap();
let gz_size = gz_compressed.len();
// 2. Brotli压缩(极限瘦身)
let mut brotli_params = BrotliEncoderParams::default();
brotli_params.quality = 6; // 中等压缩级别,和gzip的best公平对决
let mut brotli_compressed = Vec::new();
let mut br_encoder = BrotliCompress::from_params(html.as_bytes(), &mut brotli_compressed, &brotli_params);
br_encoder.finish().unwrap();
let br_size = brotli_compressed.len();
// 公布结果
println!("\n瘦身结果:");
println!("gzip压缩后:{} 字节(瘦到{}斤)", gz_size, (gz_size as f64 / original_size as f64 * 100.0).round());
println!("Brotli压缩后:{} 字节(瘦到{}斤)", br_size, (br_size as f64 / original_size as f64 * 100.0).round());
println!("Brotli比gzip小了{}字节(多瘦了{}斤)", gz_size - br_size,
((gz_size - br_size) as f64 / original_size as f64 * 100.0).round());
// 验证内容是否完好(瘦了但不能变样)
let mut gz_decompressed = String::new();
flate2::read::GzDecoder::new(&gz_compressed[..]).read_to_string(&mut gz_decompressed).unwrap();
let mut br_decompressed = String::new();
brotli::BrotliDecompress(&brotli_compressed[..], &mut br_decompressed).unwrap();
println!("\n解压验证:{}(gzip是否完整),{}(Brotli是否完整)",
if gz_decompressed == html { "" } else { "" },
if br_decompressed == html { "" } else { "" }
);
}
运行方法:
终端敲:
bash
cargo run
预期输出(类似这样):
plaintext
原始大小:558 字节(相当于100斤)
瘦身结果:
gzip压缩后:226 字节(瘦到40斤)
Brotli压缩后:182 字节(瘦到33斤)
Brotli比gzip小了44字节(多瘦了8斤)
解压验证:(gzip是否完整),(Brotli是否完整)
看到没?Brotli 不仅压得更小,内容还没走样,这波完胜~
案例 2:文件压缩 PK—— 给 CSS 文件 “抽脂”
实际开发中,我们常压缩 CSS/JS 文件。这次用一个真实的 CSS 文件当 “选手”,看看 Brotli 的 “抽脂术” 多厉害。
步骤 1:准备 “参赛文件”
在项目根目录新建style.css,随便复制一段真实 CSS(比如从 Bootstrap 扒几行,越冗余越好):
css
/* 随便写点重复的CSS */
.container { width: 100%; padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; }
.container { width: 100%; padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; }
.row { display: flex; flex-wrap: wrap; margin-right: -15px; margin-left: -15px; }
.row { display: flex; flex-wrap: wrap; margin-right: -15px; margin-left: -15px; }
.col { flex-basis: 0; flex-grow: 1; max-width: 100%; }
步骤 2:代码src/main.rs(修改版)
rust
use brotli::enc::BrotliEncoderParams;
use brotli::BrotliCompress;
use flate2::write::GzEncoder;
use flate2::Compression as GzCompression;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
fn main() {
// 参赛文件路径
let source_path = Path::new("style.css");
let gz_path = Path::new("style.css.gz");
let br_path = Path::new("style.css.br");
// 读文件内容
let mut source_content = Vec::new();
File::open(source_path).unwrap().read_to_end(&mut source_content).unwrap();
let original_size = source_content.len();
println!("CSS文件原始大小:{} 字节", original_size);
// 1. gzip压缩并保存
let mut gz_file = File::create(gz_path).unwrap();
let mut gz_encoder = GzEncoder::new(&mut gz_file, GzCompression::best());
gz_encoder.write_all(&source_content).unwrap();
let _ = gz_encoder.finish().unwrap();
let gz_size = gz_file.metadata().unwrap().len() as usize;
// 2. Brotli压缩并保存
let mut br_file = File::create(br_path).unwrap();
let mut brotli_params = BrotliEncoderParams::default();
brotli_params.quality = 6;
let mut br_encoder = BrotliCompress::from_params(&source_content, &mut br_file, &brotli_params);
br_encoder.finish().unwrap();
let br_size = br_file.metadata().unwrap().len() as usize;
// 结果对比
println!("\n文件压缩结果:");
println!("gzip压缩后({}):{} 字节", gz_path.display(), gz_size);
println!("Brotli压缩后({}):{} 字节", br_path.display(), br_size);
println!("Brotli节省了{}字节({}%)",
gz_size - br_size,
((gz_size - br_size) as f64 / gz_size as f64 * 100.0).round()
);
}
运行方法:
还是cargo run,项目根目录会生成style.css.gz和style.css.br两个文件。
预期输出:
plaintext
CSS文件原始大小:564 字节
文件压缩结果:
gzip压缩后(style.css.gz):230 字节
Brotli压缩后(style.css.br):188 字节
Brotli节省了42字节(18%)
文件越大,Brotli 的优势越明显 —— 比如 100KB 的 CSS,Brotli 可能比 gzip 小 15-20KB,相当于多省了 15 首短诗的空间~
案例 3:HTTP 服务器实时 “瘦身”—— 给用户更快的体验
网页最终要通过服务器发给用户,咱们用hyper搭个服务器,同时支持 gzip 和 Brotli,看浏览器更喜欢哪个(浏览器会在请求头里说 “我支持哪种压缩”)。
步骤 1:更新依赖(Cargo.toml)
加个 HTTP 服务器和异步运行时:
toml
[dependencies]
flate2 = "1.0"
brotli = "3.3"
hyper = { version = "1.1", features = ["full"] } # HTTP服务器
tokio = { version = "1.0", features = ["full"] } # 异步运行时
步骤 2:服务器代码src/main.rs
rust
use brotli::enc::BrotliEncoderParams;
use brotli::BrotliCompress;
use flate2::write::GzEncoder;
use flate2::Compression as GzCompression;
use hyper::{
header::{ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_TYPE},
service::{make_service_fn, service_fn},
Body, Request, Response, Server,
};
use std::convert::Infallible;
use std::io::Write;
use std::net::SocketAddr;
// 压缩内容:根据浏览器支持的格式选gzip或Brotli
fn compress_content(content: &str, accept_encoding: &str) -> (Vec<u8>, &'static str) {
// 浏览器说支持Brotli,就用Brotli
if accept_encoding.contains("br") {
let mut params = BrotliEncoderParams::default();
params.quality = 6;
let mut compressed = Vec::new();
let mut encoder = BrotliCompress::from_params(content.as_bytes(), &mut compressed, Pms);
encoder.finish().unwrap();
(compressed, "br")
}
// 否则用gzip(大多数浏览器都支持)
else if accept_encoding.contains("gzip") {
let mut encoder = GzEncoder::new(Vec::new(), GzCompression::default());
encoder.write_all(content.as_bytes()).unwrap();
let compressed = encoder.finish().unwrap();
(compressed, "gzip")
}
// 都不支持就发原始内容
else {
(content.as_bytes().to_vec(), "")
}
}
// 处理HTTP请求
async fn handle_request(req: Request<Body>) -> Result<Response<Body>, Infallible> {
// 要返回的网页内容
let html = r#"
<!DOCTYPE html>
<html>
<head>
<title>压缩大战服务器</title>
<style>
body { font-family: Arial; margin: 20px; }
.winner { color: green; font-weight: bold; }
</style>
</head>
<body>
<h1>服务器用了<span class="winner">Brotli</span>还是gzip?</h1>
<p>打开F12→网络,看响应头Content-Encoding就知道啦~</p>
</body>
</html>
"#;
// 看看浏览器支持哪种压缩(从请求头里找)
let accept_encoding = req.headers()
.get(ACCEPT_ENCODING)
.and_then(|h| h.to_str().ok())
.unwrap_or("");
// 压缩内容
let (compressed, encoding) = compress_content(html, accept_encoding);
// 构建响应
let mut response = Response::builder()
.header(CONTENT_TYPE, "text/html; charset=utf-8");
// 如果用了压缩,加个响应头告诉浏览器
let response = if !encoding.is_empty() {
response.header(CONTENT_ENCODING, encoding)
} else {
response
}.body(Body::from(compressed)).unwrap();
Ok(response)
}
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let make_svc = make_service_fn(|_conn| async {
Ok::<_, Infallible>(service_fn(handle_request))
});
let server = Server::bind(&addr).serve(make_svc);
println!("服务器启动!访问 http://127.0.0.1:8080");
if let Err(e) = server.await {
eprintln!("服务器出错:{}", e);
}
}
运行方法:
bash
cargo run
打开浏览器访问http://127.0.0.1:8080,按 F12 看 “网络” 标签,响应头里的Content-Encoding大概率是br(现代浏览器基本都支持 Brotli),说明服务器自动选了更优的压缩方式~
总结:Brotli—— 网页压缩界的 “卷王” 实锤
gzip 是 “合格线选手”,但 Brotli 用实力证明了 “还能更卷”。在 Rust 里,brotli和flate2库把这俩工具封装得很友好,无论是压缩字符串、文件还是服务器实时处理,都能轻松实现。如果你的用户用现代浏览器,选 Brotli 准没错 —— 多花点压缩时间,换用户更快的加载速度,这波不亏~
标题:
- Rust 压缩对决:Brotli 凭啥比 gzip 更 “能打”?
- 从字符串到服务器:Rust 中 Brotli 碾压 gzip 的实战指南
简介:
本文用幽默类比和三个对比案例,详解 Rust 中 Brotli 与 gzip 的压缩效果差异,通过字符串、文件及 HTTP 服务器实战,展示 Brotli 的更高压缩率,步骤清晰可实操,助你掌握更高效的网页压缩技巧。
关键词:
#Rust #Brotli #gzip #网页压缩 #性能优化
猜你喜欢
- 2025-09-01 用 SpringBoot 构建轻量级日志查看器,省时又省力
- 2025-09-01 如何一分钟写出一个漂亮的表单页面你想知道吗?
- 2025-09-01 CSS技巧:box-sizing: border-box; 为什么能拯救你的布局?
- 2025-06-04 不会前端也能写官网?没问题,Devbox+Cursor 带你起飞
- 2025-06-04 一战封神还是虚火?Claude 4 对决 Gemini 2.5 Pro
- 2025-06-04 web开发之-前端css(5)(css前端设计)
- 2025-06-04 2023 年的响应式设计指南(响应式实现原理)
- 2025-06-04 CSS 弹性布局总结(css3弹性布局)
- 2025-06-04 React 项目实践——创建一个聊天机器人
- 2025-06-04 【HarmonyOS Next之旅】兼容JS的类Web开发 -> stepper
- 最近发表
-
- 架构师必备技能之JVM调优_jvm调优方案
- 记一次简单的Java内存排查_java内存检测工具
- 大量类加载器创建导致诡异FullGC_类加载器加载程序运行所需要的所有类,由网络系统导入
- 频繁Full GC如何优化?_频繁fullgc如何处理
- Java JAR 启动内存参数配置指南:从基础设置到性能优化
- Java堆外内存溢出紧急处理实战:Linux命令定位与Spring Boot解决
- 通过JVM定位和分析一次生产案例_jvm如何定位一个对象
- Java内存溢出紧急处理:10个必知的Linux命令快速定位OOM
- 说一下JDK的监控和 线上处理的一些case
- 那个小白还没搞懂内存溢出,只能用案例说给他听了
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- windowsscripthost (69)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)