qn
Version:
Another qiniu API client for Node.js.
189 lines (170 loc) • 7.05 kB
JavaScript
/*!
* qn - lib/up.js
* Copyright(c) 2013 fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* MIT Licensed
*/
;
/**
* Module dependencies.
*/
var utility = require('utility');
var debug = require('debug')('qn:up');
var fs = require('fs');
var path = require('path');
var FormStream = require('formstream');
var bt = require('buffer-type');
var utils = require('./utils');
var version = require('../package.json').version;
/**
* Create uploadToken
* @see http://docs.qiniu.com/api/v6/put.html#uploadToken
*
* @param {Object} options
* - {String} scope 一般指文件要上传到的目标存储空间(Bucket)。
* 若为”Bucket”,表示限定只能传到该Bucket(仅限于新增文件);若为”Bucket:Key”,表示限定特定的文件,可修改该文件。
* - {Number} [deadline] 定义 uploadToken 的失效时间,Unix时间戳,精确到秒,缺省为 3600 秒
* - {String} [endUser] 给上传的文件添加唯一属主标识,特殊场景下非常有用,比如根据终端用户标识给图片或视频打水印
* - {String} [returnUrl] 设置用于浏览器端文件上传成功后,浏览器执行301跳转的URL,一般为 HTML Form 上传时使用。
* 文件上传成功后会跳转到 returnUrl?query_string, query_string 会包含 returnBody 内容。
* returnUrl 不可与 callbackUrl 同时使用。
* - {String} [returnBody] 文件上传成功后,自定义从 Qiniu-Cloud-Server 最终返回給终端 App-Client 的数据。
* 支持 魔法变量,不可与 callbackBody 同时使用。
* - {String} [callbackBody] 文件上传成功后,Qiniu-Cloud-Server 向 App-Server 发送POST请求的数据。
* 支持 魔法变量 和 自定义变量,不可与 returnBody 同时使用。
* - {String} [callbackUrl] 文件上传成功后,Qiniu-Cloud-Server 向 App-Server 发送POST请求的URL,
* 必须是公网上可以正常进行POST请求并能响应 HTTP Status 200 OK 的有效 URL
* - {String} [asyncOps] 指定文件(图片/音频/视频)上传成功后异步地执行指定的预转操作。
* 每个预转指令是一个API规格字符串,多个预转指令可以使用分号“;”隔开
* @return {String} upload token string
*/
exports.uploadToken = function uploadToken(options) {
options = options || {};
options.scope = options.scope || this.options.bucket;
options.deadline = options.deadline || (utility.timestamp() + 3600);
var flags = options;
// 步骤2:将 Flags 进行安全编码
var encodedFlags = utility.base64encode(JSON.stringify(flags), true);
// 步骤3:将编码后的元数据混入私钥进行签名
// 步骤4:将签名摘要值进行安全编码
var encodedSign = this.signData(encodedFlags);
// 步骤5:连接各字符串,生成上传授权凭证
return this.options.accessKey + ':' + encodedSign + ':' + encodedFlags;
};
/**
* Upload file content
*
* @param {String|Buffer|Stream} file content string or buffer, or a Stream instance.
* @param {Object} [options]
* - {String} [key] 标识文件的索引,所在的存储空间内唯一。key可包含斜杠,但不以斜杠开头,比如 a/b/c.jpg 是一个合法的key。
* 若不指定 key,缺省使用文件的 etag(即上传成功后返回的hash值)作为key;
* 此时若 UploadToken 有指定 returnUrl 选项,则文件上传成功后跳转到 returnUrl?query_string,
* query_string 包含key={FileID}
* - {String} [x:custom_field_name] 自定义变量,必须以 x: 开头命名,不限个数。
* 可以在 uploadToken 的 callbackBody 选项中使用 $(x:custom_field_name) 求值。
* - {String} [filename]
* - {String} [contentType]
* - {Number} [size]
* @param {Function(err, result)} callback
* - {Object} result
* - {String} hash
* - {String} key
* - {String} url
*/
exports.upload = function upload(content, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (typeof content === 'string') {
content = new Buffer(content);
}
options = options || {};
options.filename = options.filename || options.key;
if (Buffer.isBuffer(content) && !options.filename && !options.contentType) {
// try to guess contentType from buffer
var info = bt(content);
if (info) {
options.contentType = info.type;
options.filename = 'file' + info.extension;
}
}
// if (!options.filename) {
// options.filename = 'unknowfile';
// }
var form = content;
if (!(content instanceof FormStream)) {
form = new FormStream();
if (Buffer.isBuffer(content)) {
form.buffer('file', content, options.filename, options.contentType);
} else {
// stream
if (content.path) {
// try to get filename in `stream.path`
options['x:filename'] = options['x:filename'] || path.basename(content.path);
if (!options.filename) {
options.filename = options['x:filename'];
}
}
form.stream('file', content, options.filename, options.contentType, options.size);
}
}
var that = this;
form.field('token', that.uploadToken());
if (options.key) {
form.field('key', this.resourceKey(String(options.key)));
}
for (var k in options) {
if (k.indexOf('x:') === 0) {
form.field(k, String(options[k]));
}
}
var headers = form.headers();
headers['User-Agent'] = 'qn/' + version;
var reqOptions = {
method: 'POST',
dataType: 'json',
headers: headers,
timeout: options.timeout || that.options.timeout,
stream: form,
};
that._request(that._uploadURL, reqOptions, function (err, data, res) {
if (err) {
return callback(err, data, res);
}
if (data) {
data.url = that.resourceURL(data.key);
}
callback(null, data, res);
});
};
/**
* Upload a file
*
* @param {String} filepath file full path
* @param {Object} [options] options use on `upload()`
* @param {Function(err, result)} callback
*/
exports.uploadFile = function (filepath, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
var that = this;
fs.stat(filepath, function (err, stat) {
if (err) {
return callback(err);
}
var form = new FormStream();
form.file('file', filepath, stat.size);
options = options || {};
options['x:filename'] = options['x:filename'] || path.basename(filepath);
options['x:size'] = options['x:size'] || stat.size;
if (stat.mtime) {
options['x:mtime'] = options['x:mtime'] || stat.mtime.getTime() / 1000;
}
if (stat.ctime) {
options['x:ctime'] = options['x:ctime'] || stat.ctime.getTime() / 1000;
}
that.upload(form, options, callback);
});
};