qiniu.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. /*global plupload */
  2. /*global ActiveXObject */
  3. function QiniuJsSDK() {
  4. var _option = null;
  5. this.detectIEVersion = function() {
  6. var v = 4,
  7. div = document.createElement('div'),
  8. all = div.getElementsByTagName('i');
  9. while (
  10. div.innerHTML = '<!--[if gt IE ' + v + ']><i></i><![endif]-->',
  11. all[0]
  12. ) {
  13. v++;
  14. }
  15. return v > 4 ? v : false;
  16. };
  17. this.isImage = function(url) {
  18. var res, suffix = "";
  19. var imageSuffixes = ["png", "jpg", "jpeg", "gif", "bmp"];
  20. var suffixMatch = /\.([a-zA-Z0-9]+)(\?|\@|$)/;
  21. if (!url || !suffixMatch.test(url)) {
  22. return false;
  23. }
  24. res = suffixMatch.exec(url);
  25. suffix = res[1].toLowerCase();
  26. for (var i = 0, l = imageSuffixes.length; i < l; i++) {
  27. if (suffix === imageSuffixes[i]) {
  28. return true;
  29. }
  30. }
  31. return false;
  32. };
  33. this.utf8_encode = function(argString) {
  34. // http://kevin.vanzonneveld.net
  35. // + original by: Webtoolkit.info (http://www.webtoolkit.info/)
  36. // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  37. // + improved by: sowberry
  38. // + tweaked by: Jack
  39. // + bugfixed by: Onno Marsman
  40. // + improved by: Yves Sucaet
  41. // + bugfixed by: Onno Marsman
  42. // + bugfixed by: Ulrich
  43. // + bugfixed by: Rafal Kukawski
  44. // + improved by: kirilloid
  45. // + bugfixed by: kirilloid
  46. // * example 1: this.utf8_encode('Kevin van Zonneveld');
  47. // * returns 1: 'Kevin van Zonneveld'
  48. if (argString === null || typeof argString === 'undefined') {
  49. return '';
  50. }
  51. var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n');
  52. var utftext = '',
  53. start, end, stringl = 0;
  54. start = end = 0;
  55. stringl = string.length;
  56. for (var n = 0; n < stringl; n++) {
  57. var c1 = string.charCodeAt(n);
  58. var enc = null;
  59. if (c1 < 128) {
  60. end++;
  61. } else if (c1 > 127 && c1 < 2048) {
  62. enc = String.fromCharCode(
  63. (c1 >> 6) | 192, (c1 & 63) | 128
  64. );
  65. } else if (c1 & 0xF800 ^ 0xD800 > 0) {
  66. enc = String.fromCharCode(
  67. (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
  68. );
  69. } else { // surrogate pairs
  70. if (c1 & 0xFC00 ^ 0xD800 > 0) {
  71. throw new RangeError('Unmatched trail surrogate at ' + n);
  72. }
  73. var c2 = string.charCodeAt(++n);
  74. if (c2 & 0xFC00 ^ 0xDC00 > 0) {
  75. throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
  76. }
  77. c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
  78. enc = String.fromCharCode(
  79. (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
  80. );
  81. }
  82. if (enc !== null) {
  83. if (end > start) {
  84. utftext += string.slice(start, end);
  85. }
  86. utftext += enc;
  87. start = end = n + 1;
  88. }
  89. }
  90. if (end > start) {
  91. utftext += string.slice(start, stringl);
  92. }
  93. return utftext;
  94. };
  95. this.base64_encode = function(data) {
  96. // http://kevin.vanzonneveld.net
  97. // + original by: Tyler Akins (http://rumkin.com)
  98. // + improved by: Bayron Guevara
  99. // + improved by: Thunder.m
  100. // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  101. // + bugfixed by: Pellentesque Malesuada
  102. // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  103. // - depends on: this.utf8_encode
  104. // * example 1: this.base64_encode('Kevin van Zonneveld');
  105. // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
  106. // mozilla has this native
  107. // - but breaks in 2.0.0.12!
  108. //if (typeof this.window['atob'] == 'function') {
  109. // return atob(data);
  110. //}
  111. var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  112. var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
  113. ac = 0,
  114. enc = '',
  115. tmp_arr = [];
  116. if (!data) {
  117. return data;
  118. }
  119. data = this.utf8_encode(data + '');
  120. do { // pack three octets into four hexets
  121. o1 = data.charCodeAt(i++);
  122. o2 = data.charCodeAt(i++);
  123. o3 = data.charCodeAt(i++);
  124. bits = o1 << 16 | o2 << 8 | o3;
  125. h1 = bits >> 18 & 0x3f;
  126. h2 = bits >> 12 & 0x3f;
  127. h3 = bits >> 6 & 0x3f;
  128. h4 = bits & 0x3f;
  129. // use hexets to index into b64, and append result to encoded string
  130. tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
  131. } while (i < data.length);
  132. enc = tmp_arr.join('');
  133. switch (data.length % 3) {
  134. case 1:
  135. enc = enc.slice(0, -2) + '==';
  136. break;
  137. case 2:
  138. enc = enc.slice(0, -1) + '=';
  139. break;
  140. }
  141. return enc;
  142. };
  143. this.URLSafeBase64Encode = function(v) {
  144. v = this.base64_encode(v);
  145. return v.replace(/\//g, '_').replace(/\+/g, '-');
  146. };
  147. this.createAjax = function(argument) {
  148. var xmlhttp = {};
  149. if (window.XMLHttpRequest) {
  150. xmlhttp = new XMLHttpRequest();
  151. } else {
  152. xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  153. }
  154. return xmlhttp;
  155. };
  156. this.parseJSON = function(data) {
  157. // Attempt to parse using the native JSON parser first
  158. if (window.JSON && window.JSON.parse) {
  159. return window.JSON.parse(data);
  160. }
  161. if (data === null) {
  162. return data;
  163. }
  164. if (typeof data === "string") {
  165. // Make sure leading/trailing whitespace is removed (IE can't handle it)
  166. data = this.trim(data);
  167. if (data) {
  168. // Make sure the incoming data is actual JSON
  169. // Logic borrowed from http://json.org/json2.js
  170. 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, ""))) {
  171. return (new Function("return " + data))();
  172. }
  173. }
  174. }
  175. };
  176. this.trim = function(text) {
  177. return text === null ? "" : this.trim.call(text);
  178. };
  179. //Todo ie7 handler / this.parseJSON bug;
  180. var that = this;
  181. this.uploader = function(op) {
  182. if (!op.uptoken_url || !op.domain) {
  183. throw 'uptoken_url or domain is required!';
  184. }
  185. _option = op;
  186. if (!op.browse_button) {
  187. throw 'browse_button is required!';
  188. }
  189. var option = {};
  190. var Error_Handler = op.init && op.init.Error;
  191. var FileUploaded_Handler = op.init && op.init.FileUploaded;
  192. op.init.Error = function() {};
  193. op.init.FileUploaded = function() {};
  194. var uptoken_url = op.uptoken_url;
  195. this.domain = op.domain;
  196. var ie = that.detectIEVersion();
  197. if (ie && ie <= 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) {
  198. /*
  199. link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not
  200. when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) .
  201. */
  202. op.chunk_size = 0;
  203. } else {
  204. var BLOCK_BITS = 20;
  205. var MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M
  206. var chunk_size = plupload.parseSize(op.chunk_size);
  207. if (chunk_size > MAX_CHUNK_SIZE) {
  208. op.chunk_size = MAX_CHUNK_SIZE;
  209. }
  210. //qiniu service max_chunk_size is 4m
  211. //reset chunk_size to max_chunk_size(4m) when chunk_size > 4m
  212. }
  213. var token = '';
  214. var ctx = '';
  215. plupload.extend(option, op, {
  216. url: 'http://up.qiniu.com',
  217. multipart_params: {
  218. token: ''
  219. }
  220. });
  221. var uploader = new plupload.Uploader(option);
  222. var getUpToken = function() {
  223. var ajax = that.createAjax();
  224. ajax.open('GET', uptoken_url, true);
  225. ajax.setRequestHeader("If-Modified-Since", "0");
  226. ajax.onreadystatechange = function() {
  227. if (ajax.readyState === 4 && ajax.status === 200) {
  228. var res = that.parseJSON(ajax.responseText);
  229. token = res.uptoken;
  230. }
  231. };
  232. ajax.send();
  233. };
  234. uploader.bind('Init', function(up, params) {
  235. getUpToken();
  236. });
  237. uploader.init();
  238. uploader.bind('FilesAdded', function(up, files) {
  239. if (up.getOption('auto_start')) {
  240. $.each(files, function(i, file) {
  241. up.start();
  242. });
  243. }
  244. up.refresh(); // Reposition Flash/Silverlight
  245. });
  246. uploader.bind('BeforeUpload', function(up, file) {
  247. ctx = '';
  248. function directUpload() {
  249. up.setOption({
  250. 'url': 'http://up.qiniu.com/',
  251. 'multipart': true,
  252. 'chunk_size': undefined,
  253. 'multipart_params': {
  254. 'token': token,
  255. 'key': file.name
  256. }
  257. });
  258. }
  259. var chunk_size = up.getOption('chunk_size');
  260. if (uploader.runtime === 'html5' && chunk_size) {
  261. if (file.size < chunk_size) {
  262. directUpload();
  263. } else {
  264. var blockSize = chunk_size;
  265. ctx = '';
  266. up.setOption({
  267. 'url': 'http://up.qiniu.com/mkblk/' + blockSize,
  268. 'multipart': false,
  269. 'chunk_size': chunk_size,
  270. 'headers': {
  271. 'Authorization': 'UpToken ' + token
  272. },
  273. 'multipart_params': {}
  274. });
  275. }
  276. } else {
  277. directUpload();
  278. }
  279. });
  280. uploader.bind('ChunkUploaded', function(up, file, info) {
  281. var res = that.parseJSON(info.response);
  282. ctx = ctx ? ctx + ',' + res.ctx : res.ctx;
  283. var leftSize = info.total - info.offset;
  284. var chunk_size = up.getOption('chunk_size');
  285. if (leftSize < chunk_size) {
  286. up.setOption({
  287. 'url': 'http://up.qiniu.com/mkblk/' + leftSize
  288. });
  289. }
  290. });
  291. uploader.bind('Error', (function(Error_Handler) {
  292. return function(up, err) {
  293. var errTip = '';
  294. var file = err.file;
  295. if (file) {
  296. switch (err.code) {
  297. case plupload.FAILED:
  298. errTip = '上传失败。请稍后再试。';
  299. break;
  300. case plupload.FILE_SIZE_ERROR:
  301. errTip = '浏览器最大可上传' + up.getOption('max_file_size') + '。更大文件请使用命令行工具。';
  302. break;
  303. case plupload.FILE_EXTENSION_ERROR:
  304. errTip = '文件验证失败。请稍后重试。';
  305. break;
  306. case plupload.HTTP_ERROR:
  307. var errorObj = that.parseJSON(err.response);
  308. var errorText = errorObj.error;
  309. switch (err.status) {
  310. case 400:
  311. errTip = "请求报文格式错误。";
  312. break;
  313. case 401:
  314. errTip = "客户端认证授权失败。请重试或提交反馈。";
  315. break;
  316. case 405:
  317. errTip = "客户端请求错误。请重试或提交反馈。";
  318. break;
  319. case 579:
  320. errTip = "资源上传成功,但回调失败。";
  321. break;
  322. case 599:
  323. errTip = "网络连接异常。请重试或提交反馈。";
  324. break;
  325. case 614:
  326. errTip = "文件已存在。";
  327. errorObj = that.parseJSON(errorObj.error);
  328. errorText = errorObj.error || 'file exists';
  329. break;
  330. case 631:
  331. errTip = "指定空间不存在。";
  332. break;
  333. case 701:
  334. errTip = "上传数据块校验出错。请重试或提交反馈。";
  335. break;
  336. default:
  337. errTip = "未知错误。";
  338. break;
  339. }
  340. errTip = errTip + '(' + err.status + ':' + errorText + ')';
  341. break;
  342. case plupload.SECURITY_ERROR:
  343. errTip = '安全配置错误。请联系网站管理员。';
  344. break;
  345. case plupload.GENERIC_ERROR:
  346. errTip = '上传失败。请稍后再试。';
  347. break;
  348. case plupload.IO_ERROR:
  349. errTip = '上传失败。请稍后再试。';
  350. break;
  351. case plupload.INIT_ERROR:
  352. errTip = '网站配置错误。请联系网站管理员。';
  353. uploader.destroy();
  354. break;
  355. default:
  356. errTip = err.message + err.details;
  357. break;
  358. }
  359. if (Error_Handler) {
  360. Error_Handler(up, err, errTip);
  361. }
  362. } else {
  363. }
  364. up.refresh(); // Reposition Flash/Silverlight
  365. };
  366. })(Error_Handler));
  367. uploader.bind('FileUploaded', (function(FileUploaded_Handler) {
  368. return function(up, file, info) {
  369. var res = that.parseJSON(info.response);
  370. ctx = ctx ? ctx : res.ctx;
  371. if (ctx) {
  372. var url = 'http://up.qiniu.com/mkfile/' + file.size + '/key/' + that.URLSafeBase64Encode(file.name);
  373. var ajax = that.createAjax();
  374. ajax.open('POST', url, true);
  375. ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
  376. ajax.setRequestHeader('Authorization', 'UpToken ' + token);
  377. ajax.send(ctx);
  378. ajax.onreadystatechange = function() {
  379. if (ajax.readyState === 4) {
  380. if (ajax.status === 200) {
  381. var info = ajax.responseText;
  382. if (FileUploaded_Handler) {
  383. FileUploaded_Handler(up, file, info);
  384. }
  385. } else {
  386. uploader.trigger('Error', {
  387. status: ajax.status,
  388. response: ajax.responseText,
  389. file: file,
  390. code: -200
  391. });
  392. }
  393. }
  394. };
  395. } else {
  396. if (FileUploaded_Handler) {
  397. FileUploaded_Handler(up, file, info.response);
  398. }
  399. }
  400. };
  401. })(FileUploaded_Handler));
  402. return uploader;
  403. };
  404. this.getUrl = function(key) {
  405. if (!key) {
  406. return false;
  407. }
  408. key = encodeURI(key);
  409. var domain = _option.domain;
  410. if (domain.slice(domain.length - 1) !== '/') {
  411. domain = domain + '/';
  412. }
  413. return domain + key;
  414. };
  415. this.imageView2 = function(op, key) {
  416. var mode = op.mode || '',
  417. w = op.w || '',
  418. h = op.h || '',
  419. q = op.quality || '',
  420. format = op.format || '';
  421. if (!mode) {
  422. return false;
  423. }
  424. if (!w && !h) {
  425. return false;
  426. }
  427. var imageUrl = 'imageView2/' + mode;
  428. imageUrl += w ? '/w/' + w : '';
  429. imageUrl += h ? '/h/' + h : '';
  430. imageUrl += q ? '/q/' + q : '';
  431. imageUrl += format ? '/format/' + format : '';
  432. if (key) {
  433. imageUrl = this.getUrl(key) + '?' + imageUrl;
  434. }
  435. return imageUrl;
  436. };
  437. this.imageMogr2 = function(op, key) {
  438. var auto_orient = op['auto-orient'] || '',
  439. thumbnail = op.thumbnail || '',
  440. strip = op.strip || '',
  441. gravity = op.gravity || '',
  442. crop = op.crop || '',
  443. quality = op.quality || '',
  444. rotate = op.rotate || '',
  445. format = op.format || '',
  446. blur = op.blur || '';
  447. //Todo check option
  448. var imageUrl = 'imageMogr2';
  449. imageUrl += auto_orient ? '/auto-orient' : '';
  450. imageUrl += thumbnail ? '/thumbnail/' + thumbnail : '';
  451. imageUrl += strip ? '/strip' : '';
  452. imageUrl += gravity ? '/gravity/' + gravity : '';
  453. imageUrl += quality ? '/quality/' + quality : '';
  454. imageUrl += crop ? '/crop/' + crop : '';
  455. imageUrl += rotate ? '/rotate/' + rotate : '';
  456. imageUrl += format ? '/format/' + format : '';
  457. imageUrl += blur ? '/blur/' + blur : '';
  458. if (key) {
  459. imageUrl = this.getUrl(key) + '?' + imageUrl;
  460. }
  461. return imageUrl;
  462. };
  463. this.watermark = function(op, key) {
  464. var mode = op.mode;
  465. if (!mode) {
  466. return false;
  467. }
  468. var imageUrl = 'watermark/' + mode;
  469. if (mode === 1) {
  470. var image = op.image || '';
  471. if (!image) {
  472. return false;
  473. }
  474. imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : '';
  475. } else if (mode === 2) {
  476. var text = op.text ? op.text : '',
  477. font = op.font ? op.font : '',
  478. fontsize = op.fontsize ? op.fontsize : '',
  479. fill = op.fill ? op.fill : '';
  480. if (!text) {
  481. return false;
  482. }
  483. imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : '';
  484. imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : '';
  485. imageUrl += fontsize ? '/fontsize/' + fontsize : '';
  486. imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : '';
  487. } else {
  488. // Todo mode3
  489. return false;
  490. }
  491. var dissolve = op.dissolve || '',
  492. gravity = op.gravity || '',
  493. dx = op.dx || '',
  494. dy = op.dy || '';
  495. imageUrl += dissolve ? '/dissolve/' + dissolve : '';
  496. imageUrl += gravity ? '/gravity/' + gravity : '';
  497. imageUrl += dx ? '/dx/' + dx : '';
  498. imageUrl += dy ? '/dy/' + dy : '';
  499. if (key) {
  500. imageUrl = this.getUrl(key) + '?' + imageUrl;
  501. }
  502. return imageUrl;
  503. };
  504. this.imageInfo = function(key) {
  505. if (!key) {
  506. return false;
  507. }
  508. var url = this.getUrl(key) + '?imageInfo';
  509. var xhr = this.createAjax();
  510. var info;
  511. var that = this;
  512. xhr.open('GET', url, false);
  513. xhr.onreadystatechange = function() {
  514. if (xhr.readyState === 4 && xhr.status === 200) {
  515. info = that.parseJSON(xhr.responseText);
  516. }
  517. };
  518. xhr.send();
  519. return info;
  520. };
  521. this.exif = function(key) {
  522. if (!key) {
  523. return false;
  524. }
  525. var url = this.getUrl(key) + '?exif';
  526. var xhr = this.createAjax();
  527. var info;
  528. var that = this;
  529. xhr.open('GET', url, false);
  530. xhr.onreadystatechange = function() {
  531. if (xhr.readyState === 4 && xhr.status === 200) {
  532. info = that.parseJSON(xhr.responseText);
  533. }
  534. };
  535. xhr.send();
  536. return info;
  537. };
  538. this.get = function(type, key) {
  539. if (!key || !type) {
  540. return false;
  541. }
  542. if (type === 'exif') {
  543. return this.exif(key);
  544. } else if (type === 'imageInfo') {
  545. return this.imageInfo(key);
  546. }
  547. return false;
  548. };
  549. this.pipeline = function(arr, key) {
  550. var isArray = Object.prototype.toString.call(arr) === '[object Array]';
  551. var option, errOp, imageUrl = '';
  552. if (isArray) {
  553. for (var i = 0, len = arr.length; i < len; i++) {
  554. option = arr[i];
  555. if (!option.fop) {
  556. return false;
  557. }
  558. switch (option.fop) {
  559. case 'watermark':
  560. imageUrl += this.watermark(option) + '|';
  561. break;
  562. case 'imageView2':
  563. imageUrl += this.imageView2(option) + '|';
  564. break;
  565. case 'imageMogr2':
  566. imageUrl += this.imageMogr2(option) + '|';
  567. break;
  568. default:
  569. errOp = true;
  570. break;
  571. }
  572. if (errOp) {
  573. return false;
  574. }
  575. }
  576. if (key) {
  577. imageUrl = this.getUrl(key) + '?' + imageUrl;
  578. var length = imageUrl.length;
  579. if (imageUrl.slice(length - 1) === '|') {
  580. imageUrl = imageUrl.slice(0, length - 1);
  581. }
  582. }
  583. return imageUrl;
  584. }
  585. return false;
  586. };
  587. }
  588. var Qiniu = new QiniuJsSDK();