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,用于文件上传和混合数据提交