ExpressJS 的 formidable progress event 修复

新版的ExpressJS使用了1.8 以上版本的Connect,提供了自己的BodyParser用来做form表单的解析。虽然从底层来看还是使用的formidable,但是自己做了一层封装。这层封装只提供了form.on('end')之后的回调函数,而无法提供在上传过程当中的form.on('progress')的调用。

看了一下代码,发现Connect的BodyParser的时候并没有提供process事件的支持。注释里面也有说明如果希望直接使用formidable直接处理request的话,可以使用delete express.bodyParser.parse['multipart/form-data'];先删除它的这层解析,再用formidable的方法直接处理请求。

作为hacker,总不能做这种勤快的事情,尤其不知道对其他的地方是否有影响。研究了一下代码,发现bodyParser在初始化的时候会先将options传递进去赋值给form,而form本身则是从formidable而来,如果console.dir(form.__proto__)则可以发现已经挂载了formidable的属性,因此解决方案也容易了。

Express在一开始配置server的时候允许指定bodyParser();我们可以在这个地方hack它,代码如下:

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  var bp = express.bodyParser({
       parse:function(req){
             this.on('progress',function(bytesReceived,bytesExpected){
                 console.log(bytesReceived,bytesExpected,'over')});
            this.__proto__.parse.apply(this,arguments);
  }
  });
  app.use(bp);
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

首先在初始化的时候指定将form的parse进行改写,在执行parse之前先给自己绑定上progress事件。然后再调用__proto__.parse apply给自己。这样就可以在不更改bodyParser的情况下给请求增加progress事件。

但是这个方法依旧有缺陷,首先是修改了原生的parse方法,这样做会导致什么结果目前不知;其次progress的处理无法自定义化,或者说如果要自定义化需要比较麻烦的配置。

一开始我以为这个是用来向前端做输出用的,但是后面发现前端的JS在处理XHR upload的时候 ProgressEvent并不是从Server获取数据,而是浏览器直接给与的,因此Connect将progress 去掉也不是不可以的,至少从前端来看没有太大的问题。

Q.E.D.