HTTP 数据传输
HTTP 中定长、分块、压缩、范围请求等数据传输方式
问题
HTTP 协议中有哪些数据传输方式?各自的应用场景是什么?
解答
1. 定长传输
通过 Content-Length 指定响应体的字节长度。
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 12
Hello World!
服务端示例:
const http = require('http')
http.createServer((req, res) => {
const body = 'Hello World!'
// 设置 Content-Length
res.setHeader('Content-Length', Buffer.byteLength(body))
res.end(body)
}).listen(3000)
2. 分块传输
当响应体长度未知时,使用 Transfer-Encoding: chunked 分块发送。
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
7\r\n
Hello, \r\n
6\r\n
World!\r\n
0\r\n
\r\n
服务端示例:
const http = require('http')
http.createServer((req, res) => {
res.setHeader('Transfer-Encoding', 'chunked')
// 分块写入数据
res.write('Hello, ')
setTimeout(() => {
res.write('World!')
res.end() // 发送结束标记 0\r\n\r\n
}, 1000)
}).listen(3000)
3. 压缩传输
通过 Content-Encoding 指定压缩算法,减少传输体积。
常见压缩算法:
gzip- 最常用deflate- 较少使用br- Brotli,压缩率更高
const http = require('http')
const zlib = require('zlib')
http.createServer((req, res) => {
const acceptEncoding = req.headers['accept-encoding'] || ''
const body = 'Hello World!'.repeat(100)
if (acceptEncoding.includes('gzip')) {
res.setHeader('Content-Encoding', 'gzip')
res.end(zlib.gzipSync(body))
} else if (acceptEncoding.includes('br')) {
res.setHeader('Content-Encoding', 'br')
res.end(zlib.brotliCompressSync(body))
} else {
res.end(body)
}
}).listen(3000)
4. 范围请求
支持断点续传和分片下载,通过 Range 请求头指定范围。
# 请求
GET /video.mp4 HTTP/1.1
Range: bytes=0-1023
# 响应
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/10240
Content-Length: 1024
服务端示例:
const http = require('http')
const fs = require('fs')
const path = require('path')
http.createServer((req, res) => {
const filePath = './video.mp4'
const stat = fs.statSync(filePath)
const fileSize = stat.size
const range = req.headers.range
if (range) {
// 解析 Range: bytes=start-end
const [start, end] = range
.replace('bytes=', '')
.split('-')
.map((n, i) => n ? parseInt(n) : (i === 0 ? 0 : fileSize - 1))
res.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Content-Length': end - start + 1,
'Accept-Ranges': 'bytes'
})
fs.createReadStream(filePath, { start, end }).pipe(res)
} else {
res.writeHead(200, {
'Content-Length': fileSize,
'Accept-Ranges': 'bytes'
})
fs.createReadStream(filePath).pipe(res)
}
}).listen(3000)
5. 多部分传输
用于表单文件上传,Content-Type: multipart/form-data。
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
文件内容
------WebKitFormBoundary--
前端上传示例:
// 使用 FormData 上传文件
const form = new FormData()
form.append('file', fileInput.files[0])
form.append('name', 'test')
fetch('/upload', {
method: 'POST',
body: form // 浏览器自动设置 Content-Type 和 boundary
})
关键点
- 定长传输:
Content-Length指定确切字节数,适用于已知长度的响应 - 分块传输:
Transfer-Encoding: chunked,适用于动态生成、长度未知的内容 - 压缩传输:
Content-Encoding配合 gzip/br,显著减少传输体积 - 范围请求:
Range+ 206 状态码,支持断点续传和视频拖拽播放 - 多部分传输:
multipart/form-data,用于文件上传和混合数据提交
目录