nodejs 手动实现 multipart/byteranges 分块下载

nodejs 实现 multipart/byteranges 分块下载

引言

最近接触到了文件下载,一般默认返回 Content-Type: application/octet-stream 文件流,支持单范围请求,而多范围请求需要自我手动实现。

一、HTTP 范围请求基础

1.1 Range 请求头格式

客户端通过Range请求头指定需要获取的字节范围,格式如下:

http

sql 复制代码
Range: bytes=start-end, start-end
  • 单范围:bytes=0-1023(获取前 1024 字节)

  • 多范围:bytes=0-500, 1000-2000(获取两个不连续范围)

  • 后缀范围:bytes=-500(获取最后 500 字节)

1.2 服务器响应格式

  • 单范围响应 :返回206 Partial Content状态码,包含Content-Range

  • 多范围响应 :返回multipart/byteranges类型,使用 boundary 分隔各块

二、Node.js实现方案

2.1核心实现代码

javascript

解析rang参数,获取分块开始位置和结束位置
javascript 复制代码
const { ctx } = this;
const fileSize, rangeHeader, filePath;

const ranges = [];
let match;
if (rangeHeader.startsWith('bytes=')) {
    match = rangeHeader.replace('bytes=', '').split(',').map(vl => (vl && vl.trim()));
    for (let i = 0; i < match.length; i++) {
        let val = match[i];
        if (val) {
            const [startStr, endStr] = val.split('-');
            let start = Number(startStr);
            let end = endStr ? Number(endStr) : fileSize - 1;
            start = isNaN(start) ? 0 : Math.max(0, start);
            end = isNaN(end) ? fileSize - 1 : Math.min(end, fileSize - 1);
            if (start <= end) {
                ranges.push({ start, end });
            }
        }
    }
}
// 解析不成功,返回失败
if (ranges.length === 0) {
    ctx.status = 416;
    ctx.set('Content-Range', `bytes */${fileSize}`);
    ctx.body = 'Range Not Satisfiable';
    return;
} else if (ranges.length === 1) {
// 只有一块,按单范围返回
    const { start, end } = ranges[0];
    ctx.set('Content-Length', end - start + 1);
    ctx.body = createReadStream(filePath, { start, end });
    return;
}
设置响应头,分块获取文件后拼接响应体
javascript 复制代码
const stream = ctx.res
const boundary = Math.floor(Math.random() * 100000000)
stream.writeHead(206, {
    'Content-Length': contentLength,
    'Content-Type': `multipart/byteranges; boundary=${boundary}`,
    'Accept-Ranges': 'bytes'
})
for (let i = 0; i < ranges.length; i++) {
    const { start, end } = ranges[i];
    stream.write(`--${boundary}\r\n`);
    stream.write(`Content-Type: application/octet-stream\r\n`);
    stream.write(`Content-Range: bytes ${start}-${end}/${fileSize}\r\n\r\n`);
    await new Promise((resolve, reject) => {
        const readStream = createReadStream(filePath, { start, end });
        readStream.on('data', (chunk) => {
            stream.write(chunk);
        })
        readStream.on('close', resolve);
        readStream.on('error', reject);
    });
    stream.write('\r\n');
}
stream.write(`--${boundary}--\r\n`);
stream.end();
相关推荐
码林鼠25 分钟前
webpack的基本配置
前端·webpack·node.js
FeelTouch Labs4 小时前
Node.js 中的 spawn 和 exec:子进程执行的不同方式对比
node.js·编辑器·vue·vim
烂蜻蜓6 小时前
Node.js入门教程(一):初识Node.js——服务端JavaScript的诞生与特点
开发语言·javascript·node.js
凌云拓界9 小时前
NodeVerdict:Node.js 原生诊断数据可视化工具
信息可视化·架构·typescript·开源·node.js·github·bug
用户29307509766911 小时前
React Router v6 实战:理解 SPA 路由系统
node.js
星蓝_starblue1 天前
零服务器、零数据库!开源growth-board,利用GitHub自动管理刷题/学习/求职全流程
服务器·数据库·程序人生·系统架构·node.js·github·改行学it
梦想CAD控件1 天前
网页端CAD的图形选择、编辑与夹点操作教程
前端·javascript·node.js
万敏1 天前
Vue3 全栈实战第四周:watch、nextTick、性能优化与 keep-alive 实战记录
vue.js·node.js·全栈
濮水大叔2 天前
为什么 AI 最擅长 React/Next.js,却很少看到真正好用的 Next.js 开源项目?
react.js·node.js·next.js
AI行业学习2 天前
Claude Code + cc-switch + Git + Node.js 全套下载+安装+配置完整版
开发语言·git·python·前端框架·node.js·html·notepad++