Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node.js大文件的下载以及断点续传 #5

Open
EchoFUN opened this issue Oct 28, 2013 · 0 comments
Open

node.js大文件的下载以及断点续传 #5

EchoFUN opened this issue Oct 28, 2013 · 0 comments

Comments

@EchoFUN
Copy link
Owner

EchoFUN commented Oct 28, 2013

断点续传其实不是什么新鲜的功能了。
简单的做个扫盲,其基本原理就是,在文件的下载断开以后。客户端继续向服务器端请求的时候,http请求的头文件中会多了一个参数“Range”,来标示当前下载的文件所断开的位置。
用如下命令可以做个测试
wget -c -d –limit-rate=2048k -O target “http://127.0.0.1:8000″(ps. c表示让wget进行断点续传,如果不加c发送请求的头文件中是不会,d标示打印调试信息)
截图
接下来的下载,就是从这个断开的位置开始,服务器继续向客户端传输文件剩余的内容了。

主要的是关注两个地方,一个就是返回头文件的设置,和文件内容的读取。
新建一个叫Transfer的类,把请求的两个参数当成构造函数传进去。

function Transfer(req, resp) {
    this.req = req;
    this.resp = resp;
}

计算文件开始的位置和设置头文件的信息如下

/**
 * @description 计算上次的断点信息
 * @param {string} Range 请求http头文件中的断点信息,如果没有则为undefined,格式(range: bytes=232323-)
 * @return {integer} startPos 开始的下载点
 */
Transfer.prototype._calStartPosition = function(Range) {
    var startPos = 0;
    if( typeof Range != 'undefined') {
        var startPosMatch = /^bytes=([0-9]+)-$/.exec(Range);
        startPos = Number(startPosMatch[1]);
    }
    return startPos;
}
/**
 * @description 配置头文件
 * @param {object} Config 头文件配置信息(包含了下载的起始位置和文件的大小)
 */
Transfer.prototype._configHeader = function(Config) {
    var startPos = Config.startPos, 
        fileSize = Config.fileSize,
        resp = this.resp;
    // 如果startPos为0,表示文件从0开始下载的,否则则表示是断点下载的。
    if(startPos == 0) {
        resp.setHeader('Accept-Range', 'bytes');
    } else {
        resp.setHeader('Content-Range', 'bytes ' + startPos + '-' + (fileSize - 1) + '/' + fileSize);
    }
    resp.writeHead(206, 'Partial Content', {
        'Content-Type' : 'application/octet-stream',
    });
}

在nodejs中每一次http请求的头文件,已经被他封装在了http.ServerRequest的headers对象中,用node inspector的方式进行调试,就可以很清楚的看到了http.ServerRequest和http.ServerResponse对象的结构:
截图
剩下来要做的,就是从断开的位置继续读取文件,并将其返回给客户端,可以用nodejs提供的ReadStream来实现:

/**
 * @description 初始化配置信息
 * @param {string} filePath
 * @param {function} down 下载开始的回调函数
 */
Transfer.prototype._init = function(filePath, down) {
    var config = {};
    var self = this;
    fs.stat(filePath, function(error, state) {
        if(error)
            throw error;

        config.fileSize = state.size;
        var range = self.req.headers.range;
        config.startPos = self._calStartPosition(range);
        self.config = config;
        self._configHeader(config);
        down();
    });
}
/**
 * @description 生成大文件文档流,并发送
 * @param {string} filePath 文件地址
 */
Transfer.prototype.Download = function(filePath) {
    var self = this;
    path.exists(filePath, function(exist) {
        if(exist) {
            self._init(filePath, function() {
                var config = self.config
                    resp = self.resp;
                fReadStream = fs.createReadStream(filePath, {
                    encoding : 'binary',
                    bufferSize : 1024 * 1024,
                    start : config.startPos,
                    end : config.fileSize
                });
                fReadStream.on('data', function(chunk) {
                    resp.write(chunk, 'binary');
                });
                fReadStream.on('end', function() {
                    resp.end();
                });
            });
        } else {
            console.log('文件不存在!');
            return;
        }
    });
}

最终的可以用如下代码进行测试

var fs = require('fs')
http = require('http')
path = require('path');
var server = http.createServer(function(req, resp) {
    var transfer = new Transfer(req, resp);
    var filePath = '/Users/xukai/Downloads/love.rmvb';
    transfer.Download(filePath);
});
server.listen('8000');

转载请注明出处
botobe.net
本文Github地址

按照惯例
2012.3.10,情况一般。
Merci !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant