Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ results

node_modules
npm-debug.log
demo/test.jpg
test/config.js
demo/*.jpg
demo/rtest.js
demo/itest.js
demo/put.sh
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ language: node_js
node_js:
- 0.8
- 0.6
- 0.4
before_script:
- export QINIU_ACCESS_KEY="iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV"
- export QINIU_SECRET_KEY="6QTOr2Jg1gcZEWDQXKOGZh5PziC2MCV5KsntT70j"
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Node wrapper for Qiniu Resource (Cloud) Storage API

[![Build Status](https://travis-ci.org/qiniu/nodejs-sdk.png?branch=master)](https://travis-ci.org/qiniu/nodejs-sdk)

![logo](http://qiniutek.com/images/logo-2.png)

该 SDK 适用于 NodeJS 0.4.7 及其以上版本,基于 [七牛云存储官方API](/v3/api/) 构建。若您的服务端是一个基于 NodeJS 编写的网络程序,使用此 SDK ,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。以便让您应用的终端用户进行高速上传和下载,同时也使得您的服务端更加轻盈。
Expand Down
69 changes: 55 additions & 14 deletions lib/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,53 @@ var crypto = require('crypto');
var config = require("./conf.js");
var util = require('./util.js');

exports.UploadToken = UploadToken;
exports.DownloadToken = DownloadToken;

// ------------------------------------------------------------------------------------------
// func generateSignature

function generateSignature(params) {
var paramsString = JSON.stringify(params);
return util.encode(paramsString);
}

// ------------------------------------------------------------------------------------------
// func generateEncodedDigest

function generateEncodedDigest(signature) {
var hmac = crypto.createHmac('sha1', config.SECRET_KEY);
hmac.update(signature);
var digest = hmac.digest('base64');
return util.base64ToUrlsafe(digest);
}

// ------------------------------------------------------------------------------------------
// func generateToken

function generateToken(params) {
var signature = generateSignature(params);
var encodedDigest = generateEncodedDigest(signature);
return config.ACCESS_KEY + ":" + encodedDigest + ":" + signature;
}

// ------------------------------------------------------------------------------------------
// type UploadToken

function UploadToken(opts) {
this.scope = opts.scope || null;
this.expires = opts.expires || 3600;
this.callbackUrl = opts.callbackUrl || null;
this.callbackBodyType = opts.callbackBodyType || null;
this.customer = opts.customer || null;
this.escape = opts.escape || 0;
this.asyncOps = opts.asyncOps || null;
}

UploadToken.prototype.generateSignature = function() {
UploadToken.prototype.generateToken = function() {
var params = {
"deadline": this.expires + Math.floor(Date.now() / 1000),
"escape": this.excape,
};
if (this.scope !== null) {
params["scope"] = this.scope;
Expand All @@ -26,21 +62,26 @@ UploadToken.prototype.generateSignature = function() {
if (this.customer !== null) {
params["customer"] = this.customer;
}
var paramsString = JSON.stringify(params)
return util.encode(paramsString);
if (this.asyncOps !== null) {
params["asyncOps"] = this.asyncOps;
}
return generateToken(params);
};

UploadToken.prototype.generateEncodedDigest = function(signature) {
var hmac = crypto.createHmac('sha1', config.SECRET_KEY);
hmac.update(signature);
var digest = hmac.digest('base64');
return util.base64ToUrlsafe(digest);
};
// ------------------------------------------------------------------------------------------
// type DownloadToken

UploadToken.prototype.generateToken = function() {
var signature = this.generateSignature();
var encoded_digest = this.generateEncodedDigest(signature);
return config.ACCESS_KEY + ":" + encoded_digest + ":" + signature;
function DownloadToken(opts) {
this.expires = opts.expires || 3600;
this.pattern = opts.pattern || "*/*";
}

DownloadToken.prototype.generateToken = function() {
var params = {
S: this.pattern,
E: this.expires + Math.floor(Date.now() / 1000),
};
return generateToken(params);
};

exports.UploadToken = UploadToken;
// ------------------------------------------------------------------------------------------
96 changes: 96 additions & 0 deletions lib/rs.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,64 @@ Service.prototype.drop = function(onret) {
this.conn.callWith(url, null, onret);
};

