杂食性程序猿:专注于 Android,web frontend,flutter,node.js 等大前端领域,也会玩玩 python 和 java 等后端技术 ...

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

断点续传其实不是什么新鲜的功能了。
简单的做个扫盲,其基本原理就是,在文件的下载断开以后。客户端继续向服务器端请求的时候,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 !

改版倒计时

RT.
撒花ing~

GitHub地址:
github.com/EchoFUN/melodycoder
这次的改版采用了prototype.js作为前端框架。后端采用了node.js的express。mongodb数据库。清一色的js系列啊!

敬请期待…

2012.09.03
一切安好。
Merci !

JS框架系列笔记 —— 开篇的话

发觉有些东西就得记下来,然后再回味回味,才能真正放到脑袋里面去。
不然今天翻翻明天说不定就忘了,哎,可能是我太笨了?

话说发现现在做web开发的,基本上已经把jQuery用滥了,如果不了解一些框架深层次的原理而滥用带来的一个很严重的负面效应,就是代码的结构混乱,如果是对性能要求很高的客户端,就会导致性能的低下。
以前做过一个富客户端的项目hibox.com,在右边栏的消息区部分需要我重构一遍,啃吱啃吱的敲了一个多星期的代码,总算搞定了后,兴致勃勃的打算跑到团队老大那边去看他试用,可是他发现在消息区的消息很多的时候(>=500)条,打开一条会话的速度非常慢(2~3秒),《高性能javascript》说,曾经有人研究过,web页面的响应时间如果超过500ms,给客户带来的体验就是非常糟糕的了。
看着老大一边摇头一遍说这可是safri啊。旁边的我已经快满脸黑线,后脑勺都差点滴汗了。
回头仔细的检查了一遍,发现这个项目在底层对dom的选择器上,用了很多jQuery不常用的选择器,对于开发人员来说是很方便,可是说道性能和运行的时间上来说那就像懒婆娘的裹脚布。

现行的一些开发框架,给开发人员带来了极大的便利,同时也相当于是一柄双刃剑。
框架中的一些用到的思想我们可以用来借鉴。框架毕竟是别人研究出来的一套东西,对于大型的前端项目,进行定制性开发的话,一些框架就会显得捉襟见肘了。

所以打算通过【js框架系列笔记】来研究研究一些现行框架中的一些值得借鉴的思想,不死磕一些具体的技术细节。
思想这东西,就像是一束普通的阳光似的,用的好了,还能折射出7种色彩,不然就啥也见不到。

2012.07.15,刚刚来到人人网,环境不错,加油加油。
一切安好。
Merci !

WalNUT重装上阵 —— 之2.0

就像我的一位朋友说过的,他只知道iPhone,从来没听说过什么iphone。他只知道iPod touch,从来没听说过iTouch或者itouch一样。
我把walnut的名字改为了WalNUT,也是为了区分Walnut,WALNUT,walNUT… 等等的各种牛鬼蛇神。

WalNUT的后台结构进行了彻底的改头换面,采用了NodeJS进行架构。
PHP版本的WalNUT可以参考这篇Walnut 1.0 beta2即时在线聊天系统
2.0版本的WalNUT,采用了javascript本身独有的特点,很优雅的实现了核心通讯的的comet部分。至于效率上而言,应该比PHP版本的高出了应该不止一个数量级。

WalNUT倾注了我不少的心血,打算继续做下去,仅仅作为一项业余的爱好吧。
再把数据库部分完善完善可以放到国内的一些app engine上进行测试了。
已将其放在了Github(https://github.com/EchoFUN/Walnut)上愿与大家一起分享和研究。

无题

我爱爸爸,我爱妈妈~
还有我的音乐… …
我相信,你们会一直陪着我的。无论是在我失意,悲伤,开心或者难过的时候。
2011.12.17,情况有点糟。
不过依旧Merci !

不知不觉,好像离开了很久很久的样子

网站停止解析了2个月多,总是在想着怎么继续,却一直没有付诸行动。
不知不觉,好像离开了很久很久的样子。我的这块小小的网络根据地又重新“开张”了。
这三个月来,能感受到自己每天都在进步 … …
想象中的自己,在以后的日子中,每每回头望望过去的日子,能做到鲁迅先生散文中说道的“每当回忆往事的时候,他不因虚度年华而悔恨,不因碌碌无为而羞耻” 就可以了。

离开了原来的那个“安乐窝”。再也吃不到无锡的阿姨做的好吃的了,有点遗憾。换了个新的环境,认识了很多新的朋友,还算开心。
工作中换了个自己并原本并相对而言不太熟悉的方向,具体的原因我也不知道为什么,可能是当时脑子一热,觉得原来搞的东西太没意思了。好在在同事的耐心指导和帮助下也算是hold住了。

不知道什么时候或是从哪边听到这么一句话,“学会知足,懂得感恩,感谢生活中的每个关心帮助你的人,因为没有人应该对你好”,铭记在心,不敢忘记。
翻翻日历2011.10.15,一切安好。
Merci !

重新开始

前段时间自己设计制作了一个wordpress的主题,思忖再三,发现自己果然没有设计的天分。于是悄悄收起,换了一套新的主题。很喜欢这样简约大方的风格。