├── .gitignore ├── .npmignore ├── .travis.yml ├── Makefile ├── README.md ├── example.js ├── index.js ├── lib ├── asset.js ├── batch.js ├── bucket.js ├── config.js ├── fop.js ├── image.js ├── qiniu.js ├── token.js └── utils.js ├── package.json └── test ├── assets └── gogopher.jpg ├── index.js ├── qiniu_asset.js ├── qiniu_batch.js ├── qiniu_fop.js ├── qiniu_get.js ├── qiniu_image.js └── qiniu_put.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | doc 14 | test/assets/* 15 | 16 | node_modules 17 | coverage.html 18 | **.txt 19 | npm_debug.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | **.txt 16 | npm_debug.log 17 | .git -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.8" 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REPORTER = spec 2 | 3 | test: 4 | @NODE_ENV=test ./node_modules/.bin/mocha \ 5 | -t 40000 \ 6 | $(MOCHA_OPTS) \ 7 | -R $(REPORTER) 8 | 9 | test-cov: 10 | @rm -rf coverage.html 11 | @$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=html-cov > coverage.html 12 | @$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=travis-cov 13 | @ls -lh coverage.html 14 | 15 | doc: 16 | @mkdir -p doc 17 | @./node_modules/.bin/doxmate -i . -o ./doc 18 | 19 | .PHONY: test doc 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 七牛 Node.js SDK 2 | 3 | 该 SDK 适用于 Node.js 0.4.7 及其以上版本,基于 七牛云存储官方API 构建。 若您的服务端是一个基于 Node.js 编写的网络程序,使用此 SDK , 能让您以非常便捷地方式将数据安全地存储到七牛云存储上。 以便让您应用的终端用户进行高速上传和下载,同时也使得您的服务端更加轻盈。 4 | 5 | ## 安装 6 | 7 | 你可以从 npm 进行安装 8 | 9 | ```shell 10 | npm install node-qiniu 11 | ``` 12 | 13 | 也可以从 Github 进行下载安装 14 | 15 | ```shell 16 | $ git clone git://github.com/qiniu/node-qiniu 17 | $ cd node-qiniu 18 | $ npm install . 19 | ``` 20 | 21 | ## 测试 22 | 23 | 七牛 Node.js SDK 使用 Mocha 进行单元测试。 24 | 25 | ```shell 26 | $ npm install -g mocha 27 | $ make test 28 | ``` 29 | 30 | ## 使用 31 | 32 | ### 配置 `qiniu.config()` 33 | 34 | 设置全局参数,包括必须的 AccessKey 和 SecretKey,还可以设置其他如 CallbackURL 等参数,将会顺延至所有空间。 35 | 36 | ```js 37 | qiniu.config({ 38 | access_key: '------', 39 | secret_key: '------' 40 | }); 41 | ``` 42 | 43 | ### Bucket 44 | 45 | 获得空间对象并进行操作。 46 | 47 | ```js 48 | var imagesBucket = qiniu.bucket('qiniu-sdk-test'); 49 | // 也可以这样操作 50 | // var imagesBucket = new qiniu.Bucket('qiniu-sdk-test'); 51 | ``` 52 | 53 | #### 上传文件 54 | 55 | **1. `Bucket.putFile()`** 56 | 57 | 上传一个文件,参数为将要上传的 Key,文件地址(可以是绝对地址,也可以是相对地址),第三个为可选参数 options,即本次上传中所使用 PutToken 的特殊设置,第四个为可选参数回调(callback),若不传入回调函数,将由 putFile 函数所返回的 Promise 对象进行响应。 58 | 59 | ```js 60 | // 普通上传 61 | imagesBucket.putFile('exampleKey', __dirname + '/assets/example.jpg', function(err, reply) { 62 | if (err) { 63 | return console.error(err); 64 | } 65 | 66 | console.dir(reply); 67 | }); 68 | // 特殊参数 69 | imagesBucket.putFile('exampleKey_1', __dirname + '/assets/example.jpg', { 70 | // 为本次上传中所使用的 Token 进行设置,此处为设置上传者标识 71 | endUser: 'foobar' 72 | }, function(err, reply) { 73 | if (err) { 74 | return console.error(err); 75 | } 76 | 77 | console.dir(reply); 78 | }); 79 | // 七牛 Node.js SDK 所提供的 Promise 对象遵循 Promise/A(+) 标准,使用 .then 方法进行响应 80 | imagesBucket.putFile('exampleKey_2', __dirname + '/assets/example.jpg') 81 | .then( 82 | function(reply) { 83 | // 上传成功 84 | console.dir(reply); 85 | }, 86 | function(err) { 87 | // 上传失败 88 | console.error(err); 89 | } 90 | ); 91 | ``` 92 | 93 | **2. `Bucket.createPutStream()`** 94 | 95 | 七牛 Node.js SDK 提供基于流(Stream)的操作方式,为熟悉 Node.js 流式操作的开发者提供方便快捷的高性能 API。 96 | 97 | ```js 98 | var puttingStream = imagesBucket.createPutStream('exampleKey_3'); 99 | var readingStream = fs.createReadStream(__dirname + '/assets/example.jpg'); 100 | 101 | readingStream.pipe(puttingStream) 102 | .on('error', function(err) { 103 | console.error(err); 104 | }) 105 | .on('end', function(reply) { 106 | console.dir(reply); 107 | }); 108 | ``` 109 | 110 | #### 下载文件 111 | 112 | `Bucket.getFile()`和`Bucket.createGetStream()` 113 | 114 | 获取文件与上传文件同样简单,和 Node.js 中原生的文件系统(File System)的 API 有相似之处。 115 | 116 | ```js 117 | imagesBucket.getFile('exampleKey', function(err, data) { 118 | if (err) { 119 | return console.error(err); 120 | } 121 | 122 | // data 为包含文件数据的 Buffer 对象 123 | }); 124 | ``` 125 | 126 | 同样的,获取文件也可以使用流式操作进行 127 | ```js 128 | var gettingStream = imagesBucket.createGetStream('exampleKey'); 129 | var writingStream = fs.createWriteStream(__dirname + '/assets/example_tmp.jpg'); 130 | 131 | gettingStream.pipe(writingStream) 132 | .on('error', function(err) { 133 | console.error(err); 134 | }) 135 | .on('finish', function() { 136 | // 文件数据已写入本地文件系统 137 | }); 138 | ``` 139 | 140 | ### `Image` 图片操作 141 | 142 | 七牛 Node.js SDK 提供`Image`类,用于对图片资源进行操作。 143 | 144 | 使用 Bucket.image() 方法获取一个图像对象 145 | 146 | ```js 147 | var image = imagesBucket.image('exampleKey'); 148 | ``` 149 | 150 | #### `Image.imageInfo()` 151 | 152 | Image.imageInfo 方法可以用于获取图片资源的图片信息。 153 | 详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageInfo](http://docs.qiniu.com/api/v6/image-process.html#imageInfo) 154 | 155 | ```js 156 | image.imageInfo(function(err, info) { 157 | if (err) { 158 | return console.error(err); 159 | } 160 | 161 | console.dir(info); 162 | }); 163 | ``` 164 | 165 | #### `Image.exif()` 166 | 167 | Image.imageView 方法用于生成指定规格的缩略图。 168 | 详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageView](http://docs.qiniu.com/api/v6/image-process.html#imageView) 169 | 170 | ```js 171 | image.exif(function(err, exif) { 172 | if (err) { 173 | return console.error(err); 174 | } 175 | 176 | console.dir(exif); 177 | }); 178 | ``` 179 | 180 | #### `Image.imageView()` 181 | 182 | Image.imageView 方法用于生成指定规格的缩略图。 183 | 详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageView](http://docs.qiniu.com/api/v6/image-process.html#imageView) 184 | 185 | ```js 186 | image.imageView({ 187 | mode : 2, 188 | width : 180, 189 | height : 180, 190 | quality : 85, 191 | format : 'jpg' 192 | }, function(err, imageData) { 193 | if (err) { 194 | return console.error(err); 195 | } 196 | 197 | // imageData 为处理过后的图像数据 198 | }); 199 | ``` 200 | 201 | 其中,图片对象中的所有含图片数据返回的方法可以使用流式操作。不传入第二个的 callback 参数,而在调用方法的括弧后再调用`.stream()`方法,则会返回一个会不断输出数据的 IO 流。 202 | 203 | ```js 204 | var imageViewStream = image.imageView({ 205 | mode : 2, 206 | width : 180, 207 | height : 180, 208 | quality : 85, 209 | format : 'jpg' 210 | }).stream(); 211 | var writingStream = fs.createWriteStream(__dirname + '/assets/example_thumbnail.jpg'); 212 | 213 | imageViewStream.pipe(writingStream) 214 | .on('error', function(err) { 215 | console.error(err); 216 | }) 217 | .on('finish', function() { 218 | // 缩略图已写入本地文件系统 219 | }); 220 | ``` 221 | 222 | 诸如此类: 223 | ```js 224 | image.imageMogr(...).stream(); 225 | image.watermark(...).stream(); 226 | image.alias(...).stream(); 227 | ``` 228 | 229 | #### `Image.imageMogr()` 230 | 231 | Image.imageMogr 方法用于调用高级图像处理接口,并返回处理后的图片数据。 232 | 详细请看:[http://docs.qiniu.com/api/v6/image-process.html#imageMogr](http://docs.qiniu.com/api/v6/image-process.html#imageMogr) 233 | 234 | ```js 235 | image.imageMogr({ 236 | thumbnail : '300x500', 237 | gravity : 'NorthWest', 238 | crop : '!300x400a10a10', 239 | quality : 85, 240 | rotate : 90, 241 | format : 'jpg' 242 | }, function(err, imageData) { 243 | if (err) { 244 | return console.error(err); 245 | } 246 | 247 | // 使用 imageData 进行操作 248 | }); 249 | ``` 250 | 251 | #### `Image.watermark()` 252 | 253 | Image.watermark 方法用于生成一个带有水印的图片,图片水印 API 支持图片水印和文字水印两种模式。 254 | 详细请看:http://docs.qiniu.com/api/v6/image-process.html#watermark 255 | 256 | ```js 257 | image.watermark({ 258 | mode: 1, 259 | image: 'http://www.b1.qiniudn.com/images/logo-2.png', 260 | dissolve: 70, 261 | gravity: 'SouthEast', 262 | dx: 20, 263 | dy: 20 264 | }, function(err, imageData) { 265 | if (err) { 266 | return console.error(err); 267 | } 268 | 269 | // 使用 imageData 进行操作 270 | }); 271 | ``` 272 | 273 | #### `Image.alias()` 274 | 275 | Image.alias 方法用于返回既定的数据处理格式的数据,使用此方法需要在[七牛开发者平台](https://portal.qiniu.com)中对设置进行操作。 276 | 其中,`Image.alias()`方法继承于 key 所用的`Asset`类。 277 | 278 | ```js 279 | image.alias('testalias', function(err, imageData) { 280 | if (err) { 281 | return console.error(err); 282 | } 283 | 284 | // 使用 imageData 进行操作 285 | }); 286 | ``` 287 | 288 | ### `Asset` 资源操作 289 | 290 | 七牛 Node.js SDK 提供一个`Asset`类,用于对所属资源进行操作。 291 | 292 | 获取 key 所对应资源对象 293 | ```js 294 | var picture = imagesBucket.key('exampleKey'); 295 | ``` 296 | 297 | #### `Asset.url()` 298 | 299 | `Asset.url()`方法可以获得该资源的 URL 地址以用于访问 300 | 301 | ```js 302 | var picUrl = picture.url(); 303 | ``` 304 | 305 | #### `Asset.entryUrl()` 306 | 307 | `Asset.entryUrl()`方法可以获得该资源用于 API 调用时所需的 EncodedEntryURL。 308 | 但是在 Node.js SDK 中,大部分 API 都不需要开发者自行使用。:) 309 | 310 | ```js 311 | var encodedPicUrl = picture.entryUrl(); 312 | ``` 313 | 314 | #### `Asset.stat()` 315 | 316 | Asset.stat 方法可以获得该资源的如文件大小、MIME 类型等 stat 数据。 317 | 318 | ```js 319 | picture.stat(function(err, stat) { 320 | if (err) { 321 | return console.error(err); 322 | } 323 | 324 | console.dir(stat); 325 | /** 326 | * { 327 | * hash : , // string 类型,文件的Hash值 328 | * fsize : , // int 类型,文件的大小(单位: 字节) 329 | * mimeType : , // string 类型,文件的媒体类型,比如"image/gif" 330 | * putTime : // int64 类型,文件上传到七牛云的时间(Unix时间戳) 331 | * } 332 | */ 333 | }); 334 | ``` 335 | 336 | #### `Asset.move()` 337 | 338 | `Asset.move()`方法用于移动该资源到指定的位置。 339 | 第一个参数可以是来自其他 Bucket 所属的资源对象。 340 | 341 | ```js 342 | picture.move(imagesBucket.key('exampleKey_4'), function(err) { 343 | if (err) { 344 | return console.error(err); 345 | } 346 | 347 | // 此处没有返回值,如果没有错误,则为操作成功,以下方法相同 348 | }); 349 | ``` 350 | 351 | #### `Asset.copy()` 352 | 353 | `Asset.copy()`方法用于为该资源创建一个拷贝,并保存到指定的资源位置。 354 | 355 | ```js 356 | imagesBucket.key('exampleKey_4').copy(picture, function(err) { 357 | if (err) { 358 | return console.error(err); 359 | } 360 | }); 361 | ``` 362 | 363 | #### `Asset.remove()` 364 | 365 | `Asset.remove()`方法用于删除当前资源。 366 | 367 | ```js 368 | imagesBucket.key('exampleKey_4').remove(function(err) { 369 | if (err) { 370 | return console.error(err); 371 | } 372 | }); 373 | ``` 374 | 375 | #### `Asset.token()` 376 | 377 | `Asset.token()`方法用于生成当前资源的下载凭证。 378 | 379 | ```js 380 | var getToken = imagesBucket.key('exampleKey').token(); 381 | 382 | console.dir(getToken); 383 | /*=> 384 | { 385 | url: '', 386 | token: '', 387 | requestUrl: 'URL without token' 388 | } 389 | */ 390 | } 391 | ``` 392 | 393 | #### `Asset.download()` 394 | 395 | `Asset.download()`方法用于生成当前资源的下载链接。(不包含下载凭证) 396 | 397 | ```js 398 | var url = imagesBucket.key('exampleKey').download(); 399 | ``` 400 | 401 | ### `Batch` 资源批量操作 402 | 403 | 在支持对单个文件资源操作的同时,七牛云存储还支持批量地对多个文件进行查看、删除、复制和移动操作。 404 | 详细请看:http://docs.qiniu.com/api/v6/rs.html#batch 405 | 406 | 生成一个批量操作的控制器 407 | ```js 408 | var batch = qiniu.batch(); 409 | ``` 410 | 411 | `Batch`中大部分参数与资源对象`Asset`类似,支持查看、移动、复制和删除操作。 412 | 413 | ```js 414 | batch 415 | // 获取文件信息 416 | .stat(imagesBucket.key('exampleKey')) 417 | // 移动资源 418 | .move(imagesBucket.key('exampleKey'), imagesBucket.key('exampleKey_5')) 419 | // 复制资源 420 | .copy(imagesBucket.key('exampleKey_5'), imagesBucket.key('exampleKey')) 421 | // 删除资源 422 | .remove(imagesBucket.key('exampleKey_5')) 423 | // 执行操作 424 | // 每一个操作都按照前后顺序进行执行 425 | .exec(function(err, results) { 426 | if (err) { 427 | return console.error(err); 428 | } 429 | 430 | console.dir(results); 431 | // results 为每一个操作的结果 432 | }); 433 | ``` 434 | 435 | ### `Fop` 管道操作 436 | 437 | 七牛云存储提供一个非常实用的资源处理 API,可以用于对资源进行多种处理的操作。 438 | 439 | 例: 将一个原图缩略,然后在缩略图上打上另外一个图片作为水印 440 | 441 | 使用`Asset.fop()`方法创建 Fop 管道操作器,并进行操作。 442 | 443 | ```js 444 | var image = imagesBucket.key('exampleKey'); 445 | // Image.fop 方法继承于 Asset 类 446 | 447 | image.fop() 448 | // 对图片进行缩略 449 | .imageView({ 450 | mode : 2, 451 | height : 200 452 | }) 453 | // 为图片打上水印 454 | .watermark({ 455 | mode : 1, 456 | image : 'http://www.b1.qiniudn.com/images/logo-2.png' 457 | }) 458 | // 执行操作 459 | .exec(function(err, imageData) { 460 | if (err) { 461 | return console.error(err); 462 | } 463 | 464 | // imageData 为已打上水印的缩略图数据 465 | }); 466 | ``` 467 | 468 | #### `Fop.token()` 469 | 470 | 该方法用于生成当前 Fop 的下载凭证。 471 | 472 | ```js 473 | var image = imagesBucket.key('exampleKey'); 474 | // Image.fop 方法继承于 Asset 类 475 | 476 | var getToken = image.fop() 477 | // 对图片进行缩略 478 | .imageView({ 479 | mode : 2, 480 | height : 200 481 | }) 482 | // 为图片打上水印 483 | .watermark({ 484 | mode : 1, 485 | image : 'http://www.b1.qiniudn.com/images/logo-2.png' 486 | }) 487 | .token(); 488 | 489 | console.log(getToken.url); 490 | ``` 491 | 492 | ## 模块结构 493 | ![模块结构](http://ww2.sinaimg.cn/large/7287333fgw1e8263cvxeaj20mr0glgmp.jpg) 494 | 495 | ## License 496 | 497 | (The MIT License) 498 | 499 | Copyright (c) 2010-2013 Will Wen Gunn and other contributors 500 | 501 | Permission is hereby granted, free of charge, to any person obtaining 502 | a copy of this software and associated documentation files (the 503 | 'Software'), to deal in the Software without restriction, including 504 | without limitation the rights to use, copy, modify, merge, publish, 505 | distribute, sublicense, and/or sell copies of the Software, and to 506 | permit persons to whom the Software is furnished to do so, subject to 507 | the following conditions: 508 | 509 | The above copyright notice and this permission notice shall be 510 | included in all copies or substantial portions of the Software. 511 | 512 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 513 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 514 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 515 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 516 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 517 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 518 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 519 | 520 | 521 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/iwillwen/node-qiniu/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 522 | 523 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | // 引入七牛 Node.js SDK 2 | var qiniu = require('qiniu'); 3 | var fs = require('fs'); 4 | 5 | // 设置全局参数,包括必须的 AccessKey 和 SecretKey, 6 | // 还可以设置其他如 CallbackURL 等参数,将会顺延至所有空间。 7 | qiniu.config({ 8 | access_key: '------', 9 | secret_key: '------' 10 | }); 11 | 12 | // 获得空间对象 13 | var imagesBucket = qiniu.bucket('qiniu-sdk-test'); 14 | // 也可以这样操作 15 | // var imagesBucket = new qiniu.Bucket('qiniu-sdk-test'); 16 | 17 | // 上传一个文件,参数为将要上传的 Key,文件地址(可以是绝对地址,也可以是相对地址), 18 | // 第三个为可选参数 options,即本次上传中所使用 PutToken 的特殊设置, 19 | // 第四个为可选参数回调(callback), 20 | // 若不传入回调函数,将由 putFile 函数所返回的 Promise 对象进行响应。 21 | imagesBucket.putFile('exampleKey', __dirname + '/assets/example.jpg', function(err, reply) { 22 | if (err) { 23 | return console.error(err); 24 | } 25 | 26 | console.dir(reply); 27 | }); 28 | imagesBucket.putFile('exampleKey_1', __dirname + '/assets/example.jpg', { 29 | // 为本次上传中所使用的 Token 进行设置,此处为设置上传者标识 30 | endUser: 'foobar' 31 | }, function(err, reply) { 32 | if (err) { 33 | return console.error(err); 34 | } 35 | 36 | console.dir(reply); 37 | }); 38 | // 七牛 Node.js SDK 所提供的 Promise 对象遵循 Promise/A(+) 标准,使用 .then 方法进行响应 39 | imagesBucket.putFile('exampleKey_2', __dirname + '/assets/example.jpg') 40 | .then( 41 | function(reply) { 42 | // 上传成功 43 | console.dir(reply); 44 | }, 45 | function(err) { 46 | // 上传失败 47 | console.error(err); 48 | } 49 | ); 50 | 51 | // 七牛 Node.js SDK 提供基于流(Stream)的操作方式, 52 | // 为熟悉 Node.js 流式操作的开发者提供方便快捷的高性能 API 53 | var puttingStream = imagesBucket.createPutStream('exampleKey_3'); 54 | var readingStream = fs.createReadStream(__dirname + '/assets/example.jpg'); 55 | 56 | readingStream.pipe(puttingStream) 57 | .on('error', function(err) { 58 | console.error(err); 59 | }) 60 | .on('end', function(reply) { 61 | console.dir(reply); 62 | }); 63 | 64 | // 获取文件与上传文件同样简单,和 Node.js 中原生的文件系统(File System)的 API 有相似之处 65 | imagesBucket.getFile('exampleKey', function(err, data) { 66 | if (err) { 67 | return console.error(err); 68 | } 69 | 70 | // data 为包含文件数据的 Buffer 对象 71 | }); 72 | // 同样的,获取文件也可以使用流式操作进行 73 | var gettingStream = imagesBucket.createGetStream('exampleKey'); 74 | var writingStream = fs.createWriteStream(__dirname + '/assets/example_tmp.jpg'); 75 | 76 | gettingStream.pipe(writingStream) 77 | .on('error', function(err) { 78 | console.error(err); 79 | }) 80 | .on('finish', function() { 81 | // 文件数据已写入本地文件系统 82 | }); 83 | 84 | // 图片处理 85 | // 使用 Bucket.image 方法获取一个图像对象 86 | var image = imagesBucket.image('exampleKey'); 87 | 88 | // Image.imageInfo 方法可以用于获取图片资源的图片信息 89 | // 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageInfo 90 | image.imageInfo(function(err, info) { 91 | if (err) { 92 | return console.error(err); 93 | } 94 | 95 | console.dir(info); 96 | }); 97 | 98 | // Image.exif 方法可以用于获取图片资源的EXIF信息, 99 | // 前提是该图片由数码相机生成,并且没有清除EXIF信息 100 | // 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageExif 101 | image.exif(function(err, exif) { 102 | if (err) { 103 | return console.error(err); 104 | } 105 | 106 | console.dir(exif); 107 | }); 108 | 109 | // Image.imageView 方法用于生成指定规格的缩略图 110 | // 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageView 111 | image.imageView({ 112 | mode : 2, 113 | width : 180, 114 | height : 180, 115 | quality : 85, 116 | format : 'jpg' 117 | }, function(err, imageData) { 118 | if (err) { 119 | return console.error(err); 120 | } 121 | 122 | // imageData 为处理过后的图像数据 123 | }); 124 | 125 | // 其中,图片对象中的所有含图片数据返回的方法可以使用流式操作 126 | // 不传入第二个的 callback 参数,而在调用方法的括弧后再调用 .stream 方法, 127 | // 则会返回一个会不断输出数据的 IO 流 128 | var imageViewStream = image.imageView({ 129 | mode : 2, 130 | width : 180, 131 | height : 180, 132 | quality : 85, 133 | format : 'jpg' 134 | }).stream(); 135 | var writingStream = fs.createWriteStream(__dirname + '/assets/example_thumbnail.jpg'); 136 | 137 | imageViewStream.pipe(writingStream) 138 | .on('error', function(err) { 139 | console.error(err); 140 | }) 141 | .on('finish', function() { 142 | // 缩略图已写入本地文件系统 143 | }); 144 | 145 | /** 146 | * 诸如此类: 147 | * image.imageMogr(...).stream(); 148 | * image.watermark(...).stream(); 149 | * image.alias(...).stream(); 150 | */ 151 | 152 | // Image.imageMogr 方法用于调用高级图像处理接口,并返回处理后的图片数据 153 | // 详细请看:http://docs.qiniu.com/api/v6/image-process.html#imageMogr 154 | image.imageMogr({ 155 | thumbnail : '300x500', 156 | gravity : 'NorthWest', 157 | crop : '!300x400a10a10', 158 | quality : 85, 159 | rotate : 90, 160 | format : 'jpg' 161 | }, function(err, imageData) { 162 | if (err) { 163 | return console.error(err); 164 | } 165 | 166 | // 使用 imageData 进行操作 167 | }); 168 | 169 | // Image.watermark 方法用于生成一个带有水印的图片 170 | // 图片水印 API 支持图片水印和文字水印两种模式 171 | // 详细请看:http://docs.qiniu.com/api/v6/image-process.html#watermark 172 | image.watermark({ 173 | mode: 1, 174 | image: 'http://www.b1.qiniudn.com/images/logo-2.png', 175 | dissolve: 70, 176 | gravity: 'SouthEast', 177 | dx: 20, 178 | dy: 20 179 | }, function(err, imageData) { 180 | if (err) { 181 | return console.error(err); 182 | } 183 | 184 | // 使用 imageData 进行操作 185 | }); 186 | 187 | // Image.alias 方法用于返回既定的数据处理格式的数据 188 | // 使用此方法需要在 https://portal.qiniu.com 中对设置进行操作 189 | // PS: Image.alias 方法继承于 key 所用的 Asset 类 190 | image.alias('testalias', function(err, imageData) { 191 | if (err) { 192 | return console.error(err); 193 | } 194 | 195 | // 使用 imageData 进行操作 196 | }); 197 | 198 | // 资源操作 199 | // 获取 key 所对应资源对象 200 | var picture = imagesBucket.key('exampleKey'); 201 | 202 | // Asset.url 方法可以获得该资源的 URL 地址以用于访问 203 | var picUrl = picture.url(); 204 | 205 | // Asset.entryUrl 方法可以获得该资源用于 API 调用时所需的 EncodedEntryURL 206 | // 但是在 Node.js SDK 中,大部分 API 都不需要开发者自行使用。:) 207 | var encodedPicUrl = picture.entryUrl(); 208 | 209 | // Asset.stat 方法可以获得该资源的如文件大小、MIME 类型等 stat 数据 210 | picture.stat(function(err, stat) { 211 | if (err) { 212 | return console.error(err); 213 | } 214 | 215 | console.dir(stat); 216 | /** 217 | * { 218 | * hash : , // string 类型,文件的Hash值 219 | * fsize : , // int 类型,文件的大小(单位: 字节) 220 | * mimeType : , // string 类型,文件的媒体类型,比如"image/gif" 221 | * putTime : // int64 类型,文件上传到七牛云的时间(Unix时间戳) 222 | * } 223 | */ 224 | }); 225 | 226 | // Asset.move 方法用于移动该资源到指定的位置 227 | // 第一个参数可以是来自其他 Bucket 所属的资源对象 228 | picture.move(imagesBucket.key('exampleKey_4'), function(err) { 229 | if (err) { 230 | return console.error(err); 231 | } 232 | 233 | // 此处没有返回值,如果没有错误,则为操作成功,以下方法相同 234 | }); 235 | 236 | // Asset.copy 方法用于为该资源创建一个拷贝,并保存到指定的资源位置 237 | imagesBucket.key('exampleKey_4').copy(picture, function(err) { 238 | if (err) { 239 | return console.error(err); 240 | } 241 | }); 242 | 243 | // Asset.remove方法用于删除当前资源 244 | imagesBucket.key('exampleKey_4').remove(function(err) { 245 | if (err) { 246 | return console.error(err); 247 | } 248 | }); 249 | 250 | // 资源批量操作 251 | // 在支持对单个文件资源操作的同时,七牛云存储还支持批量地对多个文件进行查看、删除、复制和移动操作。 252 | // 详细请看:http://docs.qiniu.com/api/v6/rs.html#batch 253 | 254 | // 生成一个批量操作的控制器 255 | var batch = qiniu.batch(); 256 | 257 | batch 258 | // 获取文件信息 259 | .stat(imagesBucket.key('exampleKey')) 260 | // 移动资源 261 | .move(imagesBucket.key('exampleKey'), imagesBucket.key('exampleKey_5')) 262 | // 复制资源 263 | .copy(imagesBucket.key('exampleKey_5'), imagesBucket.key('exampleKey')) 264 | // 删除资源 265 | .remove(imagesBucket.key('exampleKey_5')) 266 | // 执行操作 267 | // 每一个操作都按照前后顺序进行执行 268 | .exec(function(err, results) { 269 | if (err) { 270 | return console.error(err); 271 | } 272 | 273 | console.dir(results); 274 | // results 为每一个操作的结果 275 | }); 276 | 277 | // Fop 管道操作 278 | // 七牛云存储提供一个非常实用的资源处理 API,可以用于对资源进行多种处理的操作 279 | 280 | // 例: 将一个原图缩略,然后在缩略图上打上另外一个图片作为水印 281 | 282 | var image = imagesBucket.key('exampleKey'); 283 | 284 | // 使用 Asset.fop 方法创建 Fop 管道操作器 285 | // Image.fop 方法继承于 Asset 类 286 | image.fop() 287 | // 对图片进行缩略 288 | .imageView({ 289 | mode : 2, 290 | height : 200 291 | }) 292 | // 为图片打上水印 293 | .watermark({ 294 | mode : 1, 295 | image : 'http://www.b1.qiniudn.com/images/logo-2.png' 296 | }) 297 | // 执行操作 298 | .exec(function(err, imageData) { 299 | if (err) { 300 | return console.error(err); 301 | } 302 | 303 | // imageData 为已打上水印的缩略图数据 304 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/qiniu'); -------------------------------------------------------------------------------- /lib/asset.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var Q = require('q'); 3 | var util = require('util'); 4 | var url = require('url'); 5 | var dataStream = require('dataStream'); 6 | var _ = require('underscore'); 7 | var utils = require('./utils'); 8 | var config = require('./config'); 9 | var Fop = require('./fop'); 10 | var token = require('./token')(config); 11 | 12 | var AccessToken = token.AccessToken; 13 | 14 | /** 15 | * Asset Class 16 | * @param {String} key Asset's key 17 | * @param {Bucket} parent Bucket object 18 | */ 19 | function Asset(key, parent, config) { 20 | this.key = key; 21 | this.parent = parent; 22 | this.access_token = new AccessToken(); 23 | 24 | this.config = config || {}; 25 | } 26 | 27 | /** 28 | * return the asset url 29 | * @return {String} url 30 | */ 31 | Asset.prototype.url = function() { 32 | return url.format({ 33 | protocol: 'http', 34 | hostname: util.format('%s.qiniudn.com', this.parent.name), 35 | pathname: '/' + this.key 36 | }); 37 | }; 38 | 39 | /** 40 | * generate a download token 41 | * @return {String} token 42 | */ 43 | Asset.prototype.token = function(opts) { 44 | var getPolicy = new token.GetPolicy(opts); 45 | return getPolicy.token(util.format('http://%s.qiniudn.com', this.parent.name), this.key, opts); 46 | }; 47 | 48 | /** 49 | * return the encoded entry url of the asset 50 | * @return {String} entry url 51 | */ 52 | Asset.prototype.entryUrl = function() { 53 | return utils.safeEncode(util.format( 54 | '%s:%s', 55 | this.parent.name, this.key 56 | )); 57 | }; 58 | 59 | /** 60 | * return the qrcode image of the asset 61 | * @param {Object} opts options 62 | * @param {Function} callback Callback 63 | * @return {Promise} promise object 64 | */ 65 | Asset.prototype.qrcode = function(opts, callback) { 66 | var deferred = Q.defer(); 67 | switch (true) { 68 | case _.isFunction(opts): 69 | callback = opts; 70 | opts = { mode: 0, level: 'L' }; 71 | break; 72 | case _.isObject(opts) && _.isUndefined(callback): 73 | callback = noop; 74 | break; 75 | case _.isUndefined(opts): 76 | opts = { mode: 0, level: 'L' }; 77 | callback = noop; 78 | break; 79 | } 80 | 81 | var _url = util.format('%s?qrcode/%d/level/%s', this.url(), opts.mode, opts.level); 82 | 83 | var recive = new dataStream(); 84 | request(_url).pipe(recive) 85 | .on('error', function(err) { 86 | callback(err); 87 | deferred.reject(err); 88 | }) 89 | .on('complete', function() { 90 | var data = this.body(); 91 | 92 | callback(null, data); 93 | deferred.resolve(data); 94 | }); 95 | 96 | var promise = deferred.promise; 97 | 98 | promise.stream = function() { 99 | return recive; 100 | }; 101 | 102 | return promise; 103 | }; 104 | 105 | /** 106 | * get the asset's stat 107 | * @param {Function} callback Callback 108 | * @return {Promise} promise object 109 | */ 110 | Asset.prototype.stat = function(callback) { 111 | var deferred = Q.defer(); 112 | callback = callback || noop; 113 | 114 | var path = util.format('/stat/%s', this.entryUrl()); 115 | var _url = url.format({ 116 | protocol: 'http', 117 | hostname: config.rsUrl, 118 | pathname: path 119 | }); 120 | 121 | var token = this.access_token.token(path, null); 122 | 123 | request({ 124 | url: _url, 125 | method: 'POST', 126 | headers: { 127 | 'Authorization': token, 128 | 'Content-Type': 'application/x-www-form-urlencoded' 129 | } 130 | }, function(err, res, body) { 131 | if (err) { 132 | deferred.reject(err); 133 | return callback(err); 134 | } 135 | 136 | var result = JSON.parse(body); 137 | 138 | deferred.resolve(result); 139 | callback(null, result); 140 | }); 141 | 142 | return deferred.promise; 143 | }; 144 | 145 | /** 146 | * move the asset to another position 147 | * @param {Asset} destAsset dest Asset 148 | * @param {Function} callback Callback 149 | * @return {Promise} promise object 150 | */ 151 | Asset.prototype.move = function(destAsset, callback) { 152 | var deferred = Q.defer(); 153 | callback = callback || noop; 154 | 155 | var path = util.format('/move/%s/%s', this.entryUrl(), destAsset.entryUrl()); 156 | var _url = url.format({ 157 | protocol: 'http', 158 | hostname: config.rsUrl, 159 | pathname: path 160 | }); 161 | 162 | var token = this.access_token.token(path, null); 163 | 164 | request({ 165 | url: _url, 166 | method: 'POST', 167 | headers: { 168 | 'Authorization': token, 169 | 'Content-Type': 'application/x-www-form-urlencoded' 170 | } 171 | }, function(err, res) { 172 | if (err) { 173 | deferred.reject(err); 174 | return callback(err); 175 | } 176 | 177 | 178 | deferred.resolve(); 179 | callback(null); 180 | }); 181 | 182 | return deferred.promise; 183 | }; 184 | 185 | /** 186 | * make a copy of the asset to another position 187 | * @param {Asset} destAsset dest Asset 188 | * @param {Function} callback Callback 189 | * @return {Promise} promise object 190 | */ 191 | Asset.prototype.copy = function(destAsset, callback) { 192 | var deferred = Q.defer(); 193 | callback = callback || noop; 194 | 195 | var path = util.format('/copy/%s/%s', this.entryUrl(), destAsset.entryUrl()); 196 | var _url = url.format({ 197 | protocol: 'http', 198 | hostname: config.rsUrl, 199 | pathname: path 200 | }); 201 | 202 | var token = this.access_token.token(path, null); 203 | 204 | request({ 205 | url: _url, 206 | method: 'POST', 207 | headers: { 208 | 'Authorization': token, 209 | 'Content-Type': 'application/x-www-form-urlencoded' 210 | } 211 | }, function(err, res, body) { 212 | if (err) { 213 | deferred.reject(err); 214 | return callback(err); 215 | } 216 | 217 | deferred.resolve(); 218 | callback(null); 219 | }); 220 | 221 | return deferred.promise; 222 | }; 223 | 224 | /** 225 | * delete the asset 226 | * @param {Function} callback Callback 227 | * @return {Promise} promise object 228 | */ 229 | Asset.prototype.remove = function(callback) { 230 | var deferred = Q.defer(); 231 | callback = callback || noop; 232 | 233 | var path = util.format('/delete/%s', this.entryUrl()); 234 | var _url = url.format({ 235 | protocol: 'http', 236 | hostname: config.rsUrl, 237 | pathname: path 238 | }); 239 | 240 | var token = this.access_token.token(path, null); 241 | 242 | request({ 243 | url: _url, 244 | method: 'POST', 245 | headers: { 246 | 'Authorization': token, 247 | 'Content-Type': 'application/x-www-form-urlencoded' 248 | } 249 | }, function(err, res, body) { 250 | if (err) { 251 | deferred.reject(err); 252 | return callback(err); 253 | } 254 | 255 | deferred.resolve(); 256 | callback(null); 257 | }); 258 | 259 | return deferred.promise; 260 | }; 261 | 262 | Asset.prototype.fop = function(config) { 263 | return new Fop(this, config); 264 | }; 265 | 266 | /** 267 | * return a image with a established format 268 | * @param {String} alias alias name 269 | * @param {Function} callback Callback 270 | * @return {Promise} promise object 271 | */ 272 | Asset.prototype.alias = function(alias, callback) { 273 | var deferred = Q.defer(); 274 | callback = callback || noop; 275 | 276 | var _url = this.url(); 277 | 278 | _url += util.format('%s%s', this.config.separate, alias); 279 | 280 | var recive = new dataStream(); 281 | request(_url).pipe(recive) 282 | .on('complete', function() { 283 | var data = this.body(); 284 | 285 | deferred.resolve(data); 286 | callback(null, data); 287 | }) 288 | .on('error', function(err) { 289 | deferred.reject(err); 290 | callback(err); 291 | }); 292 | 293 | var promise = deferred.promise; 294 | 295 | /** 296 | * return the image stream 297 | * @return {Stream} stream 298 | */ 299 | promise.stream = function() { 300 | return recive; 301 | }; 302 | 303 | return promise; 304 | }; 305 | 306 | /** 307 | * Generate a download link for this asset 308 | * @param {String} filename filename 309 | * @return {String} download link 310 | */ 311 | Asset.prototype.download = function(filename) { 312 | var urlInfo = url.parse(this.url()); 313 | urlInfo.search = '?download/' + filename; 314 | 315 | return url.format(urlInfo); 316 | }; 317 | 318 | /** 319 | * Markdown to HTML 320 | * @param {Object} opts options 321 | * @param {Function} callback Callback 322 | * @return {Promise} promise object 323 | */ 324 | Asset.prototype.md2html = function(opts, callback) { 325 | var deferred = Q.defer(); 326 | 327 | if (_.isFunction(opts)) { 328 | callback = opts; 329 | opts = { 330 | mode: false, 331 | css: false 332 | }; 333 | } else if (_.isObject(opts)) { 334 | callback = callback || noop; 335 | } else { 336 | callback = callback || noop; 337 | opts = { 338 | mode: false, 339 | css: false 340 | }; 341 | } 342 | 343 | var _url = this.url() + '?md2html'; 344 | 345 | if (opts.mode) { 346 | _url += util.format('/%s', opts.mode); 347 | } 348 | 349 | if (opts.css) { 350 | _url += util.format('/css/%s', utils.safeEncode(opts.css)); 351 | } 352 | 353 | var recive = new dataStream(); 354 | request(_url).pipe(recive) 355 | .on('complete', function() { 356 | var data = this.body(); 357 | 358 | deferred.resolve(data); 359 | callback(null, data); 360 | }) 361 | .on('error', function(err) { 362 | deferred.reject(err); 363 | callback(err); 364 | }); 365 | 366 | return deferred.promise; 367 | }; 368 | 369 | module.exports = Asset; 370 | 371 | function noop() { 372 | return false; 373 | } 374 | -------------------------------------------------------------------------------- /lib/batch.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var Q = require('q'); 3 | var _ = require('underscore'); 4 | var util = require('util'); 5 | var url = require('url'); 6 | var http = require('http'); 7 | var utils = require('./utils'); 8 | var config = require('./config'); 9 | 10 | /** 11 | * multiple assets control 12 | * @param {Object} _config config 13 | */ 14 | function Batch(_config) { 15 | this.config = _.extend(_.clone(config), _config); 16 | 17 | var token = require('./token')(this.config); 18 | var AccessToken = token.AccessToken; 19 | 20 | this.access_token = new AccessToken(); 21 | this.op = ''; 22 | } 23 | 24 | /** 25 | * Queue a stat control 26 | * @param {Asset} asset asset 27 | * @return {Batch} Batch 28 | */ 29 | Batch.prototype.stat = function(asset) { 30 | this.op += '&' + genOpUrl('stat', asset); 31 | 32 | return this; 33 | }; 34 | 35 | /** 36 | * Queue a move control 37 | * @param {Asset} src source asset 38 | * @param {Asset} dest dest asset 39 | * @return {Batch} Batch 40 | */ 41 | Batch.prototype.move = function(src, dest) { 42 | this.op += '&' + genOpUrl('move', src, dest); 43 | 44 | return this; 45 | }; 46 | 47 | /** 48 | * Queue a copy control 49 | * @param {Asset} src source asset 50 | * @param {Asset} dest dest asset 51 | * @return {Batch} Batch 52 | */ 53 | Batch.prototype.copy = function(src, dest) { 54 | this.op += '&' + genOpUrl('copy', src, dest); 55 | 56 | return this; 57 | }; 58 | 59 | /** 60 | * Queue a delete control 61 | * @param {Asset} asset asset 62 | * @return {Batch} Batch 63 | */ 64 | Batch.prototype.remove = function(asset) { 65 | this.op += '&' + genOpUrl('delete', asset); 66 | 67 | return this; 68 | }; 69 | 70 | /** 71 | * Execute the queue 72 | * @param {Function} callback Callback 73 | * @return {Promise} promise object 74 | */ 75 | Batch.prototype.exec = function(callback) { 76 | var deferred = Q.defer(); 77 | callback = callback || noop; 78 | 79 | var _url = url.format({ 80 | protocol: 'http', 81 | hostname: this.config.rsUrl, 82 | pathname: '/batch' 83 | }); 84 | var body = this.op.substr(1); 85 | var token = this.access_token.token('/batch', body); 86 | 87 | request({ 88 | url: _url, 89 | method: 'POST', 90 | body: body, 91 | headers: { 92 | 'Authorization': token, 93 | 'Content-Type': 'application/x-www-form-urlencoded' 94 | }, 95 | }, function(err, res, body) { 96 | if (err) { 97 | deferred.reject(err); 98 | return callback(err); 99 | } 100 | 101 | if (body.length > 0) { 102 | var rows = JSON.parse(body); 103 | 104 | deferred.resolve(rows); 105 | callback(null, rows); 106 | } else { 107 | err = new Error(http.STATUS_CODES[503]); 108 | 109 | deferred.reject(err); 110 | callback(err); 111 | } 112 | }); 113 | 114 | return deferred.promise; 115 | }; 116 | 117 | module.exports = Batch; 118 | 119 | function genOpUrl(op, src, dest) { 120 | var rtn = null; 121 | 122 | switch (op) { 123 | case 'stat': 124 | rtn = util.format('op=/stat/%s', src.entryUrl()); 125 | break; 126 | case 'move': 127 | rtn = util.format('op=/move/%s/%s', src.entryUrl(), dest.entryUrl()); 128 | break; 129 | case 'copy': 130 | rtn = util.format('op=/copy/%s/%s', src.entryUrl(), dest.entryUrl()); 131 | break; 132 | case 'delete': 133 | rtn = util.format('op=/delete/%s', src.entryUrl()); 134 | break; 135 | } 136 | 137 | return rtn; 138 | } 139 | 140 | function noop() { 141 | return false; 142 | } -------------------------------------------------------------------------------- /lib/bucket.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var _ = require('underscore'); 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var Q = require('q'); 6 | var dataStream = require('dataStream'); 7 | var util = require('util'); 8 | var url = require('url'); 9 | var path = require('path'); 10 | var Asset = require('./asset'); 11 | var Image = require('./image'); 12 | var token = null; 13 | 14 | var globalConfig = null; 15 | 16 | 17 | /** 18 | * Bucket 19 | * Example: 20 | * ``` 21 | * var imagesBucket = new qiniu.Bucket('images', { 22 | * // special option 23 | * }); 24 | * ``` 25 | * @param {String} bucketName bucket's name 26 | * @param {Object} config config 27 | */ 28 | function Bucket(bucketName, config) { 29 | config = config || {}; 30 | 31 | this.name = bucketName; 32 | this.config = _.extend(globalConfig, config, { 33 | scope: bucketName 34 | }); 35 | } 36 | 37 | /** 38 | * Upload a file 39 | * Example: 40 | * ``` 41 | * imagesBucket.putFile('example.jpg', __dirname + '/assert/example.jpg', function(err, reply) { 42 | * if (err) { 43 | * return console.error(err); 44 | * } 45 | * 46 | * console.dir(reply); 47 | * }); 48 | * ``` 49 | * @param {String} key key 50 | * @param {String} pathname file pathname 51 | * @param {Object} options upload option 52 | * @param {Function} callback Callback 53 | * @return {Promise} Promise object 54 | */ 55 | Bucket.prototype.putFile = function(key, pathname, options, callback) { 56 | // token 57 | var deferred = Q.defer(); 58 | 59 | switch (arguments.length) { 60 | case 3: 61 | if (_.isFunction(options)) { 62 | callback = options; 63 | options = {}; 64 | } 65 | break; 66 | case 2: 67 | options = {}; 68 | callback = noop; 69 | break; 70 | } 71 | 72 | var config = _.extend(_.clone(this.config), options); 73 | 74 | var putToken = (new token.PutPolicy(config)).token(); 75 | 76 | // safe pathname 77 | pathname = path.resolve(process.cwd(), pathname); 78 | 79 | // upload API 80 | var uploadUrl = url.format({ 81 | protocol: 'http', 82 | hostname: globalConfig.uploadUrl 83 | }); 84 | 85 | // uploading 86 | var req = request.post(uploadUrl, function(err, res, body) { 87 | var result = JSON.parse(body); 88 | 89 | if (err) { 90 | deferred.reject(err); 91 | return callback(err, result); 92 | } 93 | 94 | deferred.resolve(result); 95 | callback(null, result); 96 | }); 97 | 98 | // form data 99 | var form = req.form(); 100 | 101 | form.append('token', putToken); 102 | form.append('key', key); 103 | form.append('file', fs.createReadStream(pathname), { 104 | filename: path.basename(pathname) 105 | }); 106 | 107 | return deferred.promise; 108 | }; 109 | 110 | /** 111 | * create a uploading stream 112 | * @param {String} key key 113 | * @param {Object} options upload option 114 | * @return {Stream} uploading stream 115 | */ 116 | Bucket.prototype.createPutStream = function(key, options) { 117 | 118 | options = options || {}; 119 | 120 | var config = _.extend(_.clone(this.config), options); 121 | 122 | // token 123 | var putToken = (new token.PutPolicy(config)).token(); 124 | 125 | var stream = new dataStream(); 126 | 127 | // upload API 128 | var uploadUrl = url.format({ 129 | protocol: 'http', 130 | hostname: globalConfig.uploadUrl 131 | }); 132 | 133 | // uploading 134 | var req = request.post(uploadUrl, function(err, res, body) { 135 | var result = JSON.parse(body); 136 | 137 | if (err) { 138 | return stream.emit('error', err); 139 | } 140 | 141 | stream.emit('end', result); 142 | stream.emit('complete', result); 143 | }); 144 | 145 | // form data 146 | var form = req.form(); 147 | 148 | form.append('token', putToken); 149 | form.append('key', key); 150 | stream.on('pipe', function(src) { 151 | if (src.path) { 152 | var filename = path.basename(src.path); 153 | } else { 154 | var filename = key; 155 | } 156 | form.append('file', stream, { 157 | filename: filename 158 | }); 159 | }); 160 | 161 | return stream; 162 | }; 163 | 164 | /** 165 | * Get a key 166 | * @param {String} key key 167 | * @param {Function} callback Callback 168 | * @return {Promise} Promise Object 169 | */ 170 | Bucket.prototype.getFile = function(key, callback) { 171 | // token 172 | var getToken = this.key(key).token(); 173 | var deferred = Q.defer(); 174 | callback = callback || noop; 175 | 176 | var res = request(getToken.url); 177 | var recive = new dataStream(); 178 | res.pipe(recive) 179 | .on('error', deferred.reject.bind(deferred)) 180 | .on('complete', function() { 181 | var data = this.body(); 182 | 183 | deferred.resolve(data); 184 | callback(null, data); 185 | }); 186 | 187 | return deferred.promise; 188 | }; 189 | 190 | /** 191 | * create a getting stream 192 | * @param {String} key key 193 | * @return {Stream} getting stream 194 | */ 195 | Bucket.prototype.createGetStream = function(key) { 196 | // token 197 | var getToken = this.key(key).token(); 198 | 199 | var res = request(getToken.url); 200 | 201 | return res; 202 | }; 203 | 204 | Bucket.prototype.url = function() { 205 | return util.format('%s.qiniudn.com', this.name); 206 | }; 207 | 208 | /** 209 | * return a asset object 210 | * @param {String} key key 211 | * @return {Asset} asset object 212 | */ 213 | Bucket.prototype.key = function(key) { 214 | return new Asset(key, this); 215 | }; 216 | Bucket.Asset = Asset; 217 | 218 | Bucket.prototype.image = function(key) { 219 | return new Image(key, this); 220 | }; 221 | Bucket.Image = Image; 222 | 223 | Bucket.prototype.token = function(opts) { 224 | opts = opts || {}; 225 | 226 | opts = _.extend(opts, { 227 | scope: this.name 228 | }); 229 | 230 | var putToken = (new token.PutPolicy(opts)).token(); 231 | 232 | return putToken; 233 | }; 234 | 235 | module.exports = function(config) { 236 | globalConfig = config; 237 | token = require('./token')(globalConfig); 238 | return Bucket; 239 | }; 240 | 241 | function noop() { 242 | return false; 243 | } -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | uploadUrl : 'up.qiniu.com', 3 | rsUrl : 'rs.qbox.me', 4 | rsfUrl : 'rsf.qbox.me' 5 | }; -------------------------------------------------------------------------------- /lib/fop.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var _ = require('underscore'); 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var Q = require('q'); 6 | var dataStream = require('dataStream'); 7 | var util = require('util'); 8 | var url = require('url'); 9 | var Asset = require('./asset'); 10 | var utils = require('./utils'); 11 | var config = require('./config'); 12 | var Token = require('./token')(config); 13 | 14 | /** 15 | * Fop Class 16 | * @param {Asset} asset asset 17 | * @param {Object} _config config 18 | */ 19 | function Fop(asset, _config) { 20 | this.parent = asset; 21 | this.config = _.extend(_.clone(config), _config); 22 | 23 | this.query = ''; 24 | } 25 | 26 | /** 27 | * custom fop 28 | * @param {String} str fop string 29 | * @return {Fop} fop 30 | */ 31 | Fop.prototype.fop = function(str) { 32 | this.query += '|' + str; 33 | 34 | return this; 35 | }; 36 | 37 | /** 38 | * Add imageInfo to the fop 39 | * @return {Fop} fop 40 | */ 41 | Fop.prototype.imageInfo = function() { 42 | this.query += '|imageInfo'; 43 | 44 | return this; 45 | }; 46 | 47 | /** 48 | * Add exif to the fop 49 | * @return {Fop} fop 50 | */ 51 | Fop.prototype.exif = function() { 52 | this.query += '|exif'; 53 | 54 | return this; 55 | }; 56 | 57 | 58 | var imageViewTranslations = { 59 | weight: 'w', 60 | height: 'h', 61 | quality: 'q' 62 | }; 63 | 64 | /** 65 | * Add imageView to the fop 66 | * @param {Object} opts options 67 | * @return {Fop} fop 68 | */ 69 | Fop.prototype.imageView = function(opts) { 70 | var mode = opts.mode; 71 | delete opts.mode; 72 | 73 | var _url = this.url(); 74 | var params = {}; 75 | 76 | _.each(opts, function(value, key) { 77 | if (imageViewTranslations.hasOwnProperty(key)) { 78 | key = imageViewTranslations[key]; 79 | } 80 | 81 | params[key] = value; 82 | }); 83 | 84 | this.query += util.format('|imageView/%d%s', mode, genOptUrl(params)); 85 | 86 | return this; 87 | }; 88 | 89 | /** 90 | * Add imageMogr to the fop 91 | * @param {Object} opts options 92 | * @return {Fop} fop 93 | */ 94 | Fop.prototype.imageMogr = function(opts) { 95 | var params = {}; 96 | 97 | _.extend(params, opts); 98 | 99 | this.query += util.format('|imageMogr/v2/auto-orient%s', genOptUrl(params)); 100 | 101 | return this; 102 | }; 103 | 104 | /** 105 | * Add watermark to the fop 106 | * @param {Object} opts options 107 | * @return {Fop} fop 108 | */ 109 | Fop.prototype.watermark = function(opts) { 110 | var params = {}; 111 | var mode = opts.mode; 112 | delete opts.mode; 113 | 114 | _.extend(params, opts); 115 | 116 | params.image = utils.safeEncode(params.image); 117 | 118 | this.query += util.format('|watermark/%d%s', mode, genOptUrl(params)); 119 | 120 | return this; 121 | }; 122 | 123 | /** 124 | * Add qrcode to the fop 125 | * @param {Object} opts options 126 | * @return {Fop} fop 127 | */ 128 | Fop.prototype.qrcode = function(opts) { 129 | opts = opts || { 130 | mode: 0, 131 | level: 'L' 132 | }; 133 | 134 | this.query += util.format('|qrcode/%d/level/%s', this.url(), opts.mode, opts.level); 135 | 136 | return this; 137 | }; 138 | 139 | /** 140 | * Markdown to HTML 141 | * @param {Object} opts options 142 | * @return {Fop} fop 143 | */ 144 | Fop.prototype.md2html = function(opts) { 145 | opts = opts || { 146 | mode: false, 147 | css: false 148 | }; 149 | 150 | var _url = '|md2html' 151 | 152 | if (opts.css) { 153 | _url += util.format('/%s', opts.mode); 154 | } 155 | 156 | if (opts.css) { 157 | _url += util.format('/css/%s', utils.safeEncode(opts.css)); 158 | } 159 | 160 | this.query += _url; 161 | 162 | return this; 163 | }; 164 | 165 | /** 166 | * Save the fop result as an asset 167 | * @param {Asset} destAsset dest 168 | * @return {Fop} fop 169 | */ 170 | Fop.prototype.saveas = function(destAsset) { 171 | var entryUrl = destAsset.entryUrl(); 172 | 173 | this.query += util.format('|saveas/%s', entryUrl); 174 | 175 | var token = (new Token.SaveasToken(this.url())).token(); 176 | 177 | this.query += util.format('/sign/%s', token); 178 | 179 | return this; 180 | }; 181 | 182 | /** 183 | * Create a download url for this asset 184 | * @param {String} filename filename 185 | * @return {Fop} fop 186 | */ 187 | Fop.prototype.download = function(filename) { 188 | this.query += util.format('|download/%s', filename); 189 | 190 | return this; 191 | }; 192 | 193 | /** 194 | * Get the url of the fop 195 | * @return {String} url 196 | */ 197 | Fop.prototype.url = function() { 198 | return util.format('%s?%s', this.parent.url(), this.query.substr(1)); 199 | }; 200 | 201 | /** 202 | * Return the fop string 203 | * @return {String} fop 204 | */ 205 | Fop.prototype.get = function() { 206 | return this.query.substr(1); 207 | }; 208 | 209 | /** 210 | * Generate a get token for this fop 211 | * @return {Object} token 212 | */ 213 | Fop.prototype.token = function() { 214 | var getPolicy = new Token.GetPolicy(); 215 | return getPolicy.token(this.url(), this.parent.key, { 216 | fop: this.get() 217 | }); 218 | }; 219 | 220 | /** 221 | * Execute the fop 222 | * @param {Function} callback Callback 223 | * @return {Promise} promise object 224 | */ 225 | Fop.prototype.exec = function(callback) { 226 | var deferred = Q.defer(); 227 | callback = callback || noop; 228 | 229 | var recive = new dataStream(); 230 | request(this.url()) 231 | .pipe(recive) 232 | .on('error', function(err) { 233 | deferred.reject(err); 234 | callback(err); 235 | }) 236 | .on('complete', function() { 237 | var data = this.body(); 238 | 239 | deferred.resolve(data); 240 | callback(null, data); 241 | }); 242 | 243 | return deferred.promise; 244 | }; 245 | 246 | /** 247 | * Return a stream of the fop 248 | * @return {Stream} stream 249 | */ 250 | Fop.prototype.stream = function() { 251 | var recive = new dataStream(); 252 | request(this.url()).pipe(recive); 253 | 254 | return recive; 255 | }; 256 | 257 | module.exports = Fop; 258 | 259 | function genOptUrl(params) { 260 | var _url = ""; 261 | 262 | _.each(params, function(value, key) { 263 | _url += util.format('/%s/%s', key, value); 264 | }); 265 | 266 | return _url; 267 | } 268 | 269 | function noop() { 270 | return false; 271 | } -------------------------------------------------------------------------------- /lib/image.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var _ = require('underscore'); 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var Q = require('q'); 6 | var dataStream = require('dataStream'); 7 | var util = require('util'); 8 | var url = require('url'); 9 | var Asset = require('./asset'); 10 | var utils = require('./utils'); 11 | 12 | /** 13 | * Image Asset 14 | * @param {String} key key 15 | * @param {Bucket} parent bucket object 16 | */ 17 | function Image(key, parent, _config) { 18 | Image.super_.apply(this, arguments); 19 | 20 | var config = _.extend(_.clone(parent.config), { 21 | separate: '-' 22 | }, _config); 23 | 24 | this.key = key; 25 | this.parent = parent; 26 | this.config = config; 27 | } 28 | util.inherits(Image, Asset); 29 | 30 | /** 31 | * get the image's infomations 32 | * @param {Function} callback Callback 33 | * @return {Promise} promise object 34 | */ 35 | Image.prototype.imageInfo = function(callback) { 36 | var deferred = Q.defer(); 37 | callback = callback || noop; 38 | 39 | var getToken = this.token({ 40 | fop: 'imageInfo' 41 | }); 42 | 43 | var _url = getToken.url + '&token=' + getToken.token; 44 | 45 | request(_url, function(err, res, body) { 46 | if (err) { 47 | deferred.reject(err); 48 | return callback(err); 49 | } 50 | 51 | var info = JSON.parse(body); 52 | 53 | deferred.resolve(info); 54 | callback(null, info); 55 | }); 56 | 57 | return deferred.promise; 58 | }; 59 | 60 | /** 61 | * get the exif infomation of the picture 62 | * @param {Function} callback Callback 63 | * @return {Promise} promise object 64 | */ 65 | Image.prototype.exif = function(callback) { 66 | var deferred = Q.defer(); 67 | callback = callback || noop; 68 | 69 | var getToken = this.token({ 70 | fop: 'exif' 71 | }); 72 | 73 | var _url = getToken.url + '&token=' + getToken.token; 74 | 75 | request(_url, function(err, res, body) { 76 | if (err) { 77 | deferred.reject(err); 78 | return callback(err); 79 | } 80 | 81 | var info = JSON.parse(body); 82 | 83 | deferred.resolve(info); 84 | callback(null, info); 85 | }); 86 | 87 | return deferred.promise; 88 | }; 89 | 90 | var imageViewTranslations = { 91 | weight: 'w', 92 | height: 'h', 93 | quality: 'q' 94 | }; 95 | 96 | /** 97 | * return a thumbnail image 98 | * @param {Object} opts options 99 | * @param {Function} callback Callback 100 | * @return {Promise} promise object 101 | */ 102 | Image.prototype.imageView = function(opts, callback) { 103 | var deferred = Q.defer(); 104 | callback = callback || noop; 105 | 106 | var mode = opts.mode; 107 | delete opts.mode; 108 | 109 | var params = {}; 110 | 111 | _.each(opts, function(value, key) { 112 | if (imageViewTranslations.hasOwnProperty(key)) { 113 | key = imageViewTranslations[key]; 114 | } 115 | 116 | params[key] = value; 117 | }); 118 | 119 | var fop = util.format('imageView/%d%s', mode, genOptUrl(params)); 120 | 121 | var getToken = this.token({ 122 | fop: fop 123 | }); 124 | 125 | var _url = getToken.url + '&token=' + getToken.token; 126 | 127 | var recive = new dataStream(); 128 | request(_url).pipe(recive) 129 | .on('complete', function() { 130 | var data = this.body(); 131 | 132 | deferred.resolve(data); 133 | callback(null, data); 134 | }) 135 | .on('error', function(err) { 136 | deferred.reject(err); 137 | callback(err); 138 | }); 139 | 140 | 141 | var promise = deferred.promise; 142 | 143 | /** 144 | * return the image stream 145 | * @return {Stream} stream 146 | */ 147 | promise.stream = function() { 148 | return recive; 149 | }; 150 | 151 | return promise; 152 | }; 153 | 154 | /** 155 | * return a processed image 156 | * @param {Object} opts options 157 | * @param {Function} callback Callback 158 | * @return {Promise} promise object 159 | */ 160 | Image.prototype.imageMogr = function(opts, callback) { 161 | var deferred = Q.defer(); 162 | callback = callback || noop; 163 | 164 | var params = {}; 165 | 166 | _.extend(params, opts); 167 | 168 | var fop = util.format('imageMogr/v2/auto-orient%s', genOptUrl(params)); 169 | 170 | var getToken = this.token({ 171 | fop: fop 172 | }); 173 | 174 | var _url = getToken.url + '&token=' + getToken.token; 175 | 176 | var recive = new dataStream(); 177 | request(_url).pipe(recive) 178 | .on('complete', function() { 179 | var data = this.body(); 180 | 181 | deferred.resolve(data); 182 | callback(null, data); 183 | }) 184 | .on('error', function(err) { 185 | deferred.reject(err); 186 | callback(err); 187 | }); 188 | 189 | var promise = deferred.promise; 190 | 191 | /** 192 | * return the image stream 193 | * @return {Stream} stream 194 | */ 195 | promise.stream = function() { 196 | return recive; 197 | }; 198 | 199 | return promise; 200 | }; 201 | 202 | /** 203 | * return a image with a watermark 204 | * @param {Object} opts options 205 | * @param {Function} callback Callback 206 | * @return {Promise} promise object 207 | */ 208 | Image.prototype.watermark = function(opts, callback) { 209 | var deferred = Q.defer(); 210 | callback = callback || noop; 211 | 212 | var params = {}; 213 | var mode = opts.mode; 214 | delete opts.mode; 215 | 216 | _.extend(params, opts); 217 | 218 | params.image = utils.safeEncode(params.image); 219 | 220 | var fop = util.format('watermark/%d%s', mode, genOptUrl(params)); 221 | 222 | var getToken = this.token({ 223 | fop: fop 224 | }); 225 | 226 | var _url = getToken.url + '&token=' + getToken.token; 227 | 228 | var recive = new dataStream(); 229 | request(_url).pipe(recive) 230 | .on('complete', function() { 231 | var data = this.body(); 232 | 233 | deferred.resolve(data); 234 | callback(null, data); 235 | }) 236 | .on('error', function(err) { 237 | deferred.reject(err); 238 | callback(err); 239 | }); 240 | 241 | var promise = deferred.promise; 242 | 243 | /** 244 | * return the image stream 245 | * @return {Stream} stream 246 | */ 247 | promise.stream = function() { 248 | return recive; 249 | }; 250 | 251 | return promise; 252 | }; 253 | 254 | module.exports = Image; 255 | 256 | function genOptUrl(params) { 257 | var _url = ""; 258 | 259 | _.each(params, function(value, key) { 260 | _url += util.format('/%s/%s', key, value); 261 | }); 262 | 263 | return _url; 264 | } 265 | 266 | function noop() { 267 | return false; 268 | } -------------------------------------------------------------------------------- /lib/qiniu.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | var qiniu = exports; 4 | 5 | qiniu.version = '6.1.5'; 6 | 7 | var _configData = require('./config'); 8 | 9 | qiniu.Bucket = require('./bucket')(_configData); 10 | qiniu.Token = require('./token')(_configData); 11 | qiniu.Asset = require('./asset'); 12 | qiniu.Image = require('./image'); 13 | qiniu.Batch = require('./batch'); 14 | 15 | /** 16 | * Global Config 17 | * Example: 18 | * ``` 19 | * qiniu.config({ 20 | * access_key: '-----', 21 | * secret_key: '-----', 22 | * 23 | * // global token option 24 | * callbackUrl: 'http://images.example.com/upload/callback', 25 | * callbackBody: '{ \ 26 | * "foo" : "bar", \ 27 | * "name" : $(fname), \ 28 | * "size" : $(fsize), \ 29 | * "type" : $(mimeType), \ 30 | * "hash" : $(etag), \ 31 | * "w" : $(imageInfo.width), \ 32 | * "h" : $(imageInfo.height), \ 33 | * "color" : $(exif.ColorSpace.val) \ 34 | * }' 35 | * }); 36 | * 37 | * qiniu.config('foo', 'bar'); 38 | * qiniu.config('foo'); 39 | * ``` 40 | * @param {String/Object} key key of config 41 | * @param {Mix} value value 42 | */ 43 | qiniu.config = function(key, value) { 44 | if (arguments.length > 1 && _.isString(key)) { 45 | // set config data normally 46 | qiniu.set(key, value); 47 | } else { 48 | switch (true) { 49 | case _.isString(key): 50 | // Get config data 51 | return qiniu.get(key); 52 | break; 53 | case _.isObject(key): 54 | // Set config data with a object 55 | _.each(key, function(_value, _key) { 56 | qiniu.set(_key, _value); 57 | }); 58 | break; 59 | } 60 | } 61 | 62 | return this; 63 | }; 64 | 65 | /** 66 | * Set config data 67 | * @param {String} key key 68 | * @param {Mix} value value 69 | * @return {Object} qiniu object 70 | */ 71 | qiniu.set = function(key, value) { 72 | _configData[key] = value; 73 | 74 | return this; 75 | }; 76 | 77 | /** 78 | * Get config data 79 | * @param {String} key key 80 | * @return {Mix} config value 81 | */ 82 | qiniu.get = function(key) { 83 | return _configData[key]; 84 | }; 85 | 86 | qiniu.bucket = function(bucket) { 87 | return new qiniu.Bucket(bucket); 88 | }; 89 | 90 | /** 91 | * Return a batch 92 | * @param {[type]} config [description] 93 | * @return {[type]} [description] 94 | */ 95 | qiniu.batch = function(config) { 96 | return new qiniu.Batch(config); 97 | }; 98 | 99 | var policiesMap = { 100 | 'put': qiniu.Token.PutPolicy, 101 | 'get': qiniu.Token.GetPolicy, 102 | 'access': qiniu.Token.AccessToken 103 | }; 104 | 105 | /** 106 | * generate a token 107 | * @param {String} type Token's type 108 | * @param {Object} opts options 109 | * @return {String} token 110 | */ 111 | qiniu.genToken = function(type, opts) { 112 | var Policy = policiesMap[type]; 113 | 114 | if (!Policy) { 115 | return false; 116 | } 117 | 118 | var policy = new Policy(opts); 119 | 120 | return policy.token(); 121 | }; -------------------------------------------------------------------------------- /lib/token.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Qiniu Token generic 3 | */ 4 | 5 | var _ = require('underscore'); 6 | var util = require('util'); 7 | var url = require('url'); 8 | var qs = require('querystring'); 9 | var utils = require('./utils'); 10 | 11 | var globalConfig = null; 12 | 13 | /** 14 | * Put Policy 15 | * @param {Object} opts options 16 | */ 17 | function PutPolicy(opts) { 18 | var now = Math.floor(Date.now() / 1000); 19 | 20 | this.scope = opts.scope || null; 21 | this.deadline = now + opts.deadline || now + 3600; 22 | this.endUser = opts.endUser || null; 23 | this.returnUrl = opts.returnUrl || null; 24 | this.returnBody = opts.returnBody || null; 25 | this.callbackUrl = opts.callbackUrl || null; 26 | this.callbackBody = opts.callbackBody || null; 27 | this.asyncOps = opts.asyncOps || null; 28 | } 29 | 30 | /** 31 | * Generate the token 32 | * @return {String} token 33 | */ 34 | PutPolicy.prototype.token = function() { 35 | var params = {}; 36 | 37 | _.each(this, function(value, key) { 38 | if (!_.isNull(value)) { 39 | params[key] = value; 40 | } 41 | }); 42 | return generateToken(params); 43 | }; 44 | 45 | function generateToken(params) { 46 | // signature 47 | var signature = utils.safeEncode(JSON.stringify(params)); 48 | 49 | // EncodedDigest 50 | var encodedDigest = utils.encodeSign(signature, globalConfig.secret_key); 51 | 52 | return util.format('%s:%s:%s', globalConfig.access_key, encodedDigest, signature) 53 | } 54 | 55 | function GetPolicy(opts) { 56 | opts = opts || {}; 57 | var now = Math.floor(Date.now() / 1000); 58 | 59 | this.deadline = now + opts.deadline || now + 3600; 60 | } 61 | 62 | GetPolicy.prototype.token = function(domain, key, opts) { 63 | opts = opts || {}; 64 | var fop = opts.fop || 'e'; 65 | var base = ''; 66 | 67 | if (_.isUndefined(key)) { 68 | // base url 69 | var encodedDigest = utils.encodeSign(domain, globalConfig.secret_key); 70 | } else { 71 | // safe domain 72 | var hostname = url.parse(domain).hostname; 73 | 74 | base = url.format({ 75 | protocol: 'http', 76 | hostname: hostname, 77 | pathname: key 78 | }); 79 | 80 | base += '?' + encodeURIComponent(fop); 81 | base += '&' + qs.stringify({ 82 | e: this.deadline 83 | }); 84 | 85 | var encodedDigest = utils.encodeSign(base, globalConfig.secret_key); 86 | } 87 | 88 | var token = util.format('%s:%s', globalConfig.access_key, encodedDigest); 89 | 90 | return { 91 | token: token, 92 | requestUrl: base, 93 | url: base + '&token=' + token 94 | }; 95 | }; 96 | 97 | function AccessToken() {} 98 | 99 | AccessToken.prototype.token = function(path, body) { 100 | body = body || ''; 101 | 102 | var data = util.format('%s\n%s', path, body); 103 | 104 | var encodeSignData = utils.encodeSign(data, globalConfig.secret_key); 105 | 106 | return util.format('QBox %s:%s', globalConfig.access_key, encodeSignData); 107 | }; 108 | module.exports = function(config) { 109 | globalConfig = config; 110 | return { 111 | PutPolicy: PutPolicy, 112 | GetPolicy: GetPolicy, 113 | AccessToken: AccessToken 114 | }; 115 | }; -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto'); 2 | var fs = require('fs'); 3 | 4 | var utils = exports; 5 | 6 | utils.safeEncode = function(str) { 7 | var encoded = new Buffer(str).toString('base64'); 8 | var rtn = encoded.replace(/\//g, '_').replace(/\+/g, '-'); 9 | 10 | return rtn; 11 | }; 12 | 13 | utils.encodeSign = function(str, key) { 14 | return utils.safeEncode( 15 | crypto 16 | .createHmac('sha1', key) 17 | .update(str) 18 | .digest() 19 | ); 20 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-qiniu", 3 | "version": "6.1.5", 4 | "description": "Qiniu Resource Storage SDK for Node.js", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "doc", 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "test": "make test", 12 | "blanket": { 13 | "pattern": "node-qiniu/lib", 14 | "data-cover-flags": { 15 | "debug": false 16 | } 17 | }, 18 | "travis-cov": { 19 | "threshold": 88 20 | } 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/iwillwen/node-qiniu.git" 25 | }, 26 | "keywords": [ 27 | "cloud", 28 | "storage", 29 | "s3", 30 | "qiniu", 31 | "web-service" 32 | ], 33 | "author": "iwillwen", 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/iwillwen/node-qiniu/issues" 37 | }, 38 | "dependencies": { 39 | "underscore": "~1.5.1", 40 | "request": "~2.27.0", 41 | "q": "~0.9.6", 42 | "dataStream": "0.0.5" 43 | }, 44 | "devDependencies": { 45 | "async": "~0.2.9", 46 | "mocha": "~1.6.0", 47 | "should": "~1.2.2", 48 | "doxmate": "*", 49 | "blanket": "*", 50 | "travis-cov": "*" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/assets/gogopher.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwillwen/node-qiniu/e97e4779320769c23175399f2927757bb6a7e56e/test/assets/gogopher.jpg -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var qiniu = require('../'); 2 | 3 | qiniu.config({ 4 | access_key: '5UyUq-l6jsWqZMU6tuQ85Msehrs3Dr58G-mCZ9rE', 5 | secret_key: 'YaRsPKiYm4nGUt8mdz2QxeV5Q_yaUzVxagRuWTfM' 6 | 7 | }); 8 | 9 | qiniu.testBucket = new qiniu.Bucket('qiniu-sdk-test'); 10 | 11 | module.exports = qiniu; 12 | -------------------------------------------------------------------------------- /test/qiniu_asset.js: -------------------------------------------------------------------------------- 1 | var qiniu = require('./'); 2 | var fs = require('fs'); 3 | var assert = require('assert'); 4 | 5 | var asset = qiniu.testBucket.key('gogopher.jpg'); 6 | 7 | describe('qiniu.Asset', function() { 8 | 9 | describe('Asset.url()', function() { 10 | it('should return the url of the asset', function() { 11 | var url = asset.url(); 12 | 13 | assert.equal(url, 'http://qiniu-sdk-test.u.qiniudn.com/gogopher.jpg'); 14 | }); 15 | }); 16 | 17 | describe('Asset.entryUrl()', function() { 18 | it('should return the encoeded entry url of the asset', function() { 19 | var entryUrl = asset.entryUrl(); 20 | 21 | assert.equal(entryUrl, 'cWluaXUtc2RrLXRlc3Q6Z29nb3BoZXIuanBn'); 22 | }); 23 | }); 24 | 25 | describe('Asset.stat()', function() { 26 | it('should get the asset\'s stat', function(done) { 27 | asset.stat(function(err, stat) { 28 | if (err) { 29 | throw err; 30 | } 31 | 32 | done(); 33 | }); 34 | }); 35 | }); 36 | 37 | describe('Asset.move()', function() { 38 | it('should move the asset to another position', function(done) { 39 | var dest = qiniu.testBucket.key('gogopher_tmp.jpg'); 40 | 41 | asset.move(dest, function(err) { 42 | if (err) { 43 | throw err; 44 | } 45 | 46 | done(); 47 | }); 48 | }); 49 | }); 50 | 51 | describe('Asset.copy()', function() { 52 | it('should make a copy of the asset to another position', function(done) { 53 | var src = qiniu.testBucket.key('gogopher_tmp.jpg'); 54 | 55 | src.copy(asset, function(err) { 56 | if (err) { 57 | throw err; 58 | } 59 | 60 | done(); 61 | }); 62 | }); 63 | }); 64 | 65 | describe('Asset.remove()', function() { 66 | it('should delete the asset', function(done) { 67 | var tmp = qiniu.testBucket.key('gogopher_tmp.jpg'); 68 | 69 | tmp.remove(function(err) { 70 | if (err) { 71 | throw err; 72 | } 73 | 74 | done(); 75 | }); 76 | }); 77 | }); 78 | 79 | describe('Asset.qrcode()', function() { 80 | it('should return the qrcode image of the asset', function(done) { 81 | 82 | // imageView 83 | asset.qrcode(function(err, data) { 84 | if (err) { 85 | throw err; 86 | } 87 | 88 | fs.writeFile(__dirname + '/assets/gogopher_qrcode.png', data, function(err) { 89 | done(); 90 | }); 91 | }); 92 | 93 | }); 94 | }); 95 | 96 | describe('Asset.qrcode().straem()', function() { 97 | it('should return the qrcode image of the asset by a stream', function(done) { 98 | 99 | // imageView Stream 100 | var qrcodeStream = asset.qrcode().stream(); 101 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_qrcode.png'); 102 | 103 | qrcodeStream.pipe(writingStream) 104 | .on('error', function(err) { 105 | throw err; 106 | }) 107 | .on('finish', function() { 108 | done(); 109 | }); 110 | }); 111 | }); 112 | 113 | describe('Asset.md2html()', function() { 114 | it('should convert Markdown to HTML', function(done) { 115 | 116 | qiniu.testBucket.key('sample.md').md2html(function(err, html) { 117 | if (err) { 118 | throw err; 119 | } 120 | 121 | done(); 122 | }); 123 | }); 124 | }); 125 | 126 | }); -------------------------------------------------------------------------------- /test/qiniu_batch.js: -------------------------------------------------------------------------------- 1 | var qiniu = require('./'); 2 | var assert = require('assert'); 3 | 4 | var bucket = qiniu.testBucket; 5 | 6 | describe('qiniu.Batch', function() { 7 | 8 | describe('Batch.stat()', function() { 9 | it('should get the assets\' stats', function(done) { 10 | qiniu.batch() 11 | .stat(bucket.key('gogopher.jpg')) 12 | .exec(function(err, stat) { 13 | if (err) { 14 | throw err; 15 | } 16 | 17 | done(); 18 | }); 19 | }); 20 | }); 21 | 22 | describe('Batch.move()', function() { 23 | it('should move the asset to another position', function(done) { 24 | qiniu.batch() 25 | .move(bucket.key('gogopher.jpg'), bucket.key('gogopher_tmp.jpg')) 26 | .exec(function(err) { 27 | if (err) { 28 | throw err; 29 | } 30 | 31 | done(); 32 | }); 33 | }); 34 | }); 35 | 36 | describe('Batch.copy()', function() { 37 | it('should make some copies of the assets to the other positions', function(done) { 38 | qiniu.batch() 39 | .move(bucket.key('gogopher_tmp.jpg'), bucket.key('gogopher.jpg')) 40 | .exec(function(err) { 41 | if (err) { 42 | throw err; 43 | } 44 | 45 | done(); 46 | }); 47 | }); 48 | }); 49 | 50 | describe('Batch.remove()', function() { 51 | it('should delete the assets', function(done) { 52 | qiniu.batch() 53 | .remove(bucket.key('gogopher_tmp.jpg')) 54 | .exec(function(err, stat) { 55 | if (err) { 56 | throw err; 57 | } 58 | 59 | done(); 60 | }); 61 | }); 62 | }); 63 | 64 | }); -------------------------------------------------------------------------------- /test/qiniu_fop.js: -------------------------------------------------------------------------------- 1 | var qiniu = require('./'); 2 | var fs = require('fs'); 3 | var dataStream = require('dataStream'); 4 | var assert = require('assert'); 5 | 6 | var asset = qiniu.testBucket.key('gogopher.jpg'); 7 | 8 | describe('qiniu.Asset.Fop', function() { 9 | 10 | describe('Fop.imageInfo()', function() { 11 | it('should return some infomations of a picture', function(done) { 12 | 13 | asset.fop() 14 | .imageInfo() 15 | .exec(function(err, data) { 16 | if (err) { 17 | throw err; 18 | } 19 | 20 | done(); 21 | }); 22 | 23 | }); 24 | }); 25 | 26 | describe('Fop.exif()', function() { 27 | it('should return the exif infomation of a picture', function(done) { 28 | 29 | asset.fop() 30 | .exif() 31 | .exec(function(err) { 32 | if (err) { 33 | throw err; 34 | } 35 | 36 | done(); 37 | }); 38 | 39 | }); 40 | }); 41 | 42 | describe('Fop.imageView()', function() { 43 | it('should return a thumbnail asset', function(done) { 44 | 45 | // imageView 46 | asset.fop() 47 | .imageView({ 48 | mode : 2, 49 | width : 180, 50 | height : 180, 51 | quality : 85, 52 | format : 'jpg' 53 | }) 54 | .exec(function(err, thumbnail) { 55 | if (err) { 56 | throw err; 57 | } 58 | 59 | fs.writeFile(__dirname + '/assets/gogopher_thumbnail.jpg', thumbnail, function(err) { 60 | done(); 61 | }); 62 | }); 63 | 64 | }); 65 | }); 66 | 67 | describe('Fop.imageView().straem()', function() { 68 | it('should return a thumbnail asset by a stream', function(done) { 69 | 70 | // imageView Stream 71 | var imageStream = asset.fop() 72 | .imageView({ 73 | mode : 2, 74 | width : 180, 75 | height : 180, 76 | quality : 85, 77 | format : 'jpg' 78 | }) 79 | .stream(); 80 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_thumbnail.jpg'); 81 | 82 | imageStream.pipe(writingStream) 83 | .on('error', function(err) { 84 | throw err; 85 | }) 86 | .on('finish', function() { 87 | done(); 88 | }); 89 | }); 90 | }); 91 | 92 | describe('Fop.imageMogr()', function() { 93 | it('should return a processed asset', function(done) { 94 | 95 | // imageMogr 96 | asset.fop() 97 | .imageMogr({ 98 | thumbnail : '300x500', 99 | gravity : 'NorthWest', 100 | crop : '!300x400a10a10', 101 | quality : 85, 102 | rotate : 90, 103 | format : 'jpg' 104 | }) 105 | .exec(function(err, mogred) { 106 | if (err) { 107 | throw err; 108 | } 109 | 110 | fs.writeFile(__dirname + '/assets/gogopher_mogred.jpg', mogred, function(err) { 111 | done(); 112 | }); 113 | }); 114 | 115 | }); 116 | }); 117 | 118 | describe('Fop.imageMogr().stream()', function() { 119 | it('should return a processed asset by a stream', function(done) { 120 | 121 | // imageMogr 122 | var imageStream = asset.fop() 123 | .imageMogr({ 124 | thumbnail : '300x500', 125 | gravity : 'NorthWest', 126 | crop : '!300x400a10a10', 127 | quality : 85, 128 | rotate : 90, 129 | format : 'jpg' 130 | }) 131 | .stream(); 132 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_mogred.jpg'); 133 | 134 | imageStream.pipe(writingStream) 135 | .on('error', function(err) { 136 | throw err; 137 | }) 138 | .on('finish', function() { 139 | done(); 140 | }); 141 | 142 | }); 143 | }); 144 | 145 | describe('Fop.watermark()', function() { 146 | it('should return a asset with a watermark', function(done) { 147 | 148 | // watermarks 149 | asset.fop() 150 | .watermark({ 151 | mode: 1, 152 | image: 'http://www.b1.qiniudn.com/images/logo-2.png', 153 | dissolve: 70, 154 | gravity: 'SouthEast', 155 | dx: 20, 156 | dy: 20 157 | }) 158 | .exec(function(err, pic) { 159 | if (err) { 160 | throw err; 161 | } 162 | 163 | fs.writeFile(__dirname + '/assets/gogopher_watermark.jpg', pic, function(err) { 164 | done(); 165 | }); 166 | }); 167 | 168 | }); 169 | }); 170 | 171 | describe('Fop.watermark().stream()', function() { 172 | it('should return a asset with a watermark by stream', function(done) { 173 | 174 | // watermarks 175 | var imageStream = asset.fop() 176 | .watermark({ 177 | mode: 1, 178 | image: 'http://www.b1.qiniudn.com/images/logo-2.png', 179 | dissolve: 70, 180 | gravity: 'SouthEast', 181 | dx: 20, 182 | dy: 20 183 | }) 184 | .stream(); 185 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_watermark.jpg'); 186 | 187 | imageStream.pipe(writingStream) 188 | .on('error', function(err) { 189 | throw err; 190 | }) 191 | .on('finish', function() { 192 | done(); 193 | }); 194 | 195 | }); 196 | }); 197 | 198 | describe('Fop.qrcode()', function() { 199 | it('should return the qrcode image of the asset', function(done) { 200 | 201 | // watermarks 202 | asset.fop() 203 | .qrcode() 204 | .exec(function(err, pic) { 205 | if (err) { 206 | throw err; 207 | } 208 | 209 | fs.writeFile(__dirname + '/assets/gogopher_qrcode.png', pic, function(err) { 210 | done(); 211 | }); 212 | }); 213 | 214 | }); 215 | }); 216 | 217 | describe('Fop.qrcode().stream()', function() { 218 | it('should return the qrcode image of the asset by stream', function(done) { 219 | 220 | // watermarks 221 | var imageStream = asset.fop() 222 | .qrcode() 223 | .stream(); 224 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_qrcode.png'); 225 | 226 | imageStream.pipe(writingStream) 227 | .on('error', function(err) { 228 | throw err; 229 | }) 230 | .on('finish', function() { 231 | done(); 232 | }); 233 | 234 | }); 235 | }); 236 | 237 | describe('Fop.md2html()', function() { 238 | it('should convert Mardkdown to HTML', function(done) { 239 | 240 | // watermarks 241 | qiniu.testBucket.key('sample.md').fop() 242 | .md2html() 243 | .exec(function(err, html) { 244 | if (err) { 245 | throw err; 246 | } 247 | 248 | fs.writeFile(__dirname + '/assets/sample.html', html, function(err) { 249 | done(); 250 | }); 251 | }); 252 | 253 | }); 254 | }); 255 | 256 | }); -------------------------------------------------------------------------------- /test/qiniu_get.js: -------------------------------------------------------------------------------- 1 | var qiniu = require('./'); 2 | var fs = require('fs'); 3 | var assert = require('assert'); 4 | 5 | describe('qiniu.Get', function() { 6 | 7 | describe('Bucket.getFile()', function() { 8 | it('should gets a file with giving a pathname', function(done) { 9 | 10 | qiniu.testBucket.getFile('gogopher.jpg', function(err) { 11 | if (err) { 12 | throw err; 13 | } 14 | 15 | done(); 16 | }); 17 | 18 | }); 19 | }); 20 | 21 | describe('Bucket.createGetStream()', function() { 22 | it('should gets a file with a stream', function(done) { 23 | 24 | var gettingStream = qiniu.testBucket.createGetStream('gogopher.jpg'); 25 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_tmp.jpg'); 26 | 27 | gettingStream.pipe(writingStream) 28 | .on('error', function(err) { 29 | throw err; 30 | }) 31 | .on('finish', done); 32 | }); 33 | }); 34 | 35 | }); -------------------------------------------------------------------------------- /test/qiniu_image.js: -------------------------------------------------------------------------------- 1 | var qiniu = require('./'); 2 | var fs = require('fs'); 3 | var dataStream = require('dataStream'); 4 | var assert = require('assert'); 5 | 6 | var image = qiniu.testBucket.image('gogopher.jpg'); 7 | 8 | describe('qiniu.Image', function() { 9 | 10 | describe('Image.imageInfo()', function() { 11 | it('should return some infomations of a picture', function(done) { 12 | 13 | image.imageInfo(function(err, data) { 14 | if (err) { 15 | throw err; 16 | } 17 | 18 | done(); 19 | }); 20 | 21 | }); 22 | }); 23 | 24 | describe('Image.exif()', function() { 25 | it('should return the exif infomation of a picture', function(done) { 26 | 27 | image.exif(function(err) { 28 | if (err) { 29 | throw err; 30 | } 31 | 32 | done(); 33 | }); 34 | 35 | }); 36 | }); 37 | 38 | describe('Image.imageView()', function() { 39 | it('should return a thumbnail image', function(done) { 40 | 41 | // imageView 42 | image.imageView({ 43 | mode : 2, 44 | width : 180, 45 | height : 180, 46 | quality : 85, 47 | format : 'jpg' 48 | }, function(err, thumbnail) { 49 | if (err) { 50 | throw err; 51 | } 52 | 53 | fs.writeFile(__dirname + '/assets/gogopher_thumbnail.jpg', thumbnail, function(err) { 54 | done(); 55 | }); 56 | }); 57 | 58 | }); 59 | }); 60 | 61 | describe('Image.imageView().straem()', function() { 62 | it('should return a thumbnail image by a stream', function(done) { 63 | 64 | // imageView Stream 65 | var imageStream = image.imageView({ 66 | mode : 2, 67 | width : 180, 68 | height : 180, 69 | quality : 85, 70 | format : 'jpg' 71 | }).stream(); 72 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_thumbnail.jpg'); 73 | 74 | imageStream.pipe(writingStream) 75 | .on('error', function(err) { 76 | throw err; 77 | }) 78 | .on('finish', function() { 79 | done(); 80 | }); 81 | }); 82 | }); 83 | 84 | describe('Image.imageMogr()', function() { 85 | it('should return a processed image', function(done) { 86 | 87 | // imageMogr 88 | image.imageMogr({ 89 | thumbnail : '300x500', 90 | gravity : 'NorthWest', 91 | crop : '!300x400a10a10', 92 | quality : 85, 93 | rotate : 90, 94 | format : 'jpg' 95 | }, function(err, mogred) { 96 | if (err) { 97 | throw err; 98 | } 99 | 100 | fs.writeFile(__dirname + '/assets/gogopher_mogred.jpg', mogred, function(err) { 101 | done(); 102 | }); 103 | }); 104 | 105 | }); 106 | }); 107 | 108 | describe('Image.imageMogr().stream()', function() { 109 | it('should return a processed image by a stream', function(done) { 110 | 111 | // imageMogr 112 | var imageStream = image.imageMogr({ 113 | thumbnail : '300x500', 114 | gravity : 'NorthWest', 115 | crop : '!300x400a10a10', 116 | quality : 85, 117 | rotate : 90, 118 | format : 'jpg' 119 | }).stream(); 120 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_mogred.jpg'); 121 | 122 | imageStream.pipe(writingStream) 123 | .on('error', function(err) { 124 | throw err; 125 | }) 126 | .on('finish', function() { 127 | done(); 128 | }); 129 | 130 | }); 131 | }); 132 | 133 | describe('Image.watermark()', function() { 134 | it('should return a image with a watermark', function(done) { 135 | 136 | // watermarks 137 | image.watermark({ 138 | mode: 1, 139 | image: 'http://www.b1.qiniudn.com/images/logo-2.png', 140 | dissolve: 70, 141 | gravity: 'SouthEast', 142 | dx: 20, 143 | dy: 20 144 | }, function(err, pic) { 145 | if (err) { 146 | throw err; 147 | } 148 | 149 | fs.writeFile(__dirname + '/assets/gogopher_watermark.jpg', pic, function(err) { 150 | done(); 151 | }); 152 | }); 153 | 154 | }); 155 | }); 156 | 157 | describe('Image.watermark().stream()', function() { 158 | it('should return a image with a watermark by stream', function(done) { 159 | 160 | // watermarks 161 | var imageStream = image.watermark({ 162 | mode: 1, 163 | image: 'http://www.b1.qiniudn.com/images/logo-2.png', 164 | dissolve: 70, 165 | gravity: 'SouthEast', 166 | dx: 20, 167 | dy: 20 168 | }).stream(); 169 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_watermark.jpg'); 170 | 171 | imageStream.pipe(writingStream) 172 | .on('error', function(err) { 173 | throw err; 174 | }) 175 | .on('finish', function() { 176 | done(); 177 | }); 178 | 179 | }); 180 | }); 181 | 182 | describe('Image.alias()', function() { 183 | it('should return a image with a established format', function(done) { 184 | 185 | // watermarks 186 | image.alias('testalias', function(err, pic) { 187 | if (err) { 188 | throw err; 189 | } 190 | 191 | fs.writeFile(__dirname + '/assets/gogopher_alias.jpg', pic, function(err) { 192 | done(); 193 | }); 194 | }); 195 | 196 | }); 197 | }); 198 | 199 | describe('Image.alias().stream()', function() { 200 | it('should return a image with a established format by stream', function(done) { 201 | 202 | // watermarks 203 | var imageStream = image.alias('testalias').stream(); 204 | var writingStream = fs.createWriteStream(__dirname + '/assets/gogopher_alias.jpg'); 205 | 206 | imageStream.pipe(writingStream) 207 | .on('error', function(err) { 208 | throw err; 209 | }) 210 | .on('finish', function() { 211 | done(); 212 | }); 213 | 214 | }); 215 | }); 216 | 217 | }); -------------------------------------------------------------------------------- /test/qiniu_put.js: -------------------------------------------------------------------------------- 1 | var qiniu = require('./'); 2 | var fs = require('fs'); 3 | var assert = require('assert'); 4 | 5 | describe('qiniu.Put', function() { 6 | 7 | describe('Bucket.putFile()', function() { 8 | it('should uploads a file with giving a pathname', function(done) { 9 | 10 | qiniu.testBucket.putFile('gogopher.jpg', __dirname + '/assets/gogopher.jpg', function(err, reply) { 11 | if (err) { 12 | throw err; 13 | } 14 | 15 | done(); 16 | }); 17 | 18 | }); 19 | }); 20 | 21 | describe('Bucket.createPutStream()', function() { 22 | it('should uploads a file with a stream', function(done) { 23 | 24 | var puttingStream = qiniu.testBucket.createPutStream('gogopher.jpg'); 25 | var readingStream = fs.createReadStream(__dirname + '/assets/gogopher.jpg'); 26 | 27 | readingStream.pipe(puttingStream) 28 | .on('error', function(err) { 29 | throw err; 30 | }) 31 | .on('complete', function() { 32 | done(); 33 | }); 34 | }); 35 | }); 36 | 37 | }); --------------------------------------------------------------------------------