Service.prototype.copy = function(sourceBucket, sourceKey, targetBucket, targetKey, onret) {
/*
* func Copy(sourceBucket, sourceKey, targetBucket, targetKey, onret) => Bool
* 拷贝某个资源表中的文件到另一个资源表中的某个文件
*/
var url = config.RS_HOST + generateMoveOrCopyOpString('copy', sourceBucket, sourceKey, targetBucket, targetKey);
this.conn.callWith(url, null, onret);
};

Service.prototype.move = function(sourceBucket, sourceKey, targetBucket, targetKey, onret) {
/*
* func Move(sourceBucket, sourceKey, targetBucket, targetKey, onret) => Bool
* 移动某个资源表中的文件到另一个资源表中的某个文件
*/
var url = config.RS_HOST + generateMoveOrCopyOpString('move', sourceBucket, sourceKey, targetBucket, targetKey);
this.conn.callWith(url, null, onret);
};

Service.prototype.batchGet = function(bucket, keys, onret) {
/*
* func BatchGet(bucket, keys, onret) => GetRet[]
* 为每个key生成一个短期有效的下载地址
*/
batch(this, "get", bucket, keys, onret);
};

Service.prototype.batchStat = function(bucket, keys, onret) {
/*
* func BatchStat(bucket, keys, onret) => Entry[]
* 查看每个key所对应文件的属性
*/
batch(this, "stat", bucket, keys, onret);
};

Service.prototype.batchDelete = function(bucket, keys, onret) {
/*
* func BatchDelete(bucket, keys, onret) => Bool[]
* 批量删除每个key所对应的资源
*/
batch(this, "delete", bucket, keys, onret);
};

Service.prototype.batchCopy = function(entries, onret) {
/*
* func BatchCopy(entries, onret) => Bool[]
* 批量拷贝文件
*/
batchMoveOrCopy(this, 'copy', entries, onret);
};

Service.prototype.batchMove = function(entries, onret) {
/*
* func BatchMove(entries, onret) => Bool[]
* 批量移动文件
*/
batchMoveOrCopy(this, 'move', entries, onret);
};

/*
* 持久化存储一个经过云端服务处理过后的资源
*/
Expand Down Expand Up @@ -310,4 +368,42 @@ Service.prototype.unsetStyle = function(name, onret){
this.conn.callWith(url, null, onret);
};


// ------------------------------------------------------------------------------------------
// private functions

function generateMoveOrCopyOpString(command, sourceBucket, sourceKey, targetBucket, targetKey) {
var sourceEntryURI = sourceBucket + ":" + sourceKey;
var targetEntryURI = targetBucket + ":" + targetKey;
var url = '/' + command + '/' + util.encode(sourceEntryURI) + '/' + util.encode(targetEntryURI);
return url;
};

function batch(rs, command, bucket, keys, onret) {
var ops = []
, length = keys.length
, url = config.RS_HOST + '/batch?';

for(var i = 0; i < length; i++) {
console.log("Entry URI is: ", bucket + ":" + keys[i]);
var encodedEntryURI = util.encode(bucket + ":" + keys[i]);
ops.push("op=/" + command + "/" + encodedEntryURI);
}
url += ops.join("&");
console.log("Batch URL: ", url);
rs.conn.callWith(url, null, onret);
}

function batchMoveOrCopy(rs, command, entries, onret) {
var ops = []
, length = ops.length
, url = config.RS_HOST + '/batch?';

for(var i = 0; i < length; i++) {
ops.push('op=' + moveOrCopy(command, entries[i][0], entries[i][1], entries[i][2], entries[i][3]));
}
url += ops.join("&");
rs.conn.callWith(url, null, onret);
}

// ------------------------------------------------------------------------------------------
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"test": "test"
},
"scripts": {
"test": "echo \"Error => no test specified\" && exit 1\""
"test": "make test"
},
"repository": {
"type": "git",
Expand Down
5 changes: 0 additions & 5 deletions test/config.js.default

This file was deleted.

Loading