UNPKG

qn

Version:

Another qiniu API client for Node.js.

189 lines (170 loc) 7.05 kB
/*! * qn - lib/up.js * Copyright(c) 2013 fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * MIT Licensed */ "use strict"; /** * 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); }); };