diff --git a/.gitignore b/.gitignore
index 11110c5..4f8c004 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,5 @@ test/assets/*
 node_modules
 coverage.html
 **.txt
-npm_debug.log
\ No newline at end of file
+npm_debug.log
+.npmignore
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
index ae28bac..4b52936 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,17 +1,17 @@
-lib-cov
-*.seed
-*.log
-*.csv
-*.dat
-*.out
-*.pid
-*.gz
-
-pids
-logs
-results
-
-node_modules
-**.txt
-npm_debug.log
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+
+node_modules
+**.txt
+npm_debug.log
 .git
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index df63076..bb88a5b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,4 @@
-language: node_js
-node_js:
-  - "0.10"
-  - "0.8"
+language: node_js
+node_js:
+  - "0.10"
+  - "4"
diff --git a/README.md b/README.md
index 5b22bd4..73f9489 100644
--- a/README.md
+++ b/README.md
@@ -1,518 +1,526 @@
-# 七牛 Node.js SDK
-
-该 SDK 适用于 Node.js 0.4.7 及其以上版本,基于 七牛云存储官方API 构建。 若您的服务端是一个基于 Node.js 编写的网络程序,使用此 SDK , 能让您以非常便捷地方式将数据安全地存储到七牛云存储上。 以便让您应用的终端用户进行高速上传和下载,同时也使得您的服务端更加轻盈。
-
-## 安装
-
-你可以从 npm 进行安装
-
-```shell
-npm install node-qiniu
-```
-
-也可以从 Github 进行下载安装
-
-```shell
-$ git clone git://github.com/qiniu/node-qiniu
-$ cd node-qiniu
-$ npm install .
-```
-
-## 测试
-
-七牛 Node.js SDK 使用 Mocha 进行单元测试。
-
-```shell
-$ npm install -g mocha
-$ make test
-```
-
-## 使用
-
-### 配置 `qiniu.config()`
-
-设置全局参数,包括必须的 AccessKey 和 SecretKey,还可以设置其他如 CallbackURL 等参数,将会顺延至所有空间。
-
-```js
-qiniu.config({
-  access_key: '------',
-  secret_key: '------'
-});
-```
-
-### Bucket
-
-获得空间对象并进行操作。
-
-```js
-var imagesBucket = qiniu.bucket('qiniu-sdk-test');
-// 也可以这样操作
-// var imagesBucket = new qiniu.Bucket('qiniu-sdk-test');
-```
-
-#### 上传文件
-
-**1. `Bucket.putFile()`**
-
-上传一个文件,参数为将要上传的 Key,文件地址(可以是绝对地址,也可以是相对地址),第三个为可选参数 options,即本次上传中所使用 PutToken 的特殊设置,第四个为可选参数回调(callback),若不传入回调函数,将由 putFile 函数所返回的 Promise 对象进行响应。
-
-```js
-// 普通上传
-imagesBucket.putFile('exampleKey', __dirname + '/assets/example.jpg', function(err, reply) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(reply);
-});
-// 特殊参数
-imagesBucket.putFile('exampleKey_1', __dirname + '/assets/example.jpg', {
-  // 为本次上传中所使用的 Token 进行设置,此处为设置上传者标识
-  endUser: 'foobar'
-}, function(err, reply) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(reply);
-});
-// 七牛 Node.js SDK 所提供的 Promise 对象遵循 Promise/A(+) 标准,使用 .then 方法进行响应
-imagesBucket.putFile('exampleKey_2', __dirname + '/assets/example.jpg')
-  .then(
-    function(reply) {
-      // 上传成功
-      console.dir(reply);
-    },
-    function(err) {
-      // 上传失败
-      console.error(err);
-    }
-  );
-```
-
-**2. `Bucket.createPutStream()`**
-
-七牛 Node.js SDK 提供基于流(Stream)的操作方式,为熟悉 Node.js 流式操作的开发者提供方便快捷的高性能 API。
-
-```js
-var puttingStream = imagesBucket.createPutStream('exampleKey_3');
-var readingStream = fs.createReadStream(__dirname + '/assets/example.jpg');
-
-readingStream.pipe(puttingStream)
-  .on('error', function(err) {
-    console.error(err);
-  })
-  .on('end', function(reply) {
-    console.dir(reply);
-  });
-```
-
-#### 下载文件
-
-`Bucket.getFile()`和`Bucket.createGetStream()`
-
-获取文件与上传文件同样简单,和 Node.js 中原生的文件系统(File System)的 API 有相似之处。
-
-```js
-imagesBucket.getFile('exampleKey', function(err, data) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // data 为包含文件数据的 Buffer 对象
-});
-```
-
-同样的,获取文件也可以使用流式操作进行
-```js
-var gettingStream = imagesBucket.createGetStream('exampleKey');
-var writingStream = fs.createWriteStream(__dirname + '/assets/example_tmp.jpg');
-
-gettingStream.pipe(writingStream)
-  .on('error', function(err) {
-    console.error(err);
-  })
-  .on('finish', function() {
-    // 文件数据已写入本地文件系统
-  });
-```
-
-### `Image` 图片操作
-
-七牛 Node.js SDK 提供`Image`类,用于对图片资源进行操作。
-
-使用 Bucket.image() 方法获取一个图像对象
-
-```js
-var image = imagesBucket.image('exampleKey');
-```
-
-#### `Image.imageInfo()`
-
-Image.imageInfo 方法可以用于获取图片资源的图片信息。
-详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageInfo](http://docs.qiniu.com/api/v6/image-process.html#imageInfo)
-
-```js
-image.imageInfo(function(err, info) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(info);
-});
-```
-
-#### `Image.exif()`
-
-Image.imageView 方法用于生成指定规格的缩略图。
-详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageView](http://docs.qiniu.com/api/v6/image-process.html#imageView)
-
-```js
-image.exif(function(err, exif) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(exif);
-});
-```
-
-#### `Image.imageView()`
-
-Image.imageView 方法用于生成指定规格的缩略图。
-详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageView](http://docs.qiniu.com/api/v6/image-process.html#imageView)
-
-```js
-image.imageView({
-  mode    : 2,
-  width   : 180,
-  height  : 180,
-  quality : 85,
-  format  : 'jpg'
-}, function(err, imageData) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // imageData 为处理过后的图像数据
-});
-```
-
-其中,图片对象中的所有含图片数据返回的方法可以使用流式操作。不传入第二个的 callback 参数,而在调用方法的括弧后再调用`.stream()`方法,则会返回一个会不断输出数据的 IO 流。
-
-```js
-var imageViewStream = image.imageView({
-  mode    : 2,
-  width   : 180,
-  height  : 180,
-  quality : 85,
-  format  : 'jpg'
-}).stream();
-var writingStream = fs.createWriteStream(__dirname + '/assets/example_thumbnail.jpg');
-
-imageViewStream.pipe(writingStream)
-  .on('error', function(err) {
-    console.error(err);
-  })
-  .on('finish', function() {
-    // 缩略图已写入本地文件系统
-  });
-```
-
-诸如此类:
-```js
-image.imageMogr(...).stream();
-image.watermark(...).stream();
-image.alias(...).stream();
-```
-
-#### `Image.imageMogr()`
-
-Image.imageMogr 方法用于调用高级图像处理接口,并返回处理后的图片数据。
-详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageMogr](http://docs.qiniu.com/api/v6/image-process.html#imageMogr)
-
-```js
-image.imageMogr({
-  thumbnail : '300x500',
-  gravity   : 'NorthWest',
-  crop      : '!300x400a10a10',
-  quality   : 85,
-  rotate    : 90,
-  format    : 'jpg'
-}, function(err, imageData) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // 使用 imageData 进行操作
-});
-```
-
-#### `Image.watermark()`
-
-Image.watermark 方法用于生成一个带有水印的图片,图片水印 API 支持图片水印和文字水印两种模式。
-详细请看:http://docs.qiniu.com/api/v6/image-process.html#watermark
-
-```js
-image.watermark({
-  mode: 1,
-  image: 'http://www.b1.qiniudn.com/images/logo-2.png',
-  dissolve: 70,
-  gravity: 'SouthEast',
-  dx: 20,
-  dy: 20
-}, function(err, imageData) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // 使用 imageData 进行操作
-});
-```
-
-#### `Image.alias()`
-
-Image.alias 方法用于返回既定的数据处理格式的数据,使用此方法需要在[七牛开发者平台](https://portal.qiniu.com)中对设置进行操作。
-其中,`Image.alias()`方法继承于 key 所用的`Asset`类。
-
-```js
-image.alias('testalias', function(err, imageData) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // 使用 imageData 进行操作
-});
-```
-
-### `Asset` 资源操作
-
-七牛 Node.js SDK 提供一个`Asset`类,用于对所属资源进行操作。
-
-获取 key 所对应资源对象
-```js
-var picture = imagesBucket.key('exampleKey');
-```
-
-#### `Asset.url()`
-
-`Asset.url()`方法可以获得该资源的 URL 地址以用于访问
-
-```js
-var picUrl = picture.url();
-```
-
-#### `Asset.entryUrl()`
-
-`Asset.entryUrl()`方法可以获得该资源用于 API 调用时所需的 EncodedEntryURL。
-但是在 Node.js SDK 中,大部分 API 都不需要开发者自行使用。:)
-
-```js
-var encodedPicUrl = picture.entryUrl();
-```
-
-#### `Asset.stat()`
-
-Asset.stat 方法可以获得该资源的如文件大小、MIME 类型等 stat 数据。
-
-```js
-picture.stat(function(err, stat) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(stat);
-  /**
-   * {
-   *   hash     : <FileEtag>, // string 类型,文件的Hash值
-   *   fsize    : <FileSize>, // int 类型,文件的大小(单位: 字节)
-   *   mimeType : <MimeType>, // string 类型,文件的媒体类型,比如"image/gif"
-   *   putTime  : <PutTime>   // int64 类型,文件上传到七牛云的时间(Unix时间戳)
-   * }
-   */
-});
-```
-
-#### `Asset.move()`
-
-`Asset.move()`方法用于移动该资源到指定的位置。
-第一个参数可以是来自其他 Bucket 所属的资源对象。
-
-```js
-picture.move(imagesBucket.key('exampleKey_4'), function(err) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // 此处没有返回值,如果没有错误,则为操作成功,以下方法相同
-});
-```
-
-#### `Asset.copy()`
-
-`Asset.copy()`方法用于为该资源创建一个拷贝,并保存到指定的资源位置。
-
-```js
-imagesBucket.key('exampleKey_4').copy(picture, function(err) {
-  if (err) {
-    return console.error(err);
-  }
-});
-```
-
-#### `Asset.remove()`
-
-`Asset.remove()`方法用于删除当前资源。
-
-```js
-imagesBucket.key('exampleKey_4').remove(function(err) {
-  if (err) {
-    return console.error(err);
-  }
-});
-```
-
-#### `Asset.token()`
-
-`Asset.token()`方法用于生成当前资源的下载凭证。
-
-```js
-var getToken = imagesBucket.key('exampleKey').token();
-
-console.dir(getToken);
-/*=>
-  {
-    url: '<Full download url>',
-    token: '<Get token>',
-    requestUrl: 'URL without token'
-  }
- */
-  }
-```
-
-#### `Asset.download()`
-
-`Asset.download()`方法用于生成当前资源的下载链接。(不包含下载凭证)
-
-```js
-var url = imagesBucket.key('exampleKey').download();
-```
-
-### `Batch` 资源批量操作
-
-在支持对单个文件资源操作的同时,七牛云存储还支持批量地对多个文件进行查看、删除、复制和移动操作。
-详细请看:http://docs.qiniu.com/api/v6/rs.html#batch
-
-生成一个批量操作的控制器
-```js
-var batch = qiniu.batch();
-```
-
-`Batch`中大部分参数与资源对象`Asset`类似,支持查看、移动、复制和删除操作。
-
-```js
-batch
-  // 获取文件信息
-  .stat(imagesBucket.key('exampleKey'))
-  // 移动资源
-  .move(imagesBucket.key('exampleKey'), imagesBucket.key('exampleKey_5'))
-  // 复制资源
-  .copy(imagesBucket.key('exampleKey_5'), imagesBucket.key('exampleKey'))
-  // 删除资源
-  .remove(imagesBucket.key('exampleKey_5'))
-  // 执行操作
-  // 每一个操作都按照前后顺序进行执行
-  .exec(function(err, results) {
-    if (err) {
-      return console.error(err);
-    }
-
-    console.dir(results);
-    // results 为每一个操作的结果
-  });
-```
-
-### `Fop` 管道操作
-
-七牛云存储提供一个非常实用的资源处理 API,可以用于对资源进行多种处理的操作。
-
-例: 将一个原图缩略,然后在缩略图上打上另外一个图片作为水印
-
-使用`Asset.fop()`方法创建 Fop 管道操作器,并进行操作。
-
-```js
-var image = imagesBucket.key('exampleKey');
-// Image.fop 方法继承于 Asset 类
-
-image.fop()
-  // 对图片进行缩略
-  .imageView({
-    mode   : 2,
-    height : 200
-  })
-  // 为图片打上水印
-  .watermark({
-    mode  : 1,
-    image : 'http://www.b1.qiniudn.com/images/logo-2.png'
-  })
-  // 执行操作
-  .exec(function(err, imageData) {
-    if (err) {
-      return console.error(err);
-    }
-
-    // imageData 为已打上水印的缩略图数据
-  });
-```
-
-#### `Fop.token()`
-
-该方法用于生成当前 Fop 的下载凭证。
-
-```js
-var image = imagesBucket.key('exampleKey');
-// Image.fop 方法继承于 Asset 类
-
-var getToken = image.fop()
-  // 对图片进行缩略
-  .imageView({
-    mode   : 2,
-    height : 200
-  })
-  // 为图片打上水印
-  .watermark({
-    mode  : 1,
-    image : 'http://www.b1.qiniudn.com/images/logo-2.png'
-  })
-  .token();
-
-console.log(getToken.url);
-```
-
-## 模块结构
-![模块结构](http://ww2.sinaimg.cn/large/7287333fgw1e8263cvxeaj20mr0glgmp.jpg)
-
-## License 
-
-    (The MIT License)
-    
-    Copyright (c) 2010-2013 Will Wen Gunn <willwengunn@gmail.com> and other contributors
-    
-    Permission is hereby granted, free of charge, to any person obtaining
-    a copy of this software and associated documentation files (the
-    'Software'), to deal in the Software without restriction, including
-    without limitation the rights to use, copy, modify, merge, publish,
-    distribute, sublicense, and/or sell copies of the Software, and to
-    permit persons to whom the Software is furnished to do so, subject to
-    the following conditions:
-    
-    The above copyright notice and this permission notice shall be
-    included in all copies or substantial portions of the Software.
-    
-    THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+# 七牛 Node.js SDK
+
+该 SDK 适用于 Node.js 0.4.7 及其以上版本,基于 七牛云存储官方API 构建。 若您的服务端是一个基于 Node.js 编写的网络程序,使用此 SDK , 能让您以非常便捷地方式将数据安全地存储到七牛云存储上。 以便让您应用的终端用户进行高速上传和下载,同时也使得您的服务端更加轻盈。
+
+## 安装
+
+你可以从 npm 进行安装
+
+```shell
+npm install node-qiniu
+```
+
+也可以从 Github 进行下载安装
+
+```shell
+$ git clone git://github.com/qiniu/node-qiniu
+$ cd node-qiniu
+$ npm install .
+```
+
+## 测试
+
+七牛 Node.js SDK 使用 Mocha 进行单元测试。
+
+```shell
+$ npm install -g mocha
+$ make test
+```
+
+## 使用
+
+### 配置 `qiniu.config()`
+
+设置全局参数,包括必须的 AccessKey 和 SecretKey,还可以设置其他如 CallbackURL 等参数,将会顺延至所有空间。
+
+```js
+qiniu.config({
+  access_key: '------',
+  secret_key: '------'
+});
+```
+
+### Bucket
+
+获得空间对象并进行操作。
+
+```js
+var imagesBucket = qiniu.bucket('qiniu-sdk-test');
+// 也可以这样操作
+// var imagesBucket = new qiniu.Bucket('qiniu-sdk-test');
+```
+
+#### 上传文件
+
+**1. `Bucket.putFile()`**
+
+上传一个文件,参数为将要上传的 Key,文件地址(可以是绝对地址,也可以是相对地址),第三个为可选参数 options,即本次上传中所使用 PutToken 的特殊设置,第四个为可选参数回调(callback),若不传入回调函数,将由 putFile 函数所返回的 Promise 对象进行响应。
+
+```js
+// 普通上传
+imagesBucket.putFile('exampleKey', __dirname + '/assets/example.jpg', function(err, reply) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(reply);
+});
+// 特殊参数
+imagesBucket.putFile('exampleKey_1', __dirname + '/assets/example.jpg', {
+  // 为本次上传中所使用的 Token 进行设置,此处为设置上传者标识
+  endUser: 'foobar'
+}, function(err, reply) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(reply);
+});
+// 七牛 Node.js SDK 所提供的 Promise 对象遵循 Promise/A(+) 标准,使用 .then 方法进行响应
+imagesBucket.putFile('exampleKey_2', __dirname + '/assets/example.jpg')
+  .then(
+    function(reply) {
+      // 上传成功
+      console.dir(reply);
+    },
+    function(err) {
+      // 上传失败
+      console.error(err);
+    }
+  );
+```
+
+**2. `Bucket.createPutStream()`**
+
+七牛 Node.js SDK 提供基于流(Stream)的操作方式,为熟悉 Node.js 流式操作的开发者提供方便快捷的高性能 API。
+
+```js
+var puttingStream = imagesBucket.createPutStream('exampleKey_3');
+var readingStream = fs.createReadStream(__dirname + '/assets/example.jpg');
+
+readingStream.pipe(puttingStream)
+  .on('error', function(err) {
+    console.error(err);
+  })
+  .on('end', function(reply) {
+    console.dir(reply);
+  });
+```
+
+#### 下载文件
+
+`Bucket.getFile()`和`Bucket.createGetStream()`
+
+获取文件与上传文件同样简单,和 Node.js 中原生的文件系统(File System)的 API 有相似之处。
+
+```js
+imagesBucket.getFile('exampleKey', function(err, data) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // data 为包含文件数据的 Buffer 对象
+});
+```
+
+同样的,获取文件也可以使用流式操作进行
+```js
+var gettingStream = imagesBucket.createGetStream('exampleKey');
+var writingStream = fs.createWriteStream(__dirname + '/assets/example_tmp.jpg');
+
+gettingStream.pipe(writingStream)
+  .on('error', function(err) {
+    console.error(err);
+  })
+  .on('finish', function() {
+    // 文件数据已写入本地文件系统
+  });
+```
+
+### `Image` 图片操作
+
+七牛 Node.js SDK 提供`Image`类,用于对图片资源进行操作。
+
+使用 Bucket.image() 方法获取一个图像对象
+
+```js
+var image = imagesBucket.image('exampleKey');
+```
+
+#### `Image.imageInfo()`
+
+Image.imageInfo 方法可以用于获取图片资源的图片信息。
+详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageInfo](http://docs.qiniu.com/api/v6/image-process.html#imageInfo)
+
+```js
+image.imageInfo(function(err, info) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(info);
+});
+```
+
+#### `Image.exif()`
+
+Image.imageView 方法用于生成指定规格的缩略图。
+详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageView](http://docs.qiniu.com/api/v6/image-process.html#imageView)
+
+```js
+image.exif(function(err, exif) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(exif);
+});
+```
+
+#### `Image.imageView()`
+
+Image.imageView 方法用于生成指定规格的缩略图。
+详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageView](http://docs.qiniu.com/api/v6/image-process.html#imageView)
+
+```js
+image.imageView({
+  mode    : 2,
+  width   : 180,
+  height  : 180,
+  quality : 85,
+  format  : 'jpg'
+}, function(err, imageData) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // imageData 为处理过后的图像数据
+});
+```
+
+其中,图片对象中的所有含图片数据返回的方法可以使用流式操作。不传入第二个的 callback 参数,而在调用方法的括弧后再调用`.stream()`方法,则会返回一个会不断输出数据的 IO 流。
+
+```js
+var imageViewStream = image.imageView({
+  mode    : 2,
+  width   : 180,
+  height  : 180,
+  quality : 85,
+  format  : 'jpg'
+}).stream();
+var writingStream = fs.createWriteStream(__dirname + '/assets/example_thumbnail.jpg');
+
+imageViewStream.pipe(writingStream)
+  .on('error', function(err) {
+    console.error(err);
+  })
+  .on('finish', function() {
+    // 缩略图已写入本地文件系统
+  });
+```
+
+诸如此类:
+```js
+image.imageMogr(...).stream();
+image.watermark(...).stream();
+image.alias(...).stream();
+```
+
+#### `Image.imageMogr()`
+
+Image.imageMogr 方法用于调用高级图像处理接口,并返回处理后的图片数据。
+详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageMogr](http://docs.qiniu.com/api/v6/image-process.html#imageMogr)
+
+```js
+image.imageMogr({
+  thumbnail : '300x500',
+  gravity   : 'NorthWest',
+  crop      : '!300x400a10a10',
+  quality   : 85,
+  rotate    : 90,
+  format    : 'jpg'
+}, function(err, imageData) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // 使用 imageData 进行操作
+});
+```
+
+#### `Image.watermark()`
+
+Image.watermark 方法用于生成一个带有水印的图片,图片水印 API 支持图片水印和文字水印两种模式。
+详细请看:http://docs.qiniu.com/api/v6/image-process.html#watermark
+
+```js
+image.watermark({
+  mode: 1,
+  image: 'http://www.b1.qiniudn.com/images/logo-2.png',
+  dissolve: 70,
+  gravity: 'SouthEast',
+  dx: 20,
+  dy: 20
+}, function(err, imageData) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // 使用 imageData 进行操作
+});
+```
+
+#### `Image.alias()`
+
+Image.alias 方法用于返回既定的数据处理格式的数据,使用此方法需要在[七牛开发者平台](https://portal.qiniu.com)中对设置进行操作。
+其中,`Image.alias()`方法继承于 key 所用的`Asset`类。
+
+```js
+image.alias('testalias', function(err, imageData) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // 使用 imageData 进行操作
+});
+```
+
+### `Asset` 资源操作
+
+七牛 Node.js SDK 提供一个`Asset`类,用于对所属资源进行操作。
+
+获取 key 所对应资源对象
+```js
+var picture = imagesBucket.key('exampleKey');
+```
+
+#### `Asset.url()`
+
+`Asset.url()`方法可以获得该资源的 URL 地址以用于访问
+
+```js
+var picUrl = picture.url();
+```
+
+#### `Asset.entryUrl()`
+
+`Asset.entryUrl()`方法可以获得该资源用于 API 调用时所需的 EncodedEntryURL。
+但是在 Node.js SDK 中,大部分 API 都不需要开发者自行使用。:)
+
+```js
+var encodedPicUrl = picture.entryUrl();
+```
+
+#### `Asset.stat()`
+
+Asset.stat 方法可以获得该资源的如文件大小、MIME 类型等 stat 数据。
+
+```js
+picture.stat(function(err, stat) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(stat);
+  /**
+   * {
+   *   hash     : <FileEtag>, // string 类型,文件的Hash值
+   *   fsize    : <FileSize>, // int 类型,文件的大小(单位: 字节)
+   *   mimeType : <MimeType>, // string 类型,文件的媒体类型,比如"image/gif"
+   *   putTime  : <PutTime>   // int64 类型,文件上传到七牛云的时间(Unix时间戳)
+   * }
+   */
+});
+```
+
+#### `Asset.move()`
+
+`Asset.move()`方法用于移动该资源到指定的位置。
+第一个参数可以是来自其他 Bucket 所属的资源对象。
+
+```js
+picture.move(imagesBucket.key('exampleKey_4'), function(err) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // 此处没有返回值,如果没有错误,则为操作成功,以下方法相同
+});
+```
+
+#### `Asset.copy()`
+
+`Asset.copy()`方法用于为该资源创建一个拷贝,并保存到指定的资源位置。
+
+```js
+imagesBucket.key('exampleKey_4').copy(picture, function(err) {
+  if (err) {
+    return console.error(err);
+  }
+});
+```
+
+#### `Asset.remove()`
+
+`Asset.remove()`方法用于删除当前资源。
+
+```js
+imagesBucket.key('exampleKey_4').remove(function(err) {
+  if (err) {
+    return console.error(err);
+  }
+});
+```
+
+#### `Asset.token()`
+
+`Asset.token()`方法用于生成当前资源的下载凭证。
+
+```js
+var getToken = imagesBucket.key('exampleKey').token();
+
+console.dir(getToken);
+/*=>
+  {
+    url: '<Full download url>',
+    token: '<Get token>',
+    requestUrl: 'URL without token'
+  }
+ */
+  }
+```
+
+#### `Asset.download()`
+
+`Asset.download()`方法用于生成当前资源的下载链接。(不包含下载凭证)
+
+```js
+var url = imagesBucket.key('exampleKey').download();
+```
+
+#### `Asset.fetch()`
+
+`Asset.fetch()`方法用于从指定地址拉取链接,并保存到七牛云存储中。
+
+```js
+imagesBucket.keys('test.jpg').fetch('http://exampple.com/test.jpg')
+```
+
+### `Batch` 资源批量操作
+
+在支持对单个文件资源操作的同时,七牛云存储还支持批量地对多个文件进行查看、删除、复制和移动操作。
+详细请看:http://docs.qiniu.com/api/v6/rs.html#batch
+
+生成一个批量操作的控制器
+```js
+var batch = qiniu.batch();
+```
+
+`Batch`中大部分参数与资源对象`Asset`类似,支持查看、移动、复制和删除操作。
+
+```js
+batch
+  // 获取文件信息
+  .stat(imagesBucket.key('exampleKey'))
+  // 移动资源
+  .move(imagesBucket.key('exampleKey'), imagesBucket.key('exampleKey_5'))
+  // 复制资源
+  .copy(imagesBucket.key('exampleKey_5'), imagesBucket.key('exampleKey'))
+  // 删除资源
+  .remove(imagesBucket.key('exampleKey_5'))
+  // 执行操作
+  // 每一个操作都按照前后顺序进行执行
+  .exec(function(err, results) {
+    if (err) {
+      return console.error(err);
+    }
+
+    console.dir(results);
+    // results 为每一个操作的结果
+  });
+```
+
+### `Fop` 管道操作
+
+七牛云存储提供一个非常实用的资源处理 API,可以用于对资源进行多种处理的操作。
+
+例: 将一个原图缩略,然后在缩略图上打上另外一个图片作为水印
+
+使用`Asset.fop()`方法创建 Fop 管道操作器,并进行操作。
+
+```js
+var image = imagesBucket.key('exampleKey');
+// Image.fop 方法继承于 Asset 类
+
+image.fop()
+  // 对图片进行缩略
+  .imageView({
+    mode   : 2,
+    height : 200
+  })
+  // 为图片打上水印
+  .watermark({
+    mode  : 1,
+    image : 'http://www.b1.qiniudn.com/images/logo-2.png'
+  })
+  // 执行操作
+  .exec(function(err, imageData) {
+    if (err) {
+      return console.error(err);
+    }
+
+    // imageData 为已打上水印的缩略图数据
+  });
+```
+
+#### `Fop.token()`
+
+该方法用于生成当前 Fop 的下载凭证。
+
+```js
+var image = imagesBucket.key('exampleKey');
+// Image.fop 方法继承于 Asset 类
+
+var getToken = image.fop()
+  // 对图片进行缩略
+  .imageView({
+    mode   : 2,
+    height : 200
+  })
+  // 为图片打上水印
+  .watermark({
+    mode  : 1,
+    image : 'http://www.b1.qiniudn.com/images/logo-2.png'
+  })
+  .token();
+
+console.log(getToken.url);
+```
+
+## 模块结构
+![模块结构](http://ww2.sinaimg.cn/large/7287333fgw1e8263cvxeaj20mr0glgmp.jpg)
+
+## License 
+
+    (The MIT License)
+    
+    Copyright (c) 2010-2013 Will Wen Gunn <willwengunn@gmail.com> and other contributors
+    
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    'Software'), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+    
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+    
+    THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/example.js b/example.js
index aeedc71..b408d54 100644
--- a/example.js
+++ b/example.js
@@ -1,304 +1,304 @@
-// 引入七牛 Node.js SDK
-var qiniu = require('qiniu');
-var fs = require('fs');
-
-// 设置全局参数,包括必须的 AccessKey 和 SecretKey,
-// 还可以设置其他如 CallbackURL 等参数,将会顺延至所有空间。
-qiniu.config({
-  access_key: '------',
-  secret_key: '------'
-});
-
-// 获得空间对象
-var imagesBucket = qiniu.bucket('qiniu-sdk-test');
-// 也可以这样操作
-// var imagesBucket = new qiniu.Bucket('qiniu-sdk-test');
-
-// 上传一个文件,参数为将要上传的 Key,文件地址(可以是绝对地址,也可以是相对地址),
-// 第三个为可选参数 options,即本次上传中所使用 PutToken 的特殊设置,
-// 第四个为可选参数回调(callback),
-// 若不传入回调函数,将由 putFile 函数所返回的 Promise 对象进行响应。
-imagesBucket.putFile('exampleKey', __dirname + '/assets/example.jpg', function(err, reply) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(reply);
-});
-imagesBucket.putFile('exampleKey_1', __dirname + '/assets/example.jpg', {
-  // 为本次上传中所使用的 Token 进行设置,此处为设置上传者标识
-  endUser: 'foobar'
-}, function(err, reply) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(reply);
-});
-// 七牛 Node.js SDK 所提供的 Promise 对象遵循 Promise/A(+) 标准,使用 .then 方法进行响应
-imagesBucket.putFile('exampleKey_2', __dirname + '/assets/example.jpg')
-  .then(
-    function(reply) {
-      // 上传成功
-      console.dir(reply);
-    },
-    function(err) {
-      // 上传失败
-      console.error(err);
-    }
-  );
-
-// 七牛 Node.js SDK 提供基于流(Stream)的操作方式,
-// 为熟悉 Node.js 流式操作的开发者提供方便快捷的高性能 API
-var puttingStream = imagesBucket.createPutStream('exampleKey_3');
-var readingStream = fs.createReadStream(__dirname + '/assets/example.jpg');
-
-readingStream.pipe(readingStream)
-  .on('error', function(err) {
-    console.error(err);
-  })
-  .on('end', function(reply) {
-    console.dir(reply);
-  });
-
-// 获取文件与上传文件同样简单,和 Node.js 中原生的文件系统(File System)的 API 有相似之处
-imagesBucket.getFile('exampleKey', function(err, data) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // data 为包含文件数据的 Buffer 对象
-});
-// 同样的,获取文件也可以使用流式操作进行
-var gettingStream = imagesBucket.createGetStream('exampleKey');
-var writingStream = fs.createWriteStream(__dirname + '/assets/example_tmp.jpg');
-
-gettingStream.pipe(writingStream)
-  .on('error', function(err) {
-    console.error(err);
-  })
-  .on('finish', function() {
-    // 文件数据已写入本地文件系统
-  });
-
-// 图片处理
-// 使用 Bucket.image 方法获取一个图像对象
-var image = imagesBucket.image('exampleKey');
-
-// Image.imageInfo 方法可以用于获取图片资源的图片信息
-// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageInfo
-image.imageInfo(function(err, info) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(info);
-});
-
-// Image.exif 方法可以用于获取图片资源的EXIF信息,
-// 前提是该图片由数码相机生成,并且没有清除EXIF信息
-// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageExif
-image.exif(function(err, exif) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(exif);
-});
-
-// Image.imageView 方法用于生成指定规格的缩略图
-// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageView
-image.imageView({
-  mode    : 2,
-  width   : 180,
-  height  : 180,
-  quality : 85,
-  format  : 'jpg'
-}, function(err, imageData) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // imageData 为处理过后的图像数据
-});
-
-// 其中,图片对象中的所有含图片数据返回的方法可以使用流式操作
-// 不传入第二个的 callback 参数,而在调用方法的括弧后再调用 .stream 方法,
-// 则会返回一个会不断输出数据的 IO 流
-var imageViewStream = image.imageView({
-  mode    : 2,
-  width   : 180,
-  height  : 180,
-  quality : 85,
-  format  : 'jpg'
-}).stream();
-var writingStream = fs.createWriteStream(__dirname + '/assets/example_thumbnail.jpg');
-
-imageViewStream.pipe(writingStream)
-  .on('error', function(err) {
-    console.error(err);
-  })
-  .on('finish', function() {
-    // 缩略图已写入本地文件系统
-  });
-
-/**
- * 诸如此类:
- *   image.imageMogr(...).stream();
- *   image.watermark(...).stream();
- *   image.alias(...).stream();
- */
-
-// Image.imageMogr 方法用于调用高级图像处理接口,并返回处理后的图片数据
-// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageMogr
-image.imageMogr({
-  thumbnail : '300x500',
-  gravity   : 'NorthWest',
-  crop      : '!300x400a10a10',
-  quality   : 85,
-  rotate    : 90,
-  format    : 'jpg'
-}, function(err, imageData) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // 使用 imageData 进行操作
-});
-
-// Image.watermark 方法用于生成一个带有水印的图片
-// 图片水印 API 支持图片水印和文字水印两种模式
-// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#watermark
-image.watermark({
-  mode: 1,
-  image: 'http://www.b1.qiniudn.com/images/logo-2.png',
-  dissolve: 70,
-  gravity: 'SouthEast',
-  dx: 20,
-  dy: 20
-}, function(err, imageData) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // 使用 imageData 进行操作
-});
-
-// Image.alias 方法用于返回既定的数据处理格式的数据
-// 使用此方法需要在 https://portal.qiniu.com 中对设置进行操作
-// PS: Image.alias 方法继承于 key 所用的 Asset 类
-image.alias('testalias', function(err, imageData) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // 使用 imageData 进行操作
-});
-
-// 资源操作
-// 获取 key 所对应资源对象
-var picture = imagesBucket.key('exampleKey');
-
-// Asset.url 方法可以获得该资源的 URL 地址以用于访问
-var picUrl = picture.url();
-
-// Asset.entryUrl 方法可以获得该资源用于 API 调用时所需的 EncodedEntryURL
-// 但是在 Node.js SDK 中,大部分 API 都不需要开发者自行使用。:)
-var encodedPicUrl = picture.entryUrl();
-
-// Asset.stat 方法可以获得该资源的如文件大小、MIME 类型等 stat 数据
-picture.stat(function(err, stat) {
-  if (err) {
-    return console.error(err);
-  }
-
-  console.dir(stat);
-  /**
-   * {
-   *   hash     : <FileEtag>, // string 类型,文件的Hash值
-   *   fsize    : <FileSize>, // int 类型,文件的大小(单位: 字节)
-   *   mimeType : <MimeType>, // string 类型,文件的媒体类型,比如"image/gif"
-   *   putTime  : <PutTime>   // int64 类型,文件上传到七牛云的时间(Unix时间戳)
-   * }
-   */
-});
-
-// Asset.move 方法用于移动该资源到指定的位置
-// 第一个参数可以是来自其他 Bucket 所属的资源对象
-picture.move(imagesBucket.key('exampleKey_4'), function(err) {
-  if (err) {
-    return console.error(err);
-  }
-
-  // 此处没有返回值,如果没有错误,则为操作成功,以下方法相同
-});
-
-// Asset.copy 方法用于为该资源创建一个拷贝,并保存到指定的资源位置
-imagesBucket.key('exampleKey_4').copy(picture, function(err) {
-  if (err) {
-    return console.error(err);
-  }
-});
-
-// Asset.remove方法用于删除当前资源
-imagesBucket.key('exampleKey_4').remove(function(err) {
-  if (err) {
-    return console.error(err);
-  }
-});
-
-// 资源批量操作
-// 在支持对单个文件资源操作的同时,七牛云存储还支持批量地对多个文件进行查看、删除、复制和移动操作。
-// 详细请看:http://docs.qiniu.com/api/v6/rs.html#batch
-
-// 生成一个批量操作的控制器
-var batch = qiniu.batch();
-
-batch
-  // 获取文件信息
-  .stat(imagesBucket.key('exampleKey'))
-  // 移动资源
-  .move(imagesBucket.key('exampleKey'), imagesBucket.key('exampleKey_5'))
-  // 复制资源
-  .copy(imagesBucket.key('exampleKey_5'), imagesBucket.key('exampleKey'))
-  // 删除资源
-  .remove(imagesBucket.key('exampleKey_5'))
-  // 执行操作
-  // 每一个操作都按照前后顺序进行执行
-  .exec(function(err, results) {
-    if (err) {
-      return console.error(err);
-    }
-
-    console.dir(results);
-    // results 为每一个操作的结果
-  });
-
-// Fop 管道操作
-// 七牛云存储提供一个非常实用的资源处理 API,可以用于对资源进行多种处理的操作
-
-// 例: 将一个原图缩略,然后在缩略图上打上另外一个图片作为水印
-
-var image = imagesBucket.key('exampleKey');
-
-// 使用 Asset.fop 方法创建 Fop 管道操作器
-// Image.fop 方法继承于 Asset 类
-image.fop()
-  // 对图片进行缩略
-  .imageView({
-    mode   : 2,
-    height : 200
-  })
-  // 为图片打上水印
-  .watermark({
-    mode  : 1,
-    image : 'http://www.b1.qiniudn.com/images/logo-2.png'
-  })
-  // 执行操作
-  .exec(function(err, imageData) {
-    if (err) {
-      return console.error(err);
-    }
-
-    // imageData 为已打上水印的缩略图数据
+// 引入七牛 Node.js SDK
+var qiniu = require('qiniu');
+var fs = require('fs');
+
+// 设置全局参数,包括必须的 AccessKey 和 SecretKey,
+// 还可以设置其他如 CallbackURL 等参数,将会顺延至所有空间。
+qiniu.config({
+  access_key: '------',
+  secret_key: '------'
+});
+
+// 获得空间对象
+var imagesBucket = qiniu.bucket('qiniu-sdk-test');
+// 也可以这样操作
+// var imagesBucket = new qiniu.Bucket('qiniu-sdk-test');
+
+// 上传一个文件,参数为将要上传的 Key,文件地址(可以是绝对地址,也可以是相对地址),
+// 第三个为可选参数 options,即本次上传中所使用 PutToken 的特殊设置,
+// 第四个为可选参数回调(callback),
+// 若不传入回调函数,将由 putFile 函数所返回的 Promise 对象进行响应。
+imagesBucket.putFile('exampleKey', __dirname + '/assets/example.jpg', function(err, reply) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(reply);
+});
+imagesBucket.putFile('exampleKey_1', __dirname + '/assets/example.jpg', {
+  // 为本次上传中所使用的 Token 进行设置,此处为设置上传者标识
+  endUser: 'foobar'
+}, function(err, reply) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(reply);
+});
+// 七牛 Node.js SDK 所提供的 Promise 对象遵循 Promise/A(+) 标准,使用 .then 方法进行响应
+imagesBucket.putFile('exampleKey_2', __dirname + '/assets/example.jpg')
+  .then(
+    function(reply) {
+      // 上传成功
+      console.dir(reply);
+    },
+    function(err) {
+      // 上传失败
+      console.error(err);
+    }
+  );
+
+// 七牛 Node.js SDK 提供基于流(Stream)的操作方式,
+// 为熟悉 Node.js 流式操作的开发者提供方便快捷的高性能 API
+var puttingStream = imagesBucket.createPutStream('exampleKey_3');
+var readingStream = fs.createReadStream(__dirname + '/assets/example.jpg');
+
+readingStream.pipe(readingStream)
+  .on('error', function(err) {
+    console.error(err);
+  })
+  .on('end', function(reply) {
+    console.dir(reply);
+  });
+
+// 获取文件与上传文件同样简单,和 Node.js 中原生的文件系统(File System)的 API 有相似之处
+imagesBucket.getFile('exampleKey', function(err, data) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // data 为包含文件数据的 Buffer 对象
+});
+// 同样的,获取文件也可以使用流式操作进行
+var gettingStream = imagesBucket.createGetStream('exampleKey');
+var writingStream = fs.createWriteStream(__dirname + '/assets/example_tmp.jpg');
+
+gettingStream.pipe(writingStream)
+  .on('error', function(err) {
+    console.error(err);
+  })
+  .on('finish', function() {
+    // 文件数据已写入本地文件系统
+  });
+
+// 图片处理
+// 使用 Bucket.image 方法获取一个图像对象
+var image = imagesBucket.image('exampleKey');
+
+// Image.imageInfo 方法可以用于获取图片资源的图片信息
+// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageInfo
+image.imageInfo(function(err, info) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(info);
+});
+
+// Image.exif 方法可以用于获取图片资源的EXIF信息,
+// 前提是该图片由数码相机生成,并且没有清除EXIF信息
+// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageExif
+image.exif(function(err, exif) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(exif);
+});
+
+// Image.imageView 方法用于生成指定规格的缩略图
+// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageView
+image.imageView({
+  mode    : 2,
+  width   : 180,
+  height  : 180,
+  quality : 85,
+  format  : 'jpg'
+}, function(err, imageData) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // imageData 为处理过后的图像数据
+});
+
+// 其中,图片对象中的所有含图片数据返回的方法可以使用流式操作
+// 不传入第二个的 callback 参数,而在调用方法的括弧后再调用 .stream 方法,
+// 则会返回一个会不断输出数据的 IO 流
+var imageViewStream = image.imageView({
+  mode    : 2,
+  width   : 180,
+  height  : 180,
+  quality : 85,
+  format  : 'jpg'
+}).stream();
+var writingStream = fs.createWriteStream(__dirname + '/assets/example_thumbnail.jpg');
+
+imageViewStream.pipe(writingStream)
+  .on('error', function(err) {
+    console.error(err);
+  })
+  .on('finish', function() {
+    // 缩略图已写入本地文件系统
+  });
+
+/**
+ * 诸如此类:
+ *   image.imageMogr(...).stream();
+ *   image.watermark(...).stream();
+ *   image.alias(...).stream();
+ */
+
+// Image.imageMogr 方法用于调用高级图像处理接口,并返回处理后的图片数据
+// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageMogr
+image.imageMogr({
+  thumbnail : '300x500',
+  gravity   : 'NorthWest',
+  crop      : '!300x400a10a10',
+  quality   : 85,
+  rotate    : 90,
+  format    : 'jpg'
+}, function(err, imageData) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // 使用 imageData 进行操作
+});
+
+// Image.watermark 方法用于生成一个带有水印的图片
+// 图片水印 API 支持图片水印和文字水印两种模式
+// 详细请看:http://docs.qiniu.com/api/v6/image-process.html#watermark
+image.watermark({
+  mode: 1,
+  image: 'http://www.b1.qiniudn.com/images/logo-2.png',
+  dissolve: 70,
+  gravity: 'SouthEast',
+  dx: 20,
+  dy: 20
+}, function(err, imageData) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // 使用 imageData 进行操作
+});
+
+// Image.alias 方法用于返回既定的数据处理格式的数据
+// 使用此方法需要在 https://portal.qiniu.com 中对设置进行操作
+// PS: Image.alias 方法继承于 key 所用的 Asset 类
+image.alias('testalias', function(err, imageData) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // 使用 imageData 进行操作
+});
+
+// 资源操作
+// 获取 key 所对应资源对象
+var picture = imagesBucket.key('exampleKey');
+
+// Asset.url 方法可以获得该资源的 URL 地址以用于访问
+var picUrl = picture.url();
+
+// Asset.entryUrl 方法可以获得该资源用于 API 调用时所需的 EncodedEntryURL
+// 但是在 Node.js SDK 中,大部分 API 都不需要开发者自行使用。:)
+var encodedPicUrl = picture.entryUrl();
+
+// Asset.stat 方法可以获得该资源的如文件大小、MIME 类型等 stat 数据
+picture.stat(function(err, stat) {
+  if (err) {
+    return console.error(err);
+  }
+
+  console.dir(stat);
+  /**
+   * {
+   *   hash     : <FileEtag>, // string 类型,文件的Hash值
+   *   fsize    : <FileSize>, // int 类型,文件的大小(单位: 字节)
+   *   mimeType : <MimeType>, // string 类型,文件的媒体类型,比如"image/gif"
+   *   putTime  : <PutTime>   // int64 类型,文件上传到七牛云的时间(Unix时间戳)
+   * }
+   */
+});
+
+// Asset.move 方法用于移动该资源到指定的位置
+// 第一个参数可以是来自其他 Bucket 所属的资源对象
+picture.move(imagesBucket.key('exampleKey_4'), function(err) {
+  if (err) {
+    return console.error(err);
+  }
+
+  // 此处没有返回值,如果没有错误,则为操作成功,以下方法相同
+});
+
+// Asset.copy 方法用于为该资源创建一个拷贝,并保存到指定的资源位置
+imagesBucket.key('exampleKey_4').copy(picture, function(err) {
+  if (err) {
+    return console.error(err);
+  }
+});
+
+// Asset.remove方法用于删除当前资源
+imagesBucket.key('exampleKey_4').remove(function(err) {
+  if (err) {
+    return console.error(err);
+  }
+});
+
+// 资源批量操作
+// 在支持对单个文件资源操作的同时,七牛云存储还支持批量地对多个文件进行查看、删除、复制和移动操作。
+// 详细请看:http://docs.qiniu.com/api/v6/rs.html#batch
+
+// 生成一个批量操作的控制器
+var batch = qiniu.batch();
+
+batch
+  // 获取文件信息
+  .stat(imagesBucket.key('exampleKey'))
+  // 移动资源
+  .move(imagesBucket.key('exampleKey'), imagesBucket.key('exampleKey_5'))
+  // 复制资源
+  .copy(imagesBucket.key('exampleKey_5'), imagesBucket.key('exampleKey'))
+  // 删除资源
+  .remove(imagesBucket.key('exampleKey_5'))
+  // 执行操作
+  // 每一个操作都按照前后顺序进行执行
+  .exec(function(err, results) {
+    if (err) {
+      return console.error(err);
+    }
+
+    console.dir(results);
+    // results 为每一个操作的结果
+  });
+
+// Fop 管道操作
+// 七牛云存储提供一个非常实用的资源处理 API,可以用于对资源进行多种处理的操作
+
+// 例: 将一个原图缩略,然后在缩略图上打上另外一个图片作为水印
+
+var image = imagesBucket.key('exampleKey');
+
+// 使用 Asset.fop 方法创建 Fop 管道操作器
+// Image.fop 方法继承于 Asset 类
+image.fop()
+  // 对图片进行缩略
+  .imageView({
+    mode   : 2,
+    height : 200
+  })
+  // 为图片打上水印
+  .watermark({
+    mode  : 1,
+    image : 'http://www.b1.qiniudn.com/images/logo-2.png'
+  })
+  // 执行操作
+  .exec(function(err, imageData) {
+    if (err) {
+      return console.error(err);
+    }
+
+    // imageData 为已打上水印的缩略图数据
   });
\ No newline at end of file
diff --git a/lib/asset.js b/lib/asset.js
index a0e76d5..6a1b6c4 100644
--- a/lib/asset.js
+++ b/lib/asset.js
@@ -1,8 +1,7 @@
 var request    = require('request');
-var Q          = require('q');
+var Promise    = require('bluebird');
 var util       = require('util');
 var url        = require('url');
-var dataStream = require('dataStream');
 var _          = require('underscore');
 var utils      = require('./utils');
 var config     = require('./config');
@@ -63,37 +62,35 @@ Asset.prototype.entryUrl = function() {
  * @return {Promise}           promise object
  */
 Asset.prototype.qrcode = function(opts, callback) {
-  var deferred = Q.defer();
-  switch (true) {
-    case _.isFunction(opts):
-      callback = opts;
-      opts = { mode: 0, level: 'L' };
-      break;
-    case _.isObject(opts) && _.isUndefined(callback):
-      callback = noop;
-      break;
-    case _.isUndefined(opts):
-      opts = { mode: 0, level: 'L' };
-      callback = noop;
-      break;
-  }
+  var self = this
+  var recive = null
+  var promise = new Promise(function(resolve, reject) {
+    switch (true) {
+      case _.isFunction(opts):
+        callback = opts;
+        opts = { mode: 0, level: 'L' };
+        break;
+      case _.isObject(opts) && _.isUndefined(callback):
+        callback = noop;
+        break;
+      case _.isUndefined(opts):
+        opts = { mode: 0, level: 'L' };
+        callback = noop;
+        break;
+    }
 
-  var _url = util.format('%s?qrcode/%d/level/%s', this.url(), opts.mode, opts.level);
+    var _url = util.format('%s?qrcode/%d/level/%s', self.url(), opts.mode, opts.level);
 
-  var recive = new dataStream();
-  request(_url).pipe(recive)
-    .on('error', function(err) {
-      callback(err);
-      deferred.reject(err);
-    })
-    .on('complete', function() {
-      var data = this.body();
+    recive = request(_url, function(err, resp, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
 
-      callback(null, data);
-      deferred.resolve(data);
+      resolve(body);
+      callback(null, body);
     });
-
-  var promise = deferred.promise;
+  })
 
   promise.stream = function() {
     return recive;
@@ -108,38 +105,38 @@ Asset.prototype.qrcode = function(opts, callback) {
  * @return {Promise}           promise object
  */
 Asset.prototype.stat = function(callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var path = util.format('/stat/%s', this.entryUrl());
-  var _url = url.format({
-    protocol: 'http',
-    hostname: config.rsUrl,
-    pathname: path
-  });
-
-  var token = this.access_token.token(path, null);
-
-  request({
-    url: _url,
-    method: 'POST',
-    headers: {
-      'Authorization': token,
-      'Content-Type': 'application/x-www-form-urlencoded'
-    }
-  }, function(err, res, body) {
-    if (err) {
-      deferred.reject(err);
-      return callback(err);
-    }
-
-    var result = JSON.parse(body);
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
 
-    deferred.resolve(result);
-    callback(null, result);
-  });
+    var path = util.format('/stat/%s', self.entryUrl());
+    var _url = url.format({
+      protocol: 'http',
+      hostname: config.rsUrl,
+      pathname: path
+    });
 
-  return deferred.promise;
+    var token = self.access_token.token(path, null);
+
+    request({
+      url: _url,
+      method: 'POST',
+      headers: {
+        'Authorization': token,
+        'Content-Type': 'application/x-www-form-urlencoded'
+      }
+    }, function(err, res, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      var result = JSON.parse(body);
+
+      resolve(result);
+      callback(null, result);
+    });
+  })
 };
 
 /**
@@ -149,37 +146,37 @@ Asset.prototype.stat = function(callback) {
  * @return {Promise}            promise object
  */
 Asset.prototype.move = function(destAsset, callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var path = util.format('/move/%s/%s', this.entryUrl(), destAsset.entryUrl());
-  var _url = url.format({
-    protocol: 'http',
-    hostname: config.rsUrl,
-    pathname: path
-  });
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
 
-  var token = this.access_token.token(path, null);
+    var path = util.format('/move/%s/%s', self.entryUrl(), destAsset.entryUrl());
+    var _url = url.format({
+      protocol: 'http',
+      hostname: config.rsUrl,
+      pathname: path
+    });
 
-  request({
-    url: _url,
-    method: 'POST',
-    headers: {
-      'Authorization': token,
-      'Content-Type': 'application/x-www-form-urlencoded'
-    }
-  }, function(err, res) {
-    if (err) {
-      deferred.reject(err);
-      return callback(err);
-    }
+    var token = self.access_token.token(path, null);
 
+    request({
+      url: _url,
+      method: 'POST',
+      headers: {
+        'Authorization': token,
+        'Content-Type': 'application/x-www-form-urlencoded'
+      }
+    }, function(err, res) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
 
-    deferred.resolve();
-    callback(null);
-  });
 
-  return deferred.promise;
+      resolve();
+      callback(null);
+    });
+  })
 };
 
 /**
@@ -189,36 +186,36 @@ Asset.prototype.move = function(destAsset, callback) {
  * @return {Promise}            promise object
  */
 Asset.prototype.copy = function(destAsset, callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var path = util.format('/copy/%s/%s', this.entryUrl(), destAsset.entryUrl());
-  var _url = url.format({
-    protocol: 'http',
-    hostname: config.rsUrl,
-    pathname: path
-  });
-
-  var token = this.access_token.token(path, null);
-
-  request({
-    url: _url,
-    method: 'POST',
-    headers: {
-      'Authorization': token,
-      'Content-Type': 'application/x-www-form-urlencoded'
-    }
-  }, function(err, res, body) {
-    if (err) {
-      deferred.reject(err);
-      return callback(err);
-    }
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
 
-    deferred.resolve();
-    callback(null);
-  });
+    var path = util.format('/copy/%s/%s', self.entryUrl(), destAsset.entryUrl());
+    var _url = url.format({
+      protocol: 'http',
+      hostname: config.rsUrl,
+      pathname: path
+    });
 
-  return deferred.promise;
+    var token = self.access_token.token(path, null);
+
+    request({
+      url: _url,
+      method: 'POST',
+      headers: {
+        'Authorization': token,
+        'Content-Type': 'application/x-www-form-urlencoded'
+      }
+    }, function(err, res, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve();
+      callback(null);
+    });
+  })
 };
 
 /**
@@ -227,36 +224,69 @@ Asset.prototype.copy = function(destAsset, callback) {
  * @return {Promise}           promise object
  */
 Asset.prototype.remove = function(callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
 
-  var path = util.format('/delete/%s', this.entryUrl());
-  var _url = url.format({
-    protocol: 'http',
-    hostname: config.rsUrl,
-    pathname: path
-  });
+    var path = util.format('/delete/%s', self.entryUrl());
+    var _url = url.format({
+      protocol: 'http',
+      hostname: config.rsUrl,
+      pathname: path
+    });
 
-  var token = this.access_token.token(path, null);
+    var token = self.access_token.token(path, null);
+
+    request({
+      url: _url, 
+      method: 'POST',
+      headers: {
+        'Authorization': token,
+        'Content-Type': 'application/x-www-form-urlencoded'
+      }
+    }, function(err, res, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve();
+      callback(null);
+    });
+  })
+};
 
-  request({
-    url: _url,
-    method: 'POST',
-    headers: {
-      'Authorization': token,
-      'Content-Type': 'application/x-www-form-urlencoded'
-    }
-  }, function(err, res, body) {
-    if (err) {
-      deferred.reject(err);
-      return callback(err);
-    }
+Asset.prototype.fetch = function(url, callback) {
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
 
-    deferred.resolve();
-    callback(null);
-  });
+    var path = util.format('/fetch/%s/to/%s', utils.safeEncode(url), self.entryUrl());
+    var _url = url.format({
+      protocol: 'http',
+      hostname: config.vipUrl,
+      pathname: path
+    });
 
-  return deferred.promise;
+    var token = self.access_token.token(path, null);
+
+    request({
+      url: _url, 
+      method: 'POST',
+      headers: {
+        'Authorization': token,
+        'Content-Type': 'application/x-www-form-urlencoded'
+      }
+    }, function(err, res, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve();
+      callback(null);
+    });
+  })
 };
 
 Asset.prototype.fop = function(config) {
@@ -270,27 +300,25 @@ Asset.prototype.fop = function(config) {
  * @return {Promise}           promise object
  */
 Asset.prototype.alias = function(alias, callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var _url = this.url();
-
-  _url += util.format('%s%s', this.config.separate, alias);
-  
-  var recive = new dataStream();
-  request(_url).pipe(recive)
-    .on('complete', function() {
-      var data = this.body();
-
-      deferred.resolve(data);
-      callback(null, data);
-    })
-    .on('error', function(err) {
-      deferred.reject(err);
-      callback(err);
-    });
+  var self = this
+  var recive = null
+  var promise = new Promise(function(resolve, reject) {
+    callback = callback || noop;
 
-  var promise = deferred.promise;
+    var _url = self.url();
+
+    _url += util.format('%s%s', self.config.separate, alias);
+    
+    recive = request(_url, function(err, resp, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve(body);
+      callback(null, body);
+    });
+  })
 
   /**
    * return the image stream
@@ -322,48 +350,51 @@ Asset.prototype.download = function(filename) {
  * @return {Promise}           promise object
  */
 Asset.prototype.md2html = function(opts, callback) {
-  var deferred = Q.defer();
-
-  if (_.isFunction(opts)) {
-    callback = opts;
-    opts = {
-      mode: false,
-      css: false
-    };
-  } else if (_.isObject(opts)) {
-    callback = callback || noop;
-  } else {
-    callback = callback || noop;
-    opts = {
-      mode: false,
-      css: false
-    };
-  }
+  var self = this
+  var recive = null
+  var promise = new Promise(function(resolve, reject) {
+    if (_.isFunction(opts)) {
+      callback = opts;
+      opts = {
+        mode: false,
+        css: false
+      };
+    } else if (_.isObject(opts)) {
+      callback = callback || noop;
+    } else {
+      callback = callback || noop;
+      opts = {
+        mode: false,
+        css: false
+      };
+    }
 
-  var _url = this.url() + '?md2html';
+    var _url = self.url() + '?md2html';
 
-  if (opts.mode) {
-    _url += util.format('/%s', opts.mode);
-  }
+    if (opts.mode) {
+      _url += util.format('/%s', opts.mode);
+    }
 
-  if (opts.css) {
-    _url += util.format('/css/%s', utils.safeEncode(opts.css));
-  }
+    if (opts.css) {
+      _url += util.format('/css/%s', utils.safeEncode(opts.css));
+    }
 
-  var recive = new dataStream();
-  request(_url).pipe(recive)
-    .on('complete', function() {
-      var data = this.body();
-
-      deferred.resolve(data);
-      callback(null, data);
-    })
-    .on('error', function(err) {
-      deferred.reject(err);
-      callback(err);
+    recive = request(_url, function(err, resp, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve(body);
+      callback(null, body);
     });
+  })
+
+  promise.stream = function() {
+    return recive
+  }
 
-  return deferred.promise;
+  return promise
 };
 
 module.exports = Asset;
diff --git a/lib/batch.js b/lib/batch.js
index 8dcf9db..b20235d 100644
--- a/lib/batch.js
+++ b/lib/batch.js
@@ -1,142 +1,142 @@
-var request    = require('request');
-var Q          = require('q');
-var _          = require('underscore');
-var util       = require('util');
-var url        = require('url');
-var http       = require('http');
-var utils      = require('./utils');
-var config     = require('./config');
-
-/**
- * multiple assets control
- * @param {Object} _config config
- */
-function Batch(_config) {
-  this.config = _.extend(_.clone(config), _config);
-
-  var token       = require('./token')(this.config);
-  var AccessToken = token.AccessToken;
-
-  this.access_token = new AccessToken();
-  this.op = '';
-}
-
-/**
- * Queue a stat control
- * @param  {Asset} asset asset
- * @return {Batch}       Batch 
- */
-Batch.prototype.stat = function(asset) {
-  this.op += '&' + genOpUrl('stat', asset);
-
-  return this;
-};
-
-/**
- * Queue a move control
- * @param  {Asset} src  source asset
- * @param  {Asset} dest dest asset
- * @return {Batch}      Batch 
- */
-Batch.prototype.move = function(src, dest) {
-  this.op += '&' + genOpUrl('move', src, dest);
-  
-  return this;
-};
-
-/**
- * Queue a copy control
- * @param  {Asset} src  source asset
- * @param  {Asset} dest dest asset
- * @return {Batch}      Batch 
- */
-Batch.prototype.copy = function(src, dest) {
-  this.op += '&' + genOpUrl('copy', src, dest);
-  
-  return this;
-};
-
-/**
- * Queue a delete control
- * @param  {Asset} asset asset
- * @return {Batch}       Batch 
- */
-Batch.prototype.remove = function(asset) {
-  this.op += '&' + genOpUrl('delete', asset);
-
-  return this;
-};
-
-/**
- * Execute the queue
- * @param  {Function} callback Callback
- * @return {Promise}           promise object
- */
-Batch.prototype.exec = function(callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var _url = url.format({
-    protocol: 'http',
-    hostname: this.config.rsUrl,
-    pathname: '/batch'
-  });
-  var body = this.op.substr(1);
-  var token = this.access_token.token('/batch', body);
-
-  request({
-    url: _url,
-    method: 'POST',
-    body: body,
-    headers: {
-      'Authorization': token,
-      'Content-Type': 'application/x-www-form-urlencoded'
-    },
-  }, function(err, res, body) {
-    if (err) {
-      deferred.reject(err);
-      return callback(err);
-    }
-
-    if (body.length > 0) {
-      var rows = JSON.parse(body);
-
-      deferred.resolve(rows);
-      callback(null, rows);
-    } else {
-      err = new Error(http.STATUS_CODES[503]);
-
-      deferred.reject(err);
-      callback(err);
-    }
-  });
-
-  return deferred.promise;
-};
-
-module.exports = Batch;
-
-function genOpUrl(op, src, dest) {
-  var rtn = null;
-
-  switch (op) {
-    case 'stat':
-      rtn = util.format('op=/stat/%s', src.entryUrl());
-      break;
-    case 'move':
-      rtn = util.format('op=/move/%s/%s', src.entryUrl(), dest.entryUrl());
-      break;
-    case 'copy':
-      rtn = util.format('op=/copy/%s/%s', src.entryUrl(), dest.entryUrl());
-      break;
-    case 'delete':
-      rtn = util.format('op=/delete/%s', src.entryUrl());
-      break;
-  }
-
-  return rtn;
-}
-
-function noop() {
-  return false;
+var request    = require('request');
+var Promise    = require('bluebird');
+var _          = require('underscore');
+var util       = require('util');
+var url        = require('url');
+var http       = require('http');
+var utils      = require('./utils');
+var config     = require('./config');
+
+/**
+ * multiple assets control
+ * @param {Object} _config config
+ */
+function Batch(_config) {
+  this.config = _.extend(_.clone(config), _config);
+
+  var token       = require('./token')(this.config);
+  var AccessToken = token.AccessToken;
+
+  this.access_token = new AccessToken();
+  this.op = '';
+}
+
+/**
+ * Queue a stat control
+ * @param  {Asset} asset asset
+ * @return {Batch}       Batch 
+ */
+Batch.prototype.stat = function(asset) {
+  this.op += '&' + genOpUrl('stat', asset);
+
+  return this;
+};
+
+/**
+ * Queue a move control
+ * @param  {Asset} src  source asset
+ * @param  {Asset} dest dest asset
+ * @return {Batch}      Batch 
+ */
+Batch.prototype.move = function(src, dest) {
+  this.op += '&' + genOpUrl('move', src, dest);
+  
+  return this;
+};
+
+/**
+ * Queue a copy control
+ * @param  {Asset} src  source asset
+ * @param  {Asset} dest dest asset
+ * @return {Batch}      Batch 
+ */
+Batch.prototype.copy = function(src, dest) {
+  this.op += '&' + genOpUrl('copy', src, dest);
+  
+  return this;
+};
+
+/**
+ * Queue a delete control
+ * @param  {Asset} asset asset
+ * @return {Batch}       Batch 
+ */
+Batch.prototype.remove = function(asset) {
+  this.op += '&' + genOpUrl('delete', asset);
+
+  return this;
+};
+
+/**
+ * Execute the queue
+ * @param  {Function} callback Callback
+ * @return {Promise}           promise object
+ */
+Batch.prototype.exec = function(callback) {
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
+
+    var _url = url.format({
+      protocol: 'http',
+      hostname: self.config.rsUrl,
+      pathname: '/batch'
+    });
+    var body = self.op.substr(1);
+    var token = self.access_token.token('/batch', body);
+
+    request({
+      url: _url,
+      method: 'POST',
+      body: body,
+      headers: {
+        'Authorization': token,
+        'Content-Type': 'application/x-www-form-urlencoded'
+      },
+    }, function(err, res, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      if (body.length > 0) {
+        var rows = JSON.parse(body);
+
+        resolve(rows);
+        callback(null, rows);
+      } else {
+        err = new Error(http.STATUS_CODES[503]);
+
+        reject(err);
+        callback(err);
+      }
+    });
+  })
+};
+
+module.exports = Batch;
+
+function genOpUrl(op, src, dest) {
+  var rtn = null;
+
+  switch (op) {
+    case 'stat':
+      rtn = util.format('op=/stat/%s', src.entryUrl());
+      break;
+    case 'move':
+      rtn = util.format('op=/move/%s/%s', src.entryUrl(), dest.entryUrl());
+      break;
+    case 'copy':
+      rtn = util.format('op=/copy/%s/%s', src.entryUrl(), dest.entryUrl());
+      break;
+    case 'delete':
+      rtn = util.format('op=/delete/%s', src.entryUrl());
+      break;
+  }
+
+  return rtn;
+}
+
+function noop() {
+  return false;
 }
\ No newline at end of file
diff --git a/lib/bucket.js b/lib/bucket.js
index 830587d..5a20475 100644
--- a/lib/bucket.js
+++ b/lib/bucket.js
@@ -2,11 +2,11 @@ var request    = require('request');
 var _          = require('underscore');
 var fs         = require('fs');
 var path       = require('path');
-var Q          = require('q');
-var dataStream = require('dataStream');
+var Promise    = require('bluebird');
 var util       = require('util');
 var url        = require('url');
 var path       = require('path');
+var stream     = require('stream');
 var Asset      = require('./asset');
 var Image      = require('./image');
 var token      = null;
@@ -53,58 +53,58 @@ function Bucket(bucketName, config) {
  * @return {Promise}           Promise object
  */
 Bucket.prototype.putFile = function(key, pathname, options, callback) {
+  var self = this
+  var args = [].slice.apply(arguments)
   // token
-  var deferred = Q.defer();
-
-  switch (arguments.length) {
-    case 3:
-      if (_.isFunction(options)) {
-        callback = options;
+  return new Promise(function(resolve, reject) {
+    switch (args.length) {
+      case 3:
+        if (_.isFunction(options)) {
+          callback = options;
+          options = {};
+        }
+        break;
+      case 2:
         options = {};
-      }
-      break;
-    case 2:
-      options = {};
-      callback = noop;
-      break;
-  }
+        callback = noop;
+        break;
+    }
 
-  var config = _.extend(_.clone(this.config), options);
+    var config = _.extend(_.clone(self.config), options);
 
-  var putToken = (new token.PutPolicy(config)).token();
+    var putToken = (new token.PutPolicy(config)).token();
 
-  // safe pathname
-  pathname = path.resolve(process.cwd(), pathname);
-
-  // upload API
-  var uploadUrl = url.format({
-    protocol: 'http',
-    hostname: globalConfig.uploadUrl
-  });
+    // safe pathname
+    pathname = path.resolve(process.cwd(), pathname);
 
-  // uploading
-  var req = request.post(uploadUrl, function(err, res, body) {
-    var result = JSON.parse(body);
+    // upload API
+    var uploadUrl = url.format({
+      protocol: 'http',
+      hostname: globalConfig.uploadUrl
+    });
 
-    if (err) {
-      deferred.reject(err);
-      return callback(err, result);
-    }
+    // uploading
+    var req = request.post(uploadUrl, function(err, res, body) {
+      var result = JSON.parse(body);
 
-    deferred.resolve(result);
-    callback(null, result);
-  });
+      if (err) {
+        reject(err);
+        return callback(err, result);
+      }
 
-  // form data
-  var form = req.form();
+      resolve(result);
+      callback(null, result);
+    });
 
-  form.append('token', putToken);
-  form.append('key', key);
-  form.append('file', fs.createReadStream(pathname), {
-    filename: path.basename(pathname)
-  });
+    // form data
+    var form = req.form();
 
-  return deferred.promise;
+    form.append('token', putToken);
+    form.append('key', key);
+    form.append('file', fs.createReadStream(pathname), {
+      filename: path.basename(pathname)
+    });
+  })
 };
 
 /**
@@ -122,43 +122,47 @@ Bucket.prototype.createPutStream = function(key, options) {
   // token
   var putToken = (new token.PutPolicy(config)).token();
 
-  var stream = new dataStream();
+  var _stream = new stream.Transform();
 
+  _stream._transform = function(chunk, encoding, cb) {
+    cb(null);
+  };
+  
   // upload API
   var uploadUrl = url.format({
     protocol: 'http',
     hostname: globalConfig.uploadUrl
   });
 
-  // uploading
-  var req = request.post(uploadUrl, function(err, res, body) {
-    var result = JSON.parse(body);
-
-    if (err) {
-      return stream.emit('error', err);
-    }
-
-    stream.emit('end', result);
-    stream.emit('complete', result);
-  });
-
-  // form data
-  var form = req.form();
-
-  form.append('token', putToken);
-  form.append('key', key);
-  stream.on('pipe', function(src) {
+  _stream.on('pipe', function(src) {
     if (src.path) {
       var filename = path.basename(src.path);
     } else {
       var filename = key;
     }
-    form.append('file', stream, {
+
+    // uploading
+    var req = request.post(uploadUrl, function(err, res, body) {
+      var result = JSON.parse(body);
+
+      if (err) {
+        return _stream.emit('error', err);
+      }
+
+      _stream.emit('end', result);
+    });
+
+    // form data
+    var form = req.form();
+
+    form.append('token', putToken);
+    form.append('key', key);
+    form.append('file', src, {
       filename: filename
     });
   });
 
-  return stream;
+  return _stream;
 };
 
 /**
@@ -170,21 +174,19 @@ Bucket.prototype.createPutStream = function(key, options) {
 Bucket.prototype.getFile = function(key, callback) {
   // token
   var getToken = this.key(key).token();
-  var deferred = Q.defer();
-  callback = callback || noop;
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
 
-  var res = request(getToken.url);
-  var recive = new dataStream();
-  res.pipe(recive)
-    .on('error', deferred.reject.bind(deferred))
-    .on('complete', function() {
-      var data = this.body();
-
-      deferred.resolve(data);
-      callback(null, data);
-    });
+    request(getToken.url, function(err, res, body) {
+      if (err) {
+        callback(err)
+        return reject(err)
+      }
 
-  return deferred.promise;
+      callback(null, body)
+      resolve(body)
+    });
+  })
 };
 
 /**
@@ -202,6 +204,7 @@ Bucket.prototype.createGetStream = function(key) {
 };
 
 Bucket.prototype.url = function() {
+  if (this.config.url) return this.config.url;
   return util.format('%s.qiniudn.com', this.name);
 };
 
diff --git a/lib/config.js b/lib/config.js
index cb0ff9a..ebd92de 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -1,5 +1,6 @@
-module.exports = {
-  uploadUrl : 'up.qiniu.com',
-  rsUrl     : 'rs.qbox.me',
-  rsfUrl    : 'rsf.qbox.me'
+module.exports = {
+  uploadUrl : 'upload.qiniu.com',
+  rsUrl     : 'rs.qbox.me',
+  rsfUrl    : 'rsf.qbox.me',
+  vipUrl    : 'iovip.qbox.me'
 };
\ No newline at end of file
diff --git a/lib/fop.js b/lib/fop.js
index 2c36f7c..4ea5908 100644
--- a/lib/fop.js
+++ b/lib/fop.js
@@ -2,8 +2,7 @@ var request    = require('request');
 var _          = require('underscore');
 var fs         = require('fs');
 var path       = require('path');
-var Q          = require('q');
-var dataStream = require('dataStream');
+var Promise    = require('bluebird');
 var util       = require('util');
 var url        = require('url');
 var Asset      = require('./asset');
@@ -223,24 +222,20 @@ Fop.prototype.token = function() {
  * @return {Promise}            promise object
  */
 Fop.prototype.exec = function(callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var recive = new dataStream();
-  request(this.url())
-    .pipe(recive)
-    .on('error', function(err) {
-      deferred.reject(err);
-      callback(err);
-    })
-    .on('complete', function() {
-      var data = this.body();
-
-      deferred.resolve(data);
-      callback(null, data);
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
+
+    request(self.url(), function(err, resp, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve(body);
+      callback(null, body);
     });
-
-  return deferred.promise;
+  })
 };
 
 /**
@@ -248,10 +243,7 @@ Fop.prototype.exec = function(callback) {
  * @return {Stream} stream
  */
 Fop.prototype.stream = function() {
-  var recive = new dataStream();
-  request(this.url()).pipe(recive);
-
-  return recive;
+  return request(this.url());
 };
 
 module.exports = Fop;
diff --git a/lib/image.js b/lib/image.js
index 2beebd9..efe8024 100644
--- a/lib/image.js
+++ b/lib/image.js
@@ -1,268 +1,260 @@
-var request    = require('request');
-var _          = require('underscore');
-var fs         = require('fs');
-var path       = require('path');
-var Q          = require('q');
-var dataStream = require('dataStream');
-var util       = require('util');
-var url        = require('url');
-var Asset      = require('./asset');
-var utils      = require('./utils');
-
-/**
- * Image Asset
- * @param {String} key    key
- * @param {Bucket} parent bucket object
- */
-function Image(key, parent, _config) {
-  Image.super_.apply(this, arguments);
-
-  var config = _.extend(_.clone(parent.config), {
-    separate: '-'
-  }, _config);
-
-  this.key = key;
-  this.parent = parent;
-  this.config = config;
-}
-util.inherits(Image, Asset);
-
-/**
- * get the image's infomations
- * @param  {Function} callback Callback
- * @return {Promise}           promise object
- */
-Image.prototype.imageInfo = function(callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var getToken = this.token({
-    fop: 'imageInfo'
-  });
-
-  var _url = getToken.url + '&token=' + getToken.token;
-
-  request(_url, function(err, res, body) {
-    if (err) {
-      deferred.reject(err);
-      return callback(err);
-    }
-
-    var info = JSON.parse(body);
-
-    deferred.resolve(info);
-    callback(null, info);
-  });
-
-  return deferred.promise;
-};
-
-/**
- * get the exif infomation of the picture
- * @param  {Function} callback Callback
- * @return {Promise}           promise object
- */
-Image.prototype.exif = function(callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var getToken = this.token({
-    fop: 'exif'
-  });
-
-  var _url = getToken.url + '&token=' + getToken.token;
-
-  request(_url, function(err, res, body) {
-    if (err) {
-      deferred.reject(err);
-      return callback(err);
-    }
-
-    var info = JSON.parse(body);
-
-    deferred.resolve(info);
-    callback(null, info);
-  });
-
-  return deferred.promise;
-};
-
-var imageViewTranslations = {
-  weight: 'w',
-  height: 'h',
-  quality: 'q'
-};
-
-/**
- * return a thumbnail image
- * @param  {Object}   opts     options
- * @param  {Function} callback Callback
- * @return {Promise}           promise object
- */
-Image.prototype.imageView = function(opts, callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var mode = opts.mode;
-  delete opts.mode;
-
-  var params = {};
-
-  _.each(opts, function(value, key) {
-    if (imageViewTranslations.hasOwnProperty(key)) {
-      key = imageViewTranslations[key];
-    }
-
-    params[key] = value;
-  });
-
-  var fop = util.format('imageView/%d%s', mode, genOptUrl(params));
-
-  var getToken = this.token({
-    fop: fop
-  });
-
-  var _url = getToken.url + '&token=' + getToken.token;
-
-  var recive = new dataStream();
-  request(_url).pipe(recive)
-    .on('complete', function() {
-      var data = this.body();
-
-      deferred.resolve(data);
-      callback(null, data);
-    })
-    .on('error', function(err) {
-      deferred.reject(err);
-      callback(err);
-    });
-
-
-  var promise = deferred.promise;
-
-  /**
-   * return the image stream
-   * @return {Stream} stream
-   */
-  promise.stream = function() {
-    return recive;
-  };
-
-  return promise;
-};
-
-/**
- * return a processed image
- * @param  {Object}   opts     options
- * @param  {Function} callback Callback
- * @return {Promise}           promise object
- */
-Image.prototype.imageMogr = function(opts, callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var params = {};
-
-  _.extend(params, opts);
-  
-  var fop = util.format('imageMogr/v2/auto-orient%s', genOptUrl(params));
-
-  var getToken = this.token({
-    fop: fop
-  });
-
-  var _url = getToken.url + '&token=' + getToken.token;
-
-  var recive = new dataStream();
-  request(_url).pipe(recive)
-    .on('complete', function() {
-      var data = this.body();
-
-      deferred.resolve(data);
-      callback(null, data);
-    })
-    .on('error', function(err) {
-      deferred.reject(err);
-      callback(err);
-    });
-
-  var promise = deferred.promise;
-
-  /**
-   * return the image stream
-   * @return {Stream} stream
-   */
-  promise.stream = function() {
-    return recive;
-  };
-
-  return promise;
-};
-
-/**
- * return a image with a watermark
- * @param  {Object}   opts     options
- * @param  {Function} callback Callback
- * @return {Promise}           promise object
- */
-Image.prototype.watermark = function(opts, callback) {
-  var deferred = Q.defer();
-  callback = callback || noop;
-
-  var params = {};
-  var mode = opts.mode;
-  delete opts.mode;
-
-  _.extend(params, opts);
-
-  params.image = utils.safeEncode(params.image);
-
-  var fop = util.format('watermark/%d%s', mode, genOptUrl(params));
-
-  var getToken = this.token({
-    fop: fop
-  });
-
-  var _url = getToken.url + '&token=' + getToken.token;
-
-  var recive = new dataStream();
-  request(_url).pipe(recive)
-    .on('complete', function() {
-      var data = this.body();
-
-      deferred.resolve(data);
-      callback(null, data);
-    })
-    .on('error', function(err) {
-      deferred.reject(err);
-      callback(err);
-    });
-
-  var promise = deferred.promise;
-
-  /**
-   * return the image stream
-   * @return {Stream} stream
-   */
-  promise.stream = function() {
-    return recive;
-  };
-
-  return promise;
-};
-
-module.exports = Image;
-
-function genOptUrl(params) {
-  var _url = "";
-
-  _.each(params, function(value, key) {
-    _url += util.format('/%s/%s', key, value);
-  });
-
-  return _url;
-}
-
-function noop() {
-  return false;
+var request    = require('request');
+var _          = require('underscore');
+var fs         = require('fs');
+var path       = require('path');
+var Promise    = require('bluebird');
+var stream     = require('stream');
+var util       = require('util');
+var url        = require('url');
+var Asset      = require('./asset');
+var utils      = require('./utils');
+
+/**
+ * Image Asset
+ * @param {String} key    key
+ * @param {Bucket} parent bucket object
+ */
+function Image(key, parent, _config) {
+  Image.super_.apply(this, arguments);
+
+  var config = _.extend(_.clone(parent.config), {
+    separate: '-'
+  }, _config);
+
+  this.key = key;
+  this.parent = parent;
+  this.config = config;
+}
+util.inherits(Image, Asset);
+
+/**
+ * get the image's infomations
+ * @param  {Function} callback Callback
+ * @return {Promise}           promise object
+ */
+Image.prototype.imageInfo = function(callback) {
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
+
+    var getToken = self.token({
+      fop: 'imageInfo'
+    });
+
+    var _url = getToken.url + '&token=' + getToken.token;
+
+    request(_url, function(err, res, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      var info = JSON.parse(body);
+
+      resolve(info);
+      callback(null, info);
+    });
+  })
+};
+
+/**
+ * get the exif infomation of the picture
+ * @param  {Function} callback Callback
+ * @return {Promise}           promise object
+ */
+Image.prototype.exif = function(callback) {
+  var self = this
+  return new Promise(function(resolve, reject) {
+    callback = callback || noop;
+
+    var getToken = self.token({
+      fop: 'exif'
+    });
+
+    var _url = getToken.url + '&token=' + getToken.token;
+
+    request(_url, function(err, res, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      var info = JSON.parse(body);
+
+      resolve(info);
+      callback(null, info);
+    });
+  })
+};
+
+var imageViewTranslations = {
+  weight: 'w',
+  height: 'h',
+  quality: 'q'
+};
+
+/**
+ * return a thumbnail image
+ * @param  {Object}   opts     options
+ * @param  {Function} callback Callback
+ * @return {Promise}           promise object
+ */
+Image.prototype.imageView = function(opts, callback) {
+  var self = this
+  var recive = null
+  var promise = new Promise(function(resolve, reject) {
+    callback = callback || noop;
+
+    var mode = opts.mode;
+    delete opts.mode;
+
+    var params = {};
+
+    _.each(opts, function(value, key) {
+      if (imageViewTranslations.hasOwnProperty(key)) {
+        key = imageViewTranslations[key];
+      }
+
+      params[key] = value;
+    });
+
+    var fop = util.format('imageView/%d%s', mode, genOptUrl(params));
+
+    var getToken = self.token({
+      fop: fop
+    });
+
+    var _url = getToken.url + '&token=' + getToken.token;
+
+    recive = request(_url, function(err, resp, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve(body);
+      callback(null, body);
+    });
+  })
+
+  /**
+   * return the image stream
+   * @return {Stream} stream
+   */
+  promise.stream = function() {
+    return recive;
+  };
+
+  return promise;
+};
+
+/**
+ * return a processed image
+ * @param  {Object}   opts     options
+ * @param  {Function} callback Callback
+ * @return {Promise}           promise object
+ */
+Image.prototype.imageMogr = function(opts, callback) {
+  var self = this
+  var recive = null
+  var promise = new Promise(function(resolve, reject) {
+    callback = callback || noop;
+
+    var params = {};
+
+    _.extend(params, opts);
+    
+    var fop = util.format('imageMogr/v2/auto-orient%s', genOptUrl(params));
+
+    var getToken = self.token({
+      fop: fop
+    });
+
+    var _url = getToken.url + '&token=' + getToken.token;
+
+    recive = request(_url, function(err, resp, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve(body);
+      callback(null, body);
+    });
+  })
+
+  /**
+   * return the image stream
+   * @return {Stream} stream
+   */
+  promise.stream = function() {
+    return recive;
+  };
+
+  return promise;
+};
+
+/**
+ * return a image with a watermark
+ * @param  {Object}   opts     options
+ * @param  {Function} callback Callback
+ * @return {Promise}           promise object
+ */
+Image.prototype.watermark = function(opts, callback) {
+  var self = this
+  var recive = null
+  var promise = new Promise(function(resolve, reject) {
+    callback = callback || noop;
+
+    var params = {};
+    var mode = opts.mode;
+    delete opts.mode;
+
+    _.extend(params, opts);
+
+    params.image = utils.safeEncode(params.image);
+
+    var fop = util.format('watermark/%d%s', mode, genOptUrl(params));
+
+    var getToken = self.token({
+      fop: fop
+    });
+
+    var _url = getToken.url + '&token=' + getToken.token;
+
+    recive = request(_url, function(err, resp, body) {
+      if (err) {
+        reject(err);
+        return callback(err);
+      }
+
+      resolve(body);
+      callback(null, body);
+    });
+  })
+
+  /**
+   * return the image stream
+   * @return {Stream} stream
+   */
+  promise.stream = function() {
+    return recive;
+  };
+  return promise;
+};
+
+module.exports = Image;
+
+function genOptUrl(params) {
+  var _url = "";
+
+  _.each(params, function(value, key) {
+    _url += util.format('/%s/%s', key, value);
+  });
+
+  return _url;
+}
+
+function noop() {
+  return false;
 }
\ No newline at end of file
diff --git a/lib/qiniu.js b/lib/qiniu.js
index ace7e03..c849cf2 100644
--- a/lib/qiniu.js
+++ b/lib/qiniu.js
@@ -2,7 +2,7 @@ var _ = require('underscore');
 
 var qiniu = exports;
 
-qiniu.version = '6.1.5';
+qiniu.version = '6.1.7';
 
 var _configData = require('./config');
 
diff --git a/lib/token.js b/lib/token.js
index d228b21..29a2067 100644
--- a/lib/token.js
+++ b/lib/token.js
@@ -1,115 +1,115 @@
-/**
- * Qiniu Token generic
- */
-
-var _     = require('underscore');
-var util  = require('util');
-var url   = require('url');
-var qs    = require('querystring');
-var utils = require('./utils');
-
-var globalConfig = null;
-
-/**
- * Put Policy
- * @param {Object} opts options
- */
-function PutPolicy(opts) {
-  var now = Math.floor(Date.now() / 1000);
-
-  this.scope        = opts.scope        || null;
-  this.deadline     = now + opts.deadline || now + 3600;
-  this.endUser      = opts.endUser      || null;
-  this.returnUrl    = opts.returnUrl    || null;
-  this.returnBody   = opts.returnBody   || null;
-  this.callbackUrl  = opts.callbackUrl  || null;
-  this.callbackBody = opts.callbackBody || null;
-  this.asyncOps     = opts.asyncOps     || null;
-}
-
-/**
- * Generate the token
- * @return {String} token
- */
-PutPolicy.prototype.token = function() {
-  var params = {};
-
-  _.each(this, function(value, key) {
-    if (!_.isNull(value)) {
-      params[key] = value;
-    }
-  });
-  return generateToken(params);
-};
-
-function generateToken(params) {
-  // signature
-  var signature = utils.safeEncode(JSON.stringify(params));
-
-  // EncodedDigest
-  var encodedDigest = utils.encodeSign(signature, globalConfig.secret_key);
-
-  return util.format('%s:%s:%s', globalConfig.access_key, encodedDigest, signature)
-}
-
-function GetPolicy(opts) {
-  opts = opts || {};
-  var now = Math.floor(Date.now() / 1000);
-
-  this.deadline = now + opts.deadline || now + 3600;
-}
-
-GetPolicy.prototype.token = function(domain, key, opts) {
-  opts = opts || {};
-  var fop = opts.fop || 'e';
-  var base = '';
-
-  if (_.isUndefined(key)) {
-    // base url
-    var encodedDigest = utils.encodeSign(domain, globalConfig.secret_key);
-  } else {
-    // safe domain
-    var hostname = url.parse(domain).hostname;
-
-    base = url.format({
-      protocol: 'http',
-      hostname: hostname,
-      pathname: key
-    });
-
-    base += '?' + encodeURIComponent(fop);
-    base += '&' + qs.stringify({
-      e: this.deadline
-    });
-
-    var encodedDigest = utils.encodeSign(base, globalConfig.secret_key);
-  }
-
-  var token = util.format('%s:%s', globalConfig.access_key, encodedDigest);
-
-  return {
-    token: token,
-    requestUrl: base,
-    url: base + '&token=' + token
-  };
-};
-
-function AccessToken() {}
-
-AccessToken.prototype.token = function(path, body) {
-  body = body || '';
-
-  var data = util.format('%s\n%s', path, body);
-
-  var encodeSignData = utils.encodeSign(data, globalConfig.secret_key);
-
-  return util.format('QBox %s:%s', globalConfig.access_key, encodeSignData);
-};
-module.exports = function(config) {
-  globalConfig = config;
-  return {
-    PutPolicy: PutPolicy,
-    GetPolicy: GetPolicy,
-    AccessToken: AccessToken
-  };
+/**
+ * Qiniu Token generic
+ */
+
+var _     = require('underscore');
+var util  = require('util');
+var url   = require('url');
+var qs    = require('querystring');
+var utils = require('./utils');
+
+var globalConfig = null;
+
+/**
+ * Put Policy
+ * @param {Object} opts options
+ */
+function PutPolicy(opts) {
+  var now = Math.floor(Date.now() / 1000);
+
+  this.scope        = opts.scope        || null;
+  this.deadline     = now + opts.deadline || now + 3600;
+  this.endUser      = opts.endUser      || null;
+  this.returnUrl    = opts.returnUrl    || null;
+  this.returnBody   = opts.returnBody   || null;
+  this.callbackUrl  = opts.callbackUrl  || null;
+  this.callbackBody = opts.callbackBody || null;
+  this.asyncOps     = opts.asyncOps     || null;
+}
+
+/**
+ * Generate the token
+ * @return {String} token
+ */
+PutPolicy.prototype.token = function() {
+  var params = {};
+
+  _.each(this, function(value, key) {
+    if (!_.isNull(value)) {
+      params[key] = value;
+    }
+  });
+  return generateToken(params);
+};
+
+function generateToken(params) {
+  // signature
+  var signature = utils.safeEncode(JSON.stringify(params));
+
+  // EncodedDigest
+  var encodedDigest = utils.encodeSign(signature, globalConfig.secret_key);
+
+  return util.format('%s:%s:%s', globalConfig.access_key, encodedDigest, signature)
+}
+
+function GetPolicy(opts) {
+  opts = opts || {};
+  var now = Math.floor(Date.now() / 1000);
+
+  this.deadline = now + opts.deadline || now + 3600;
+}
+
+GetPolicy.prototype.token = function(domain, key, opts) {
+  opts = opts || {};
+  var fop = opts.fop || 'e';
+  var base = '';
+
+  if (_.isUndefined(key)) {
+    // base url
+    var encodedDigest = utils.encodeSign(domain, globalConfig.secret_key);
+  } else {
+    // safe domain
+    var hostname = url.parse(domain).hostname;
+
+    base = url.format({
+      protocol: 'http',
+      hostname: hostname,
+      pathname: key
+    });
+
+    base += '?' + encodeURIComponent(fop);
+    base += '&' + qs.stringify({
+      e: this.deadline
+    });
+
+    var encodedDigest = utils.encodeSign(base, globalConfig.secret_key);
+  }
+
+  var token = util.format('%s:%s', globalConfig.access_key, encodedDigest);
+
+  return {
+    token: token,
+    requestUrl: base,
+    url: base + '&token=' + token
+  };
+};
+
+function AccessToken() {}
+
+AccessToken.prototype.token = function(path, body) {
+  body = body || '';
+
+  var data = util.format('%s\n%s', path, body);
+
+  var encodeSignData = utils.encodeSign(data, globalConfig.secret_key);
+
+  return util.format('QBox %s:%s', globalConfig.access_key, encodeSignData);
+};
+module.exports = function(config) {
+  globalConfig = config;
+  return {
+    PutPolicy: PutPolicy,
+    GetPolicy: GetPolicy,
+    AccessToken: AccessToken
+  };
 };
\ No newline at end of file
diff --git a/lib/utils.js b/lib/utils.js
index c1b75ef..2dab1b4 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -1,20 +1,20 @@
-var crypto = require('crypto');
-var fs = require('fs');
-
-var utils = exports;
-
-utils.safeEncode = function(str) {
-  var encoded = new Buffer(str).toString('base64');
-  var rtn = encoded.replace(/\//g, '_').replace(/\+/g, '-');
-
-  return rtn;
-};
-
-utils.encodeSign = function(str, key) {
-  return utils.safeEncode(
-    crypto
-      .createHmac('sha1', key)
-      .update(str)
-      .digest()
-  );
+var crypto = require('crypto');
+var fs = require('fs');
+
+var utils = exports;
+
+utils.safeEncode = function(str) {
+  var encoded = new Buffer(str).toString('base64');
+  var rtn = encoded.replace(/\//g, '_').replace(/\+/g, '-');
+
+  return rtn;
+};
+
+utils.encodeSign = function(str, key) {
+  return utils.safeEncode(
+    crypto
+      .createHmac('sha1', key)
+      .update(str)
+      .digest()
+  );
 };
\ No newline at end of file
diff --git a/package.json b/package.json
index 597ff52..4153177 100644
--- a/package.json
+++ b/package.json
@@ -1,52 +1,53 @@
-{
-  "name": "node-qiniu",
-  "version": "6.1.5",
-  "description": "Qiniu Resource Storage SDK for Node.js",
-  "main": "index.js",
-  "directories": {
-    "doc": "doc",
-    "test": "test"
-  },
-  "scripts": {
-    "test": "make test",
-    "blanket": {
-      "pattern": "node-qiniu/lib",
-      "data-cover-flags": {
-        "debug": false
-      }
-    },
-    "travis-cov": {
-      "threshold": 88
-    }
-  },
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/iwillwen/nodejs-sdk"
-  },
-  "keywords": [
-    "cloud",
-    "storage",
-    "s3",
-    "qiniu",
-    "web-service"
-  ],
-  "author": "iwillwen",
-  "license": "MIT",
-  "bugs": {
-    "url": "https://github.com/iwillwen/nodejs-sdk/issues"
-  },
-  "dependencies": {
-    "underscore": "~1.5.1",
-    "request": "~2.27.0",
-    "q": "~0.9.6",
-    "dataStream": "0.0.5"
-  },
-  "devDependencies": {
-    "async": "~0.2.9",
-    "mocha": "~1.6.0",
-    "should": "~1.2.2",
-    "doxmate": "*",
-    "blanket": "*",
-    "travis-cov": "*"
-  }
-}
+{
+  "name": "node-qiniu",
+  "version": "6.2.0",
+  "description": "Qiniu Resource Storage SDK for Node.js",
+  "main": "index.js",
+  "directories": {
+    "doc": "doc",
+    "test": "test"
+  },
+  "scripts": {
+    "test": "make test",
+    "blanket": {
+      "pattern": "node-qiniu/lib",
+      "data-cover-flags": {
+        "debug": false
+      }
+    },
+    "travis-cov": {
+      "threshold": 88
+    }
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://www.npmjs.org/package/node-qiniu"
+  },
+  "keywords": [
+    "cloud",
+    "storage",
+    "s3",
+    "qiniu",
+    "web-service"
+  ],
+  "author": "iwillwen",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://www.npmjs.org/package/node-qiniu/issues"
+  },
+  "dependencies": {
+    "bluebird": "^2.10.2",
+    "bufferhelper": "^0.2.0",
+    "q": "~0.9.6",
+    "request": "~2.27.0",
+    "underscore": "~1.5.1"
+  },
+  "devDependencies": {
+    "async": "~0.2.9",
+    "mocha": "~1.6.0",
+    "should": "~1.2.2",
+    "doxmate": "*",
+    "blanket": "*",
+    "travis-cov": "*"
+  }
+}
diff --git a/test/index.js b/test/index.js
index 7eb0c65..bd5438f 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,11 +1,10 @@
-var qiniu = require('../');
-
-qiniu.config({
-  access_key: '5UyUq-l6jsWqZMU6tuQ85Msehrs3Dr58G-mCZ9rE',
-  secret_key: 'YaRsPKiYm4nGUt8mdz2QxeV5Q_yaUzVxagRuWTfM'
-
-});
-
-qiniu.testBucket = new qiniu.Bucket('qiniu-sdk-test');
-
-module.exports = qiniu;
+var qiniu = require('../');
+
+qiniu.config({
+  access_key: '5UyUq-l6jsWqZMU6tuQ85Msehrs3Dr58G-mCZ9rE',
+  secret_key: 'YaRsPKiYm4nGUt8mdz2QxeV5Q_yaUzVxagRuWTfM'
+});
+
+qiniu.testBucket = new qiniu.Bucket('qiniu-sdk-test');
+
+module.exports = qiniu;
diff --git a/test/qiniu_asset.js b/test/qiniu_asset.js
index 47b929b..e9edb63 100644
--- a/test/qiniu_asset.js
+++ b/test/qiniu_asset.js
@@ -10,7 +10,7 @@ describe('qiniu.Asset', function() {
     it('should return the url of the asset', function() {
       var url = asset.url();
 
-      assert.equal(url, 'http://qiniu-sdk-test.u.qiniudn.com/gogopher.jpg');
+      assert.equal(url, 'http://qiniu-sdk-test.qiniudn.com/gogopher.jpg');
     });
   });
 
diff --git a/test/qiniu_batch.js b/test/qiniu_batch.js
index 2451a86..45a23b7 100644
--- a/test/qiniu_batch.js
+++ b/test/qiniu_batch.js
@@ -1,64 +1,64 @@
-var qiniu      = require('./');
-var assert     = require('assert');
-
-var bucket = qiniu.testBucket;
-
-describe('qiniu.Batch', function() {
-
-  describe('Batch.stat()', function() {
-    it('should get the assets\' stats', function(done) {
-      qiniu.batch()
-        .stat(bucket.key('gogopher.jpg'))
-        .exec(function(err, stat) {
-          if (err) {
-            throw err;
-          }
-
-          done();
-        });
-    });
-  });
-
-  describe('Batch.move()', function() {
-    it('should move the asset to another position', function(done) {
-      qiniu.batch()
-        .move(bucket.key('gogopher.jpg'), bucket.key('gogopher_tmp.jpg'))
-        .exec(function(err) {
-          if (err) {
-            throw err;
-          }
-
-          done();
-        });
-    });
-  });
-
-  describe('Batch.copy()', function() {
-    it('should make some copies of the assets to the other positions', function(done) {
-      qiniu.batch()
-        .move(bucket.key('gogopher_tmp.jpg'), bucket.key('gogopher.jpg'))
-        .exec(function(err) {
-          if (err) {
-            throw err;
-          }
-
-          done();
-        });
-    });
-  });
-
-  describe('Batch.remove()', function() {
-    it('should delete the assets', function(done) {
-      qiniu.batch()
-        .remove(bucket.key('gogopher_tmp.jpg'))
-        .exec(function(err, stat) {
-          if (err) {
-            throw err;
-          }
-
-          done();
-        });
-    });
-  });
-
+var qiniu      = require('./');
+var assert     = require('assert');
+
+var bucket = qiniu.testBucket;
+
+describe('qiniu.Batch', function() {
+
+  describe('Batch.stat()', function() {
+    it('should get the assets\' stats', function(done) {
+      qiniu.batch()
+        .stat(bucket.key('gogopher.jpg'))
+        .exec(function(err, stat) {
+          if (err) {
+            throw err;
+          }
+
+          done();
+        });
+    });
+  });
+
+  describe('Batch.move()', function() {
+    it('should move the asset to another position', function(done) {
+      qiniu.batch()
+        .move(bucket.key('gogopher.jpg'), bucket.key('gogopher_tmp.jpg'))
+        .exec(function(err) {
+          if (err) {
+            throw err;
+          }
+
+          done();
+        });
+    });
+  });
+
+  describe('Batch.copy()', function() {
+    it('should make some copies of the assets to the other positions', function(done) {
+      qiniu.batch()
+        .move(bucket.key('gogopher_tmp.jpg'), bucket.key('gogopher.jpg'))
+        .exec(function(err) {
+          if (err) {
+            throw err;
+          }
+
+          done();
+        });
+    });
+  });
+
+  describe('Batch.remove()', function() {
+    it('should delete the assets', function(done) {
+      qiniu.batch()
+        .remove(bucket.key('gogopher_tmp.jpg'))
+        .exec(function(err, stat) {
+          if (err) {
+            throw err;
+          }
+
+          done();
+        });
+    });
+  });
+
 });
\ No newline at end of file
diff --git a/test/qiniu_fop.js b/test/qiniu_fop.js
index 07d36dc..f355496 100644
--- a/test/qiniu_fop.js
+++ b/test/qiniu_fop.js
@@ -1,6 +1,5 @@
 var qiniu      = require('./');
 var fs         = require('fs');
-var dataStream = require('dataStream');
 var assert     = require('assert');
 
 var asset = qiniu.testBucket.key('gogopher.jpg');
diff --git a/test/qiniu_get.js b/test/qiniu_get.js
index 1a3827b..d41c4e3 100644
--- a/test/qiniu_get.js
+++ b/test/qiniu_get.js
@@ -1,35 +1,35 @@
-var qiniu  = require('./');
-var fs     = require('fs');
-var assert = require('assert');
-
-describe('qiniu.Get', function() {
-  
-  describe('Bucket.getFile()', function() {
-    it('should gets a file with giving a pathname', function(done) {
-      
-      qiniu.testBucket.getFile('gogopher.jpg', function(err) {
-        if (err) {
-          throw err;
-        }
-
-        done();
-      });
-
-    });
-  });
-
-  describe('Bucket.createGetStream()', function() {
-    it('should gets a file with a stream', function(done) {
-      
-      var gettingStream = qiniu.testBucket.createGetStream('gogopher.jpg');
-      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_tmp.jpg');
-
-      gettingStream.pipe(writingStream)
-        .on('error', function(err) {
-          throw err;
-        })
-        .on('finish', done);
-    });
-  });
-
+var qiniu  = require('./');
+var fs     = require('fs');
+var assert = require('assert');
+
+describe('qiniu.Get', function() {
+  
+  describe('Bucket.getFile()', function() {
+    it('should gets a file with giving a pathname', function(done) {
+      
+      qiniu.testBucket.getFile('gogopher.jpg', function(err) {
+        if (err) {
+          throw err;
+        }
+
+        done();
+      });
+
+    });
+  });
+
+  describe('Bucket.createGetStream()', function() {
+    it('should gets a file with a stream', function(done) {
+      
+      var gettingStream = qiniu.testBucket.createGetStream('gogopher.jpg');
+      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_tmp.jpg');
+
+      gettingStream.pipe(writingStream)
+        .on('error', function(err) {
+          throw err;
+        })
+        .on('finish', done);
+    });
+  });
+
 });
\ No newline at end of file
diff --git a/test/qiniu_image.js b/test/qiniu_image.js
index 096fa1f..71ac2c6 100644
--- a/test/qiniu_image.js
+++ b/test/qiniu_image.js
@@ -1,217 +1,216 @@
-var qiniu      = require('./');
-var fs         = require('fs');
-var dataStream = require('dataStream');
-var assert     = require('assert');
-
-var image = qiniu.testBucket.image('gogopher.jpg');
-
-describe('qiniu.Image', function() {
-  
-  describe('Image.imageInfo()', function() {
-    it('should return some infomations of a picture', function(done) {
-      
-      image.imageInfo(function(err, data) {
-        if (err) {
-          throw err;
-        }
-
-        done();
-      });
-
-    });
-  });
-
-  describe('Image.exif()', function() {
-    it('should return the exif infomation of a picture', function(done) {
-      
-      image.exif(function(err) {
-        if (err) {
-          throw err;
-        }
-
-        done();
-      });
-
-    });
-  });
-
-  describe('Image.imageView()', function() {
-    it('should return a thumbnail image', function(done) {
-      
-      // imageView
-      image.imageView({
-        mode    : 2,
-        width   : 180,
-        height  : 180,
-        quality : 85,
-        format  : 'jpg'
-      }, function(err, thumbnail) {
-        if (err) {
-          throw err;
-        }
-
-        fs.writeFile(__dirname + '/assets/gogopher_thumbnail.jpg', thumbnail, function(err) {
-          done();
-        });
-      });
-
-    });
-  });
-
-  describe('Image.imageView().straem()', function() {
-    it('should return a thumbnail image by a stream', function(done) {
-      
-      // imageView Stream
-      var imageStream = image.imageView({
-        mode    : 2,
-        width   : 180,
-        height  : 180,
-        quality : 85,
-        format  : 'jpg'
-      }).stream();
-      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_thumbnail.jpg');
-
-      imageStream.pipe(writingStream)
-        .on('error', function(err) {
-          throw err;
-        })
-        .on('finish', function() {
-          done();
-        });
-    });
-  });
-
-  describe('Image.imageMogr()', function() {
-    it('should return a processed image', function(done) {
-      
-      // imageMogr
-      image.imageMogr({
-        thumbnail : '300x500',
-        gravity   : 'NorthWest',
-        crop      : '!300x400a10a10',
-        quality   : 85,
-        rotate    : 90,
-        format    : 'jpg'
-      }, function(err, mogred) {
-        if (err) {
-          throw err;
-        }
-
-        fs.writeFile(__dirname + '/assets/gogopher_mogred.jpg', mogred, function(err) {
-          done();
-        });
-      });
-
-    });
-  });
-
-  describe('Image.imageMogr().stream()', function() {
-    it('should return a processed image by a stream', function(done) {
-
-      // imageMogr
-      var imageStream = image.imageMogr({
-        thumbnail : '300x500',
-        gravity   : 'NorthWest',
-        crop      : '!300x400a10a10',
-        quality   : 85,
-        rotate    : 90,
-        format    : 'jpg'
-      }).stream();
-      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_mogred.jpg');
-
-      imageStream.pipe(writingStream)
-        .on('error', function(err) {
-          throw err;
-        })
-        .on('finish', function() {
-          done();
-        });
-
-    });
-  });
-
-  describe('Image.watermark()', function() {
-    it('should return a image with a watermark', function(done) {
-      
-      // watermarks
-      image.watermark({
-        mode: 1,
-        image: 'http://www.b1.qiniudn.com/images/logo-2.png',
-        dissolve: 70,
-        gravity: 'SouthEast',
-        dx: 20,
-        dy: 20
-      }, function(err, pic) {
-        if (err) {
-          throw err;
-        }
-
-        fs.writeFile(__dirname + '/assets/gogopher_watermark.jpg', pic, function(err) {
-          done();
-        });
-      });
-
-    });
-  });
-
-  describe('Image.watermark().stream()', function() {
-    it('should return a image with a watermark by stream', function(done) {
-      
-      // watermarks
-      var imageStream = image.watermark({
-        mode: 1,
-        image: 'http://www.b1.qiniudn.com/images/logo-2.png',
-        dissolve: 70,
-        gravity: 'SouthEast',
-        dx: 20,
-        dy: 20
-      }).stream();
-      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_watermark.jpg');
-
-      imageStream.pipe(writingStream)
-        .on('error', function(err) {
-          throw err;
-        })
-        .on('finish', function() {
-          done();
-        });
-
-    });
-  });
-
-  describe('Image.alias()', function() {
-    it('should return a image with a established format', function(done) {
-      
-      // watermarks
-      image.alias('testalias', function(err, pic) {
-        if (err) {
-          throw err;
-        }
-
-        fs.writeFile(__dirname + '/assets/gogopher_alias.jpg', pic, function(err) {
-          done();
-        });
-      });
-
-    });
-  });
-
-  describe('Image.alias().stream()', function() {
-    it('should return a image with a established format by stream', function(done) {
-      
-      // watermarks
-      var imageStream = image.alias('testalias').stream();
-      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_alias.jpg');
-
-      imageStream.pipe(writingStream)
-        .on('error', function(err) {
-          throw err;
-        })
-        .on('finish', function() {
-          done();
-        });
-
-    });
-  });
-
+var qiniu      = require('./');
+var fs         = require('fs');
+var assert     = require('assert');
+
+var image = qiniu.testBucket.image('gogopher.jpg');
+
+describe('qiniu.Image', function() {
+  
+  describe('Image.imageInfo()', function() {
+    it('should return some infomations of a picture', function(done) {
+      
+      image.imageInfo(function(err, data) {
+        if (err) {
+          throw err;
+        }
+
+        done();
+      });
+
+    });
+  });
+
+  describe('Image.exif()', function() {
+    it('should return the exif infomation of a picture', function(done) {
+      
+      image.exif(function(err) {
+        if (err) {
+          throw err;
+        }
+
+        done();
+      });
+
+    });
+  });
+
+  describe('Image.imageView()', function() {
+    it('should return a thumbnail image', function(done) {
+      
+      // imageView
+      image.imageView({
+        mode    : 2,
+        width   : 180,
+        height  : 180,
+        quality : 85,
+        format  : 'jpg'
+      }, function(err, thumbnail) {
+        if (err) {
+          throw err;
+        }
+
+        fs.writeFile(__dirname + '/assets/gogopher_thumbnail.jpg', thumbnail, function(err) {
+          done();
+        });
+      });
+
+    });
+  });
+
+  describe('Image.imageView().straem()', function() {
+    it('should return a thumbnail image by a stream', function(done) {
+      
+      // imageView Stream
+      var imageStream = image.imageView({
+        mode    : 2,
+        width   : 180,
+        height  : 180,
+        quality : 85,
+        format  : 'jpg'
+      }).stream();
+      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_thumbnail.jpg');
+
+      imageStream.pipe(writingStream)
+        .on('error', function(err) {
+          throw err;
+        })
+        .on('finish', function() {
+          done();
+        });
+    });
+  });
+
+  describe('Image.imageMogr()', function() {
+    it('should return a processed image', function(done) {
+      
+      // imageMogr
+      image.imageMogr({
+        thumbnail : '300x500',
+        gravity   : 'NorthWest',
+        crop      : '!300x400a10a10',
+        quality   : 85,
+        rotate    : 90,
+        format    : 'jpg'
+      }, function(err, mogred) {
+        if (err) {
+          throw err;
+        }
+
+        fs.writeFile(__dirname + '/assets/gogopher_mogred.jpg', mogred, function(err) {
+          done();
+        });
+      });
+
+    });
+  });
+
+  describe('Image.imageMogr().stream()', function() {
+    it('should return a processed image by a stream', function(done) {
+
+      // imageMogr
+      var imageStream = image.imageMogr({
+        thumbnail : '300x500',
+        gravity   : 'NorthWest',
+        crop      : '!300x400a10a10',
+        quality   : 85,
+        rotate    : 90,
+        format    : 'jpg'
+      }).stream();
+      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_mogred.jpg');
+
+      imageStream.pipe(writingStream)
+        .on('error', function(err) {
+          throw err;
+        })
+        .on('finish', function() {
+          done();
+        });
+
+    });
+  });
+
+  describe('Image.watermark()', function() {
+    it('should return a image with a watermark', function(done) {
+      
+      // watermarks
+      image.watermark({
+        mode: 1,
+        image: 'http://www.b1.qiniudn.com/images/logo-2.png',
+        dissolve: 70,
+        gravity: 'SouthEast',
+        dx: 20,
+        dy: 20
+      }, function(err, pic) {
+        if (err) {
+          throw err;
+        }
+
+        fs.writeFile(__dirname + '/assets/gogopher_watermark.jpg', pic, function(err) {
+          done();
+        });
+      });
+
+    });
+  });
+
+  describe('Image.watermark().stream()', function() {
+    it('should return a image with a watermark by stream', function(done) {
+      
+      // watermarks
+      var imageStream = image.watermark({
+        mode: 1,
+        image: 'http://www.b1.qiniudn.com/images/logo-2.png',
+        dissolve: 70,
+        gravity: 'SouthEast',
+        dx: 20,
+        dy: 20
+      }).stream();
+      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_watermark.jpg');
+
+      imageStream.pipe(writingStream)
+        .on('error', function(err) {
+          throw err;
+        })
+        .on('finish', function() {
+          done();
+        });
+
+    });
+  });
+
+  describe('Image.alias()', function() {
+    it('should return a image with a established format', function(done) {
+      
+      // watermarks
+      image.alias('testalias', function(err, pic) {
+        if (err) {
+          throw err;
+        }
+
+        fs.writeFile(__dirname + '/assets/gogopher_alias.jpg', pic, function(err) {
+          done();
+        });
+      });
+
+    });
+  });
+
+  describe('Image.alias().stream()', function() {
+    it('should return a image with a established format by stream', function(done) {
+      
+      // watermarks
+      var imageStream = image.alias('testalias').stream();
+      var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_alias.jpg');
+
+      imageStream.pipe(writingStream)
+        .on('error', function(err) {
+          throw err;
+        })
+        .on('finish', function() {
+          done();
+        });
+
+    });
+  });
+
 });
\ No newline at end of file
diff --git a/test/qiniu_put.js b/test/qiniu_put.js
index 15f4a13..c20e219 100644
--- a/test/qiniu_put.js
+++ b/test/qiniu_put.js
@@ -1,37 +1,37 @@
-var qiniu  = require('./');
-var fs     = require('fs');
-var assert = require('assert');
-
-describe('qiniu.Put', function() {
-  
-  describe('Bucket.putFile()', function() {
-    it('should uploads a file with giving a pathname', function(done) {
-      
-      qiniu.testBucket.putFile('gogopher.jpg', __dirname + '/assets/gogopher.jpg', function(err, reply) {
-        if (err) {
-          throw err;
-        }
-
-        done();
-      });
-
-    });
-  });
-
-  describe('Bucket.createPutStream()', function() {
-    it('should uploads a file with a stream', function(done) {
-      
-      var puttingStream = qiniu.testBucket.createPutStream('gogopher.jpg');
-      var readingStream = fs.createReadStream(__dirname + '/assets/gogopher.jpg');
-
-      readingStream.pipe(puttingStream)
-        .on('error', function(err) {
-          throw err;
-        })
-        .on('complete', function() {
-          done();
-        });
-    });
-  });
-
+var qiniu  = require('./');
+var fs     = require('fs');
+var assert = require('assert');
+
+describe('qiniu.Put', function() {
+  
+  describe('Bucket.putFile()', function() {
+    it('should uploads a file with giving a pathname', function(done) {
+      
+      qiniu.testBucket.putFile('gogopher.jpg', __dirname + '/assets/gogopher.jpg', function(err, reply) {
+        if (err) {
+          throw err;
+        }
+
+        done();
+      });
+
+    });
+  });
+
+  describe('Bucket.createPutStream()', function() {
+    it('should uploads a file with a stream', function(done) {
+      
+      var puttingStream = qiniu.testBucket.createPutStream('gogopher.jpg');
+      var readingStream = fs.createReadStream(__dirname + '/assets/gogopher.jpg');
+
+      readingStream.pipe(puttingStream)
+        .on('error', function(err) {
+          throw err;
+        })
+        .on('end', function() {
+          done();
+        });
+    });
+  });
+
 });
\ No newline at end of file