qiniu-js-sdk
Version:
Qiniu JavaScript SDK Node Module
839 lines (745 loc) • 31.9 kB
JavaScript
/*global plupload ,mOxie*/
/*global ActiveXObject */
/*exported Qiniu */
function QiniuJsSDK() {
this.detectIEVersion = function() {
var v = 4,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '<!--[if gt IE ' + v + ']><i></i><![endif]-->',
all[0]
) {
v++;
}
return v > 4 ? v : false;
};
this.isImage = function(url) {
var res, suffix = "";
var imageSuffixes = ["png", "jpg", "jpeg", "gif", "bmp"];
var suffixMatch = /\.([a-zA-Z0-9]+)(\?|\@|$)/;
if (!url || !suffixMatch.test(url)) {
return false;
}
res = suffixMatch.exec(url);
suffix = res[1].toLowerCase();
for (var i = 0, l = imageSuffixes.length; i < l; i++) {
if (suffix === imageSuffixes[i]) {
return true;
}
}
return false;
};
this.getFileExtension = function(filename) {
var tempArr = filename.split(".");
var ext;
if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) {
ext = "";
} else {
ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case
}
return ext;
};
this.utf8_encode = function(argString) {
// http://kevin.vanzonneveld.net
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: sowberry
// + tweaked by: Jack
// + bugfixed by: Onno Marsman
// + improved by: Yves Sucaet
// + bugfixed by: Onno Marsman
// + bugfixed by: Ulrich
// + bugfixed by: Rafal Kukawski
// + improved by: kirilloid
// + bugfixed by: kirilloid
// * example 1: this.utf8_encode('Kevin van Zonneveld');
// * returns 1: 'Kevin van Zonneveld'
if (argString === null || typeof argString === 'undefined') {
return '';
}
var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n');
var utftext = '',
start, end, stringl = 0;
start = end = 0;
stringl = string.length;
for (var n = 0; n < stringl; n++) {
var c1 = string.charCodeAt(n);
var enc = null;
if (c1 < 128) {
end++;
} else if (c1 > 127 && c1 < 2048) {
enc = String.fromCharCode(
(c1 >> 6) | 192, (c1 & 63) | 128
);
} else if (c1 & 0xF800 ^ 0xD800 > 0) {
enc = String.fromCharCode(
(c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
} else { // surrogate pairs
if (c1 & 0xFC00 ^ 0xD800 > 0) {
throw new RangeError('Unmatched trail surrogate at ' + n);
}
var c2 = string.charCodeAt(++n);
if (c2 & 0xFC00 ^ 0xDC00 > 0) {
throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
}
c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
enc = String.fromCharCode(
(c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
}
if (enc !== null) {
if (end > start) {
utftext += string.slice(start, end);
}
utftext += enc;
start = end = n + 1;
}
}
if (end > start) {
utftext += string.slice(start, stringl);
}
return utftext;
};
this.base64_encode = function(data) {
// http://kevin.vanzonneveld.net
// + original by: Tyler Akins (http://rumkin.com)
// + improved by: Bayron Guevara
// + improved by: Thunder.m
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Pellentesque Malesuada
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// - depends on: this.utf8_encode
// * example 1: this.base64_encode('Kevin van Zonneveld');
// * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
// mozilla has this native
// - but breaks in 2.0.0.12!
//if (typeof this.window['atob'] == 'function') {
// return atob(data);
//}
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
ac = 0,
enc = '',
tmp_arr = [];
if (!data) {
return data;
}
data = this.utf8_encode(data + '');
do { // pack three octets into four hexets
o1 = data.charCodeAt(i++);
o2 = data.charCodeAt(i++);
o3 = data.charCodeAt(i++);
bits = o1 << 16 | o2 << 8 | o3;
h1 = bits >> 18 & 0x3f;
h2 = bits >> 12 & 0x3f;
h3 = bits >> 6 & 0x3f;
h4 = bits & 0x3f;
// use hexets to index into b64, and append result to encoded string
tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
} while (i < data.length);
enc = tmp_arr.join('');
switch (data.length % 3) {
case 1:
enc = enc.slice(0, -2) + '==';
break;
case 2:
enc = enc.slice(0, -1) + '=';
break;
}
return enc;
};
this.URLSafeBase64Encode = function(v) {
v = this.base64_encode(v);
return v.replace(/\//g, '_').replace(/\+/g, '-');
};
this.createAjax = function(argument) {
var xmlhttp = {};
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
return xmlhttp;
};
this.parseJSON = function(data) {
// Attempt to parse using the native JSON parser first
if (window.JSON && window.JSON.parse) {
return window.JSON.parse(data);
}
if (data === null) {
return data;
}
if (typeof data === "string") {
// Make sure leading/trailing whitespace is removed (IE can't handle it)
data = this.trim(data);
if (data) {
// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if (/^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, "@").replace(/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) {
return (function() {
return data;
})();
}
}
}
};
this.trim = function(text) {
return text === null ? "" : this.trim.call(text);
};
//Todo ie7 handler / this.parseJSON bug;
var that = this;
this.uploader = function(op) {
if (!op.domain) {
throw 'uptoken_url or domain is required!';
}
if (!op.browse_button) {
throw 'browse_button is required!';
}
var option = {};
var _Error_Handler = op.init && op.init.Error;
var _FileUploaded_Handler = op.init && op.init.FileUploaded;
op.init.Error = function() {};
op.init.FileUploaded = function() {};
that.uptoken_url = op.uptoken_url;
that.token = '';
that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : '';
this.domain = op.domain;
var ctx = '';
var reset_chunk_size = function() {
var ie = that.detectIEVersion();
var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size;
var isSpecialSafari = (mOxie.Env.browser === "Safari" && mOxie.Env.version <= 5 && mOxie.Env.os === "Windows" && mOxie.Env.osVersion === "7") || (mOxie.Env.browser === "Safari" && mOxie.Env.os === "iOS" && mOxie.Env.osVersion === "7");
if (ie && ie <= 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) {
// link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not
// when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) .
op.chunk_size = 0;
} else if (isSpecialSafari) {
// win7 safari / iOS7 safari have bug when in chunk upload mode
// reset chunk_size to 0
// disable chunk in special version safari
op.chunk_size = 0;
} else {
BLOCK_BITS = 20;
MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M
chunk_size = plupload.parseSize(op.chunk_size);
if (chunk_size > MAX_CHUNK_SIZE) {
op.chunk_size = MAX_CHUNK_SIZE;
}
// qiniu service max_chunk_size is 4m
// reset chunk_size to max_chunk_size(4m) when chunk_size > 4m
}
};
reset_chunk_size();
var getUpToken = function() {
if (!op.uptoken) {
var ajax = that.createAjax();
ajax.open('GET', that.uptoken_url, true);
ajax.setRequestHeader("If-Modified-Since", "0");
ajax.onreadystatechange = function() {
if (ajax.readyState === 4 && ajax.status === 200) {
var res = that.parseJSON(ajax.responseText);
that.token = res.uptoken;
}
};
ajax.send();
} else {
that.token = op.uptoken;
}
};
var getFileKey = function(up, file, func) {
var key = '',
unique_names = false;
if (!op.save_key) {
unique_names = up.getOption && up.getOption('unique_names');
unique_names = unique_names || (up.settings && up.settings.unique_names);
if (unique_names) {
var ext = that.getFileExtension(file.name);
key = ext ? file.id + '.' + ext : file.id;
} else if (typeof func === 'function') {
key = func(up, file);
} else {
key = file.name;
}
}
return key;
};
plupload.extend(option, op, {
url: 'http://up.qiniu.com',
multipart_params: {
token: ''
}
});
var uploader = new plupload.Uploader(option);
uploader.bind('Init', function(up, params) {
getUpToken();
});
uploader.init();
uploader.bind('FilesAdded', function(up, files) {
var auto_start = up.getOption && up.getOption('auto_start');
auto_start = auto_start || (up.settings && up.settings.auto_start);
if (auto_start) {
plupload.each(files, function(i, file) {
up.start();
});
}
up.refresh(); // Reposition Flash/Silverlight
});
uploader.bind('BeforeUpload', function(up, file) {
ctx = '';
var directUpload = function(up, file, func) {
var multipart_params_obj;
if (op.save_key) {
multipart_params_obj = {
'token': that.token
};
} else {
multipart_params_obj = {
'key': getFileKey(up, file, func),
'token': that.token
};
}
var x_vars = op.x_vars;
if (x_vars !== undefined && typeof x_vars === 'object') {
for (var x_key in x_vars) {
if (x_vars.hasOwnProperty(x_key)) {
if (typeof x_vars[x_key] === 'function') {
multipart_params_obj['x:' + x_key] = x_vars[x_key](up, file);
} else if (typeof x_vars[x_key] !== 'object') {
multipart_params_obj['x:' + x_key] = x_vars[x_key];
}
}
}
}
up.setOption({
'url': 'http://up.qiniu.com/',
'multipart': true,
'chunk_size': undefined,
'multipart_params': multipart_params_obj
});
};
var chunk_size = up.getOption && up.getOption('chunk_size');
chunk_size = chunk_size || (up.settings && up.settings.chunk_size);
if (uploader.runtime === 'html5' && chunk_size) {
if (file.size < chunk_size) {
directUpload(up, file, that.key_handler);
} else {
var localFileInfo = localStorage.getItem(file.name);
var blockSize = chunk_size;
if (localFileInfo) {
localFileInfo = JSON.parse(localFileInfo);
var now = (new Date()).getTime();
var before = localFileInfo.time || 0;
var aDay = 24 * 60 * 60 * 1000; // milliseconds
if (now - before < aDay) {
if (localFileInfo.percent !== 100) {
if (file.size === localFileInfo.total) {
file.percent = localFileInfo.percent;
file.loaded = localFileInfo.offset;
ctx = localFileInfo.ctx;
if (localFileInfo.offset + blockSize > file.size) {
blockSize = file.size - localFileInfo.offset;
}
} else {
localStorage.removeItem(file.name);
}
} else {
// 进度100%时,删除对应的localStorage,避免 499 bug
localStorage.removeItem(file.name);
}
} else {
localStorage.removeItem(file.name);
}
}
up.setOption({
'url': 'http://up.qiniu.com/mkblk/' + blockSize,
'multipart': false,
'chunk_size': chunk_size,
'required_features': "chunks",
'headers': {
'Authorization': 'UpToken ' + that.token
},
'multipart_params': {}
});
}
} else {
directUpload(up, file, that.key_handler);
}
});
uploader.bind('ChunkUploaded', function(up, file, info) {
var res = that.parseJSON(info.response);
ctx = ctx ? ctx + ',' + res.ctx : res.ctx;
var leftSize = info.total - info.offset;
var chunk_size = up.getOption && up.getOption('chunk_size');
chunk_size = chunk_size || (up.settings && up.settings.chunk_size);
if (leftSize < chunk_size) {
up.setOption({
'url': 'http://up.qiniu.com/mkblk/' + leftSize
});
}
localStorage.setItem(file.name, JSON.stringify({
ctx: ctx,
percent: file.percent,
total: info.total,
offset: info.offset,
time: (new Date()).getTime()
}));
});
uploader.bind('Error', (function(_Error_Handler) {
return function(up, err) {
var errTip = '';
var file = err.file;
if (file) {
switch (err.code) {
case plupload.FAILED:
errTip = '上传失败。请稍后再试。';
break;
case plupload.FILE_SIZE_ERROR:
var max_file_size = up.getOption && up.getOption('max_file_size');
max_file_size = max_file_size || (up.settings && up.settings.max_file_size);
errTip = '浏览器最大可上传' + max_file_size + '。更大文件请使用命令行工具。';
break;
case plupload.FILE_EXTENSION_ERROR:
errTip = '文件验证失败。请稍后重试。';
break;
case plupload.HTTP_ERROR:
var errorObj = that.parseJSON(err.response);
var errorText = errorObj.error;
switch (err.status) {
case 400:
errTip = "请求报文格式错误。";
break;
case 401:
errTip = "客户端认证授权失败。请重试或提交反馈。";
break;
case 405:
errTip = "客户端请求错误。请重试或提交反馈。";
break;
case 579:
errTip = "资源上传成功,但回调失败。";
break;
case 599:
errTip = "网络连接异常。请重试或提交反馈。";
break;
case 614:
errTip = "文件已存在。";
try {
errorObj = that.parseJSON(errorObj.error);
errorText = errorObj.error || 'file exists';
} catch (e) {
errorText = errorObj.error || 'file exists';
}
break;
case 631:
errTip = "指定空间不存在。";
break;
case 701:
errTip = "上传数据块校验出错。请重试或提交反馈。";
break;
default:
errTip = "未知错误。";
break;
}
errTip = errTip + '(' + err.status + ':' + errorText + ')';
break;
case plupload.SECURITY_ERROR:
errTip = '安全配置错误。请联系网站管理员。';
break;
case plupload.GENERIC_ERROR:
errTip = '上传失败。请稍后再试。';
break;
case plupload.IO_ERROR:
errTip = '上传失败。请稍后再试。';
break;
case plupload.INIT_ERROR:
errTip = '网站配置错误。请联系网站管理员。';
uploader.destroy();
break;
default:
errTip = err.message + err.details;
break;
}
if (_Error_Handler) {
_Error_Handler(up, err, errTip);
}
}
up.refresh(); // Reposition Flash/Silverlight
};
})(_Error_Handler));
uploader.bind('FileUploaded', (function(_FileUploaded_Handler) {
return function(up, file, info) {
var last_step = function(up, file, info) {
if (op.downtoken_url) {
var ajax_downtoken = that.createAjax();
ajax_downtoken.open('POST', op.downtoken_url, true);
ajax_downtoken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
ajax_downtoken.onreadystatechange = function() {
if (ajax_downtoken.readyState === 4) {
if (ajax_downtoken.status === 200) {
var res_downtoken;
try {
res_downtoken = that.parseJSON(ajax_downtoken.responseText);
} catch (e) {
throw ('invalid json format');
}
var info_extended = {};
plupload.extend(info_extended, that.parseJSON(info), res_downtoken);
if (_FileUploaded_Handler) {
_FileUploaded_Handler(up, file, JSON.stringify(info_extended));
}
} else {
uploader.trigger('Error', {
status: ajax_downtoken.status,
response: ajax_downtoken.responseText,
file: file,
code: plupload.HTTP_ERROR
});
}
}
};
ajax_downtoken.send('key=' + that.parseJSON(info).key + '&domain=' + op.domain);
} else if (_FileUploaded_Handler) {
_FileUploaded_Handler(up, file, info);
}
};
var res = that.parseJSON(info.response);
ctx = ctx ? ctx : res.ctx;
if (ctx) {
var key = '';
if (!op.save_key) {
key = getFileKey(up, file, that.key_handler);
key = key ? '/key/' + that.URLSafeBase64Encode(key) : '';
}
var x_vars = op.x_vars,
x_val = '',
x_vars_url = '';
if (x_vars !== undefined && typeof x_vars === 'object') {
for (var x_key in x_vars) {
if (x_vars.hasOwnProperty(x_key)) {
if (typeof x_vars[x_key] === 'function') {
x_val = that.URLSafeBase64Encode(x_vars[x_key](up, file));
} else if (typeof x_vars[x_key] !== 'object') {
x_val = that.URLSafeBase64Encode(x_vars[x_key]);
}
x_vars_url += '/x:' + x_key + '/' + x_val;
}
}
}
var url = 'http://up.qiniu.com/mkfile/' + file.size + key + x_vars_url;
var ajax = that.createAjax();
ajax.open('POST', url, true);
ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
ajax.setRequestHeader('Authorization', 'UpToken ' + that.token);
ajax.onreadystatechange = function() {
if (ajax.readyState === 4) {
localStorage.removeItem(file.name);
if (ajax.status === 200) {
var info = ajax.responseText;
last_step(up, file, info);
} else {
uploader.trigger('Error', {
status: ajax.status,
response: ajax.responseText,
file: file,
code: -200
});
}
}
};
ajax.send(ctx);
} else {
last_step(up, file, info.response);
}
};
})(_FileUploaded_Handler));
return uploader;
};
this.getUrl = function(key) {
if (!key) {
return false;
}
key = encodeURI(key);
var domain = this.domain;
if (domain.slice(domain.length - 1) !== '/') {
domain = domain + '/';
}
return domain + key;
};
this.imageView2 = function(op, key) {
var mode = op.mode || '',
w = op.w || '',
h = op.h || '',
q = op.quality || '',
format = op.format || '';
if (!mode) {
return false;
}
if (!w && !h) {
return false;
}
var imageUrl = 'imageView2/' + mode;
imageUrl += w ? '/w/' + w : '';
imageUrl += h ? '/h/' + h : '';
imageUrl += q ? '/q/' + q : '';
imageUrl += format ? '/format/' + format : '';
if (key) {
imageUrl = this.getUrl(key) + '?' + imageUrl;
}
return imageUrl;
};
this.imageMogr2 = function(op, key) {
var auto_orient = op['auto-orient'] || '',
thumbnail = op.thumbnail || '',
strip = op.strip || '',
gravity = op.gravity || '',
crop = op.crop || '',
quality = op.quality || '',
rotate = op.rotate || '',
format = op.format || '',
blur = op.blur || '';
//Todo check option
var imageUrl = 'imageMogr2';
imageUrl += auto_orient ? '/auto-orient' : '';
imageUrl += thumbnail ? '/thumbnail/' + thumbnail : '';
imageUrl += strip ? '/strip' : '';
imageUrl += gravity ? '/gravity/' + gravity : '';
imageUrl += quality ? '/quality/' + quality : '';
imageUrl += crop ? '/crop/' + crop : '';
imageUrl += rotate ? '/rotate/' + rotate : '';
imageUrl += format ? '/format/' + format : '';
imageUrl += blur ? '/blur/' + blur : '';
if (key) {
imageUrl = this.getUrl(key) + '?' + imageUrl;
}
return imageUrl;
};
this.watermark = function(op, key) {
var mode = op.mode;
if (!mode) {
return false;
}
var imageUrl = 'watermark/' + mode;
if (mode === 1) {
var image = op.image || '';
if (!image) {
return false;
}
imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : '';
} else if (mode === 2) {
var text = op.text ? op.text : '',
font = op.font ? op.font : '',
fontsize = op.fontsize ? op.fontsize : '',
fill = op.fill ? op.fill : '';
if (!text) {
return false;
}
imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : '';
imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : '';
imageUrl += fontsize ? '/fontsize/' + fontsize : '';
imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : '';
} else {
// Todo mode3
return false;
}
var dissolve = op.dissolve || '',
gravity = op.gravity || '',
dx = op.dx || '',
dy = op.dy || '';
imageUrl += dissolve ? '/dissolve/' + dissolve : '';
imageUrl += gravity ? '/gravity/' + gravity : '';
imageUrl += dx ? '/dx/' + dx : '';
imageUrl += dy ? '/dy/' + dy : '';
if (key) {
imageUrl = this.getUrl(key) + '?' + imageUrl;
}
return imageUrl;
};
this.imageInfo = function(key) {
if (!key) {
return false;
}
var url = this.getUrl(key) + '?imageInfo';
var xhr = this.createAjax();
var info;
var that = this;
xhr.open('GET', url, false);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
info = that.parseJSON(xhr.responseText);
}
};
xhr.send();
return info;
};
this.exif = function(key) {
if (!key) {
return false;
}
var url = this.getUrl(key) + '?exif';
var xhr = this.createAjax();
var info;
var that = this;
xhr.open('GET', url, false);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
info = that.parseJSON(xhr.responseText);
}
};
xhr.send();
return info;
};
this.get = function(type, key) {
if (!key || !type) {
return false;
}
if (type === 'exif') {
return this.exif(key);
} else if (type === 'imageInfo') {
return this.imageInfo(key);
}
return false;
};
this.pipeline = function(arr, key) {
var isArray = Object.prototype.toString.call(arr) === '[object Array]';
var option, errOp, imageUrl = '';
if (isArray) {
for (var i = 0, len = arr.length; i < len; i++) {
option = arr[i];
if (!option.fop) {
return false;
}
switch (option.fop) {
case 'watermark':
imageUrl += this.watermark(option) + '|';
break;
case 'imageView2':
imageUrl += this.imageView2(option) + '|';
break;
case 'imageMogr2':
imageUrl += this.imageMogr2(option) + '|';
break;
default:
errOp = true;
break;
}
if (errOp) {
return false;
}
}
if (key) {
imageUrl = this.getUrl(key) + '?' + imageUrl;
var length = imageUrl.length;
if (imageUrl.slice(length - 1) === '|') {
imageUrl = imageUrl.slice(0, length - 1);
}
}
return imageUrl;
}
return false;
};
}
exports.uploader = function(options) {
var qiniu = new QiniuJsSDK();
return qiniu.uploader(options);
}