NodeJS 使用 Range 请求实现下载功能

本文介绍了如何使用NodeJS的HTTP服务和Range请求实现下载功能,包括服务端和客户端的实现细节。服务端通过处理Range请求,兼容不同类型的请求头,客户端则模拟下载过程,支持开始、暂停和恢复。通过此功能,可以实现断点续传,提高用户体验。

在这里插入图片描述


阅读原文


前言

本篇使用 NodeJS 的 HTTP 服务创建客户端,使用 Range 请求实现下载功能,并通过本篇的 Demo 扩展在业务中实现断点续传等功能的思路。


服务端的实现

我们通过 http 模块创建服务器处理 Range 请求,在服务器代码中我们为了减少回调嵌套使用 async 函数,所以需要将异步的操作方法转换成 Promise,以往我们使用 utilpromisify 来一个一个转换异步方法,比较麻烦,我们这次使用第三方模块 mz 并直接引入转换好的替代模块。

使用 mz 之前需要先安装:

npm install mz

服务端代码如下:

// 文件:server.js
const http = require("http");
const path = require("path");
const url = require("url");

// 引入 mz 模块转换成 Promise 的 fs 模块
const fs = require("mz/fs");

// 请求处理函数
async function listener(req, res) {
   
   
    // 获取 range 请求头,格式为 Range:bytes=0-5
    let range = req.headers["range"];

    // 下载文件路径
    let p = path.resovle(__dirname, url.parse(url, true).pathname);

    // 存在 range 请求头将返回范围请求的数据
    if (range) {
   
   
        // 获取范围请求的开始和结束位置
        let [, start, end] = range.match(/(\d*)-(\d*)/);

        // 错误处理
        try {
   
   
            let statObj = await fs.stat
<think>我们正在讨论如何使用Node.js实现一个文件下载接口。根据用户引用,用户之前有一个基于Node.js的文件下载服务器,用于测试多线程断点续传的下载库。现在用户的需求是实现一个“文章下载接口”。我们可以理解为用户希望提供一个HTTP接口,当访问该接口时,可以下载一篇文章(可能是文本文件,也可能是其他格式)。 实现思路: 1. 创建一个Node.js HTTP服务器。 2. 设置路由,例如当用户访问`/download`时,触发下载。 3. 在下载接口中,设置HTTP响应头,告诉浏览器这是一个需要下载的文件(设置Content-Disposition)。 4. 将文章内容作为文件流发送给客户端。 假设我们要下载的文章是一个文本文件(例如article.txt),我们可以这样做: 步骤: 1. 引入所需模块:http, fs(文件系统)等。 2. 创建HTTP服务器。 3. 处理请求,当请求的URL是下载路径时,执行下载逻辑。 4. 使用fs读取文件(或者直接发送一段文本作为文件内容),设置响应头,然后发送内容。 另外,用户之前的引用中提到他需要测试多线程断点续传,所以我们的服务器还应该支持断点续传(Range请求)。但根据用户当前问题,先实现基本下载功能,再考虑断点续传。 基本下载示例代码(不支持断点续传): 我们将创建一个简单的服务器,当访问`/download`时,下载一个名为“article.txt”的文件。 注意:如果文章内容不是来自文件,而是动态生成的文本(比如一篇存储在数据库中的文章),我们可以直接发送字符串,但同样需要设置响应头。 以下是一个简单的实现: </think>### 如何使用Node.js实现文章下载接口 以下是实现文章下载接口的完整指南,基于Node.js原生HTTP模块和Express框架两种方式: #### 一、核心实现原理 1. **HTTP响应头设置**: - `Content-Type`: 设置为`text/plain`(纯文本)或`application/pdf`等格式 - `Content-Disposition`: 设为`attachment; filename="文章.txt"`触发浏览器下载 - 示例: ```javascript res.setHeader('Content-Type', 'text/plain'); res.setHeader('Content-Disposition', 'attachment; filename="article.txt"'); ``` 2. **数据流处理**: - 使用`fs.createReadStream()`读取文件 - 通过`.pipe(res)`高效传输数据 - 内存占用公式:$Memory_{used} \propto \frac{FileSize}{ChunkSize}$ #### 二、原生HTTP模块实现 ```javascript const http = require('http'); const fs = require('fs'); const server = http.createServer((req, res) => { if (req.url === '/download') { const filePath = './articles/sample.txt'; res.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename="重要文章.txt"' }); fs.createReadStream(filePath) .on('error', (err) => { res.writeHead(500); res.end('文件读取错误'); }) .pipe(res); } else { res.writeHead(404); res.end('接口不存在'); } }); server.listen(3000, () => { console.log('下载服务运行中: http://localhost:3000/download'); }); ``` #### 三、Express框架实现(推荐) ```javascript const express = require('express'); const app = express(); // 动态生成文章内容 app.get('/generate', (req, res) => { const content = `文章标题: ${req.query.title || '默认标题'}\n\n正文内容...`; res.set({ 'Content-Type': 'text/plain', 'Content-Disposition': `attachment; filename="${encodeURIComponent(req.query.title || 'article')}.txt"` }); res.send(content); }); // 下载静态文件 app.get('/download/:id', (req, res) => { const filePath = `./articles/${req.params.id}.txt`; res.download(filePath, err => { if (err) res.status(404).send('文件不存在'); }); }); app.listen(3000); ``` #### 四、关键优化技术 1. **断点续传支持**: ```javascript const range = req.headers.range; if (range) { const parts = range.replace(/bytes=/, "").split("-"); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1; // 处理部分内容请求(206状态码) } ``` 2. **安全防护**: - 文件名过滤:`filename.replace(/[^a-z0-9_.-]/gi, '_')` - 路径校验:防止`../../`目录遍历攻击 - 下载限流:使用`express-rate-limit`中间件 3. **性能优化**: - 使用Stream API处理大文件 - 添加Gzip压缩:`app.use(compression())` - 内存管理公式:$MaxMemory = FileSize \times ConcurrentRequests$ #### 五、部署流程 1. 安装Node.js环境[^2] 2. 配置PATH变量[^3] 3. 启动服务:`node server.js` 4. 测试接口: ```bash curl -OJ http://localhost:3000/download/123 ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值