main.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. (function(){
  2. //选择符API note: suite for IE 9+
  3. var $ = document.querySelectorAll.bind(document);
  4. //事件监听
  5. Element.prototype.on = Element.prototype.addEventListener;
  6. //在NodeList对象上通过forEach部署监听函数
  7. NodeList.prototype.on = function (event, fn) {
  8. // this 为 NodeList
  9. [].forEach.call(this, function (el) {
  10. el.on(event, fn);
  11. });
  12. return this;
  13. };
  14. /**
  15. * 上传文件示例
  16. * @type {HTMLElement}
  17. */
  18. Ks3.config.AK = '+vB0RuO22DeeanAAb0ig'; //TODO: 请替换为您的AK
  19. Ks3.config.SK = '3kbTp8pg+LymO84/ZyxG/od/3FeVDjWt+8/m5iBc'; //TODO: 测试时请填写您的secret key 注意:前端计算signature不安全
  20. Ks3.config.region = 'BEIJING'; //TODO: 需要设置bucket所在region, 如杭州region: HANGZHOU,北京region:BEIJING,香港region:HONGKONG,上海region: SHANGHAI ,美国region:AMERICA ;如果region设置和实际不符,则会返回301状态码; region的endpoint参见:http://ks3.ksyun.com/doc/api/index.html
  21. Ks3.config.bucket = 'teaseyoulearn'; // TODO : 设置默认bucket name
  22. var bucketName = Ks3.config.bucket; //TODO: 请替换为您需要上传文件的bucket名称
  23. var filelistNode = document.getElementById('filelist');
  24. /*
  25. * 如果bucket不是公开读写的,需要先鉴权,即提供policy和signature表单
  26. * policy的conditions中需要指明请求体的form中用户添加的字段
  27. */
  28. var policy = {
  29. "expiration": new Date(getExpires(3600)*1000).toISOString(), //一小时后
  30. "conditions": [
  31. ["eq","$bucket", bucketName],
  32. ["starts-with", "$key", ""],
  33. ["starts-with","$acl", "public-read"],
  34. ["starts-with", "$name", ""], //表单中传了name字段,也需要加到policy中
  35. ["starts-with", "$x-kss-meta-custom-param1",""],
  36. ["starts-with", "$x-kss-newfilename-in-body",""],//必须只包含小写字符
  37. ["starts-with", "$Cache-Control",""],
  38. ["starts-with", "$Expires", ""],
  39. ["starts-with", "$Content-Disposition", ""],
  40. ["starts-with", "$Content-Type",""],
  41. ["starts-with", "$Content-Encoding",""]
  42. ]
  43. };
  44. //policy stringify再经过BASE64加密后的字符串(utf8编码格式)
  45. var stringToSign = Ks3.Base64.encode(JSON.stringify(policy));
  46. //建议从后端sdk获取signature签名 算法为:Signature = Base64(HMAC-SHA1(YourSecretKey, stringToSign ) );
  47. var signatureFromPolicy = Ks3.b64_hmac_sha1(Ks3.config.SK, stringToSign);
  48. console.log('signatureFromPolicy:' + signatureFromPolicy);
  49. var ks3UploadUrl;
  50. //支持https 上传
  51. if (window.location.protocol === 'https:') {
  52. Ks3.config.protocol = 'https';
  53. } else {
  54. Ks3.config.protocol = 'http';
  55. }
  56. ks3UploadUrl = Ks3.config.protocol + '://' + Ks3.ENDPOINT[Ks3.config.region] + '/';
  57. var ks3Options = {
  58. KSSAccessKeyId: Ks3.config.AK,
  59. policy: stringToSign,
  60. signature: signatureFromPolicy,
  61. bucket_name: bucketName,
  62. key: '${filename}',
  63. acl: "public-read",
  64. uploadDomain: ks3UploadUrl + bucketName,
  65. autoStart: false,
  66. 'x-kss-meta-custom-param1': 'Hello',
  67. 'x-kss-newfilename-in-body': true,
  68. 'Cache-Control': 'max-age=600', //设置缓存多少秒后过期
  69. 'Expires': new Date(getExpires(600) * 1000), //设置缓存过期时间
  70. 'Content-Disposition' :'attachment;filename=', // 触发浏览器下载文件
  71. //'Content-Type' :' application/octet-stream',
  72. onUploadProgressCallBack: function(uploader, obj){
  73. var itemNode = document.getElementById(obj.id);
  74. var resultNode = itemNode.querySelector('span');
  75. resultNode.innerHTML = obj.percent + "%";
  76. },
  77. onFileUploadedCallBack: function(uploader, obj){ //obj是当前上传的文件对象
  78. var itemNode = document.getElementById(obj.id);
  79. var resultNode = itemNode.querySelector('span');
  80. resultNode.innerHTML = "完成";
  81. //显示上传的文件的链接
  82. var linkNode = itemNode.querySelector('a');
  83. linkNode.href = ks3Options.uploadDomain + "/" + obj.name;
  84. linkNode.innerHTML = obj.name;
  85. //增加加水印按钮
  86. var adpBtn = document.createElement("button");
  87. adpBtn.innerHTML = '添加水印';
  88. adpBtn.onclick = function(){
  89. var objKey = encodeURIComponent(obj.name);
  90. var url = ks3UploadUrl + bucketName + '/' + objKey + '?adp' ;
  91. var kssHeaders = {
  92. 'kss-async-process': 'tag=imgWaterMark&type=2&dissolve=65&gravity=NorthEast&text=6YeR5bGx5LqR&font=5b6u6L2v6ZuF6buR&fill=I2JmMTcxNw==&fontsize=500&dy=10&dx=20|tag=saveas&bucket=' + bucketName + '&object=imgWaterMark-' + obj.name,
  93. 'kss-notifyurl': 'http://10.4.2.38:19090/'
  94. };
  95. var signature = Ks3.generateToken(Ks3.config.SK, bucketName, objKey + '?adp', 'PUT','', kssHeaders, '');
  96. var xhr = new XMLHttpRequest();
  97. xhr.onreadystatechange = function() {
  98. if (xhr.readyState == 4) {
  99. if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
  100. alert("put请求成功");
  101. var waterMarkImgLink = document.createElement('a');
  102. waterMarkImgLink.setAttribute('target','_blank');
  103. waterMarkImgLink.style.marginLeft = "30px";
  104. var processedImgName = "imgWaterMark-" + obj.name;
  105. waterMarkImgLink.innerHTML = processedImgName;
  106. //10分钟后的时间戳
  107. var timeStamp = getExpires(600);
  108. //根据Expires过期时间戳计算外链signature
  109. var expiresSignature = Ks3.generateToken(Ks3.config.SK, bucketName, processedImgName, 'GET', '' ,kssHeaders, timeStamp);
  110. setTimeout(function(){ //异步任务,等1秒再看处理结果
  111. waterMarkImgLink.href = ks3UploadUrl + bucketName + '/imgWaterMark-' + obj.name + '?KSSAccessKeyId=' + encodeURIComponent(Ks3.config.AK) + '&Expires=' + timeStamp + '&Signature=' + encodeURIComponent(expiresSignature);
  112. itemNode.appendChild(waterMarkImgLink);
  113. },1000);
  114. } else{
  115. //alert('Request was unsuccessful: ' + xhr.status);
  116. }
  117. }
  118. };
  119. xhr.open("put", url, true);
  120. xhr.setRequestHeader('Authorization','KSS ' + Ks3.config.AK + ':' + signature );
  121. xhr.setRequestHeader('kss-async-process', kssHeaders['kss-async-process']);
  122. xhr.setRequestHeader('kss-notifyurl',kssHeaders['kss-notifyurl']); //替换成您接收异步处理任务完成通知的url地址
  123. xhr.send(null);
  124. };
  125. itemNode.appendChild(adpBtn);
  126. },
  127. onFilesAddedCallBack: function(uploader, objArray){ // objArray是等待上传的文件对象的数组
  128. for (var i = 0 ; i < objArray.length ; i++){
  129. var itemNode = document.createElement("li");
  130. itemNode.innerHTML = objArray[i].name + "<span style='margin:5px 20px;'></span><a style='margin-right: 20px;' target='_blank'></a>";
  131. itemNode.id = objArray[i].id;
  132. filelistNode.appendChild(itemNode);
  133. }
  134. },
  135. onBeforeUploadCallBack: function(uploader, obj) {
  136. //可以在这里更新object key
  137. //var newObjectKey = 'yourRenamedFileName_' + obj.name;
  138. //uploader.settings.multipart_params['key'] = newObjectKey;
  139. //obj.name = newObjectKey;
  140. },
  141. onErrorCallBack: function(uploader, errObject){
  142. if(errObject.status === 413 || errObject.status === 415){
  143. var responseXML = Ks3.parseStringToXML(errObject.response );
  144. alert(Ks3.xmlToJson(responseXML)['Error']['Message']);
  145. }else{
  146. alert(errObject.code + " : Error happened in uploading " + errObject.file.name + " ( " + errObject.message + " )");
  147. }
  148. }
  149. };
  150. var pluploadOptions = {
  151. browse_button: 'browse', //触发对话框的DOM元素自身或者其ID
  152. drop_element: document.body, //指定了使用拖拽方式来选择上传文件时的拖拽区域,即可以把文件拖拽到这个区域的方式来选择文件。该参数的值可以为一个DOM元素的id,也可是DOM元素本身,还可以是一个包括多个DOM元素的数组。如果不设置该参数则拖拽上传功能不可用。目前只有html5上传方式才支持拖拽上传。
  153. filters: {
  154. mime_types : [ //只允许上传图片和zip文件
  155. { title : "Video files", extensions : "mp4,mov,qt,ts,rmvb,rm,avi,flv,mkv,wmv,mpg,mpeg,m2v,m4v,3gp,3g2,webm,vob,ogv,ogg" }
  156. ],
  157. max_file_size : '2gb', //最大只能上传2GB的文件
  158. prevent_duplicates : true //不允许选取重复文件
  159. }
  160. }
  161. var tempUpload = new ks3FileUploader(ks3Options, pluploadOptions);
  162. document.getElementById('start-upload').onclick = function (){
  163. console.log("start...");
  164. tempUpload.uploader.start();
  165. }
  166. /**
  167. * GET Bucket (List Objects)
  168. * 获取bucket(空间)中object(文件对象)示例
  169. * 参见:http://ks3.ksyun.com/doc/api/bucket/get.html
  170. */
  171. document.getElementById('get-bucket').onclick = function() {
  172. Ks3.listObject({
  173. 'max-keys': '15'
  174. },function(json) {
  175. document.getElementById('responsexml').innerHTML = JSON.stringify(json, null, 4);
  176. });
  177. };
  178. /**
  179. * Delete Object
  180. * 删除指定文件
  181. */
  182. (function listObjects() {
  183. Ks3.listObject(
  184. {
  185. Bucket: bucketName,
  186. 'max-keys': '10',
  187. delimiter:'|'
  188. },function(json) {
  189. /**
  190. * 以表格展示bucket中的object
  191. */
  192. var tableEle = document.getElementById('exampleForDeleteFile');
  193. var objectArray = json['ListBucketResult']['Contents'];
  194. for(var i= 0, len = objectArray.length; i< len; i++) {
  195. var item = document.createElement("tr");
  196. var objKey = objectArray[i]['Key'];
  197. item.id = objKey;
  198. item.innerHTML = '<td>' + objKey + '</td><td>' + objectArray[i]['Size']/1024 + ' KB </td>' + '<td>' + new Date(objectArray[i]['LastModified']).toLocaleString() + '</td>' + '<td class="del-opt">删除</td>';
  199. tableEle.appendChild(item);
  200. };
  201. $('.del-opt').on('click', function(e) {
  202. var key = this.parentNode.firstChild.innerHTML;
  203. Ks3.delObject(
  204. {
  205. Key: key
  206. }, function(status) {
  207. if( status === 204) {
  208. alert( key + " 删除成功");
  209. var ele = document.getElementById(key);
  210. ele.parentNode.removeChild(ele);
  211. }
  212. });
  213. });
  214. });
  215. })();
  216. /**
  217. * PUT Object 上传触发处理示例(上传图片增加水印,后端计算签名,转发请求到Ks3 API)
  218. * 参见:http://ks3.ksyun.com/doc/api/async/trigger.html
  219. * 注: 这里使用了FormData序列化表单中选取的文件,XMLHttpRequest 2级定义了FormData类型,
  220. * 支持的浏览器有Firefox 4+,Safari 5+,Chrome 和 Android 3+版的WebKit
  221. */
  222. document.getElementById('utp').onclick = function() {
  223. var imgFile = document.getElementById('imgFile').files[0]; //获取文件对象
  224. var formData = new FormData();
  225. var objKey = imgFile.name;
  226. formData.append("key", objKey);
  227. formData.append("file", imgFile);
  228. //10分钟后的时间戳,以秒为单位
  229. var timeStamp = getExpires( 10 * 60 );
  230. var url = 'http://127.0.0.1:3000/' + bucketName + '?t=' + timeStamp ;
  231. var kssHeaders = {
  232. 'kss-async-process': 'tag=imgWaterMark&type=2&dissolve=65&gravity=NorthEast&text=6YeR5bGx5LqR&font=5b6u6L2v6ZuF6buR&fill=I2JmMTcxNw==&fontsize=500&dy=10&dx=20|tag=saveas&bucket=' + bucketName + '&object=imgWaterMark-' + objKey,
  233. 'kss-notifyurl': 'http://10.4.2.38:19090/',
  234. 'x-kss-storage-class' : 'STANDARD' // STANDARD | STANDARD_IA 即标准存储和低频访问存储(主要用于备份)
  235. };
  236. var xhr = new XMLHttpRequest();
  237. xhr.onreadystatechange = function() {
  238. if (xhr.readyState == 4) {
  239. if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
  240. alert("上传触发处理成功");
  241. var waterMarkImg = document.getElementById('display-adp-result').firstChild;
  242. console.log('Signature:' + xhr.responseText);
  243. console.log('timestamp:' + timeStamp);
  244. waterMarkImg.src = 'http://kss.ksyun.com/' + bucketName + '/imgWaterMark-' + objKey + '?KSSAccessKeyId=' + encodeURIComponent(Ks3.config.AK) + '&Expires=' + timeStamp + '&Signature=' + encodeURIComponent(xhr.responseText);
  245. }else{
  246. //alert('Request was unsuccessful: ' + xhr.status);
  247. }
  248. }
  249. };
  250. function progressFunction(e) {
  251. var progressBar = document.getElementById("progressBar");
  252. if (e.lengthComputable) {
  253. progressBar.max = e.total;
  254. progressBar.value = e.loaded;
  255. }
  256. }
  257. xhr.upload.addEventListener("progress", progressFunction, false);
  258. xhr.open("put", url, true);
  259. //xhr.setRequestHeader('Content-Length',imgFile.size);
  260. xhr.setRequestHeader('Authorization','KSS ' + Ks3.config.AK );
  261. xhr.setRequestHeader('kss-async-process', kssHeaders['kss-async-process']);
  262. xhr.setRequestHeader('kss-notifyurl',kssHeaders['kss-notifyurl']); //替换成您接收异步处理任务完成通知的url地址
  263. xhr.setRequestHeader('x-kss-storage-class', kssHeaders['x-kss-storage-class']);
  264. xhr.send(formData);
  265. };
  266. /**
  267. * PUT Object 上传文件
  268. * 前端计算signature,put请求直传到ks3 API
  269. * 注意:前端计算signature容易泄露SK, 只用于调试,生产环境需要从后端获取签名,作为第一个参数params的Signature属性传入sdk api函数
  270. *
  271. */
  272. document.getElementById('utp2').onclick = function() {
  273. var file = document.getElementById('imgFile2').files[0]; //获取文件对象
  274. var objKey = Ks3.encodeKey(file.name);
  275. var contentType = file.type;
  276. Ks3.putObject({
  277. Key: 'image/'+objKey,
  278. File: file,
  279. ACL: 'public-read',
  280. ProgressListener: progressFunction,
  281. Sinature: ''
  282. },function(err) {
  283. if(err) {
  284. alert(JSON.stringify(err));
  285. }else{
  286. alert('put上传成功');
  287. }
  288. });
  289. function progressFunction(e) {
  290. var progressBar = document.getElementById("progressBar2");
  291. if (e.lengthComputable) {
  292. progressBar.max = e.total;
  293. progressBar.value = e.loaded;
  294. }
  295. }
  296. };
  297. /**
  298. * 下载文件(支持断点续传)
  299. */
  300. document.getElementById('downloadBigFileBtn').onclick = function() {
  301. blockDownload(
  302. {
  303. Bucket: 'sanrui',
  304. Key:'jssdk/book.pdf'
  305. },
  306. function(err, results) {
  307. if(err) {
  308. console.log(err);
  309. }
  310. console.log(JSON.stringify(results));
  311. });
  312. }
  313. document.getElementById('stopDownload').onclick = function() {
  314. Ks3.config.stopFlag = true;
  315. };
  316. /**
  317. * 上传文件(支持断点续传)
  318. */
  319. var fileToBeUpload; //被上传的文件的文件名
  320. document.getElementById('uploadBigFile').onclick = function() {
  321. /**
  322. * 文件名,最后修改时间,bucket和object key都没变化的情况,续传
  323. */
  324. var file = document.getElementById('bigFile').files[0];
  325. fileToBeUpload = file.name;
  326. //设置分块大小
  327. //Ks3.config.chunkSize = 6*1024*1024; //最小(默认)为5 MB,增大分块会增加部分浏览器崩溃的风险
  328. multipartUpload({
  329. Bucket: bucketName,
  330. Key: fileToBeUpload,
  331. //region: 'BEIJING',
  332. ACL: 'public-read',
  333. //ContentType : 'video/mp4',
  334. File: file,
  335. TotalSize: file.size,
  336. Signature: '',
  337. }, function(err, res){
  338. if(err) {
  339. if(err.msg != 'stop') {
  340. console.error(err);
  341. alert(err.msg);
  342. }else{
  343. console.log(err);
  344. }
  345. }else{
  346. console.log('res: ' + JSON.stringify(res));
  347. }
  348. });
  349. }
  350. document.getElementById('suspendMultipartUpload').onclick = function() {
  351. Ks3.config.stopFlag = true;
  352. };
  353. document.getElementById('cancelMultipartUpload').onclick = function() {
  354. //前端暂停
  355. Ks3.config.stopFlag = true;
  356. //通知ks3取消上传
  357. if(Ks3.config.currentUploadId) {
  358. Ks3.abort_multipart_upload({
  359. Bucket: bucketName,
  360. Key:fileToBeUpload,
  361. UploadId: Ks3.config.currentUploadId
  362. },function(err,res) {
  363. if(err) {
  364. console.log(err);
  365. }else{
  366. console.log('res: ' + JSON.stringify(res));
  367. //清理前端缓存与重置界面进度条
  368. var len = localStorage.length;
  369. for(var i=0; i< len; i++) {
  370. var itemKey = localStorage.key(i);
  371. //自动创建一个临时String对象封装itemKey在IE下会导致内存泄露,故显示转换
  372. if(typeof itemKey === 'string' && (new String(itemKey)).endWith(bucketName + '-' + Ks3.encodeKey(fileToBeUpload))) {
  373. localStorage.removeItem(itemKey);
  374. }
  375. }
  376. var progressBar = document.getElementById("multipartUploadProgressBar");
  377. progressBar.value = 0;
  378. }
  379. });
  380. }
  381. };
  382. })();
  383. /**
  384. * 上传文件
  385. * 根据文件大小,进行简单上传和分块上传
  386. * @param params
  387. * {
  388. * Bucket: '' not required, bucket name
  389. * Key: '' Required object key
  390. * region : '' not required bucket所在region
  391. * ContentType: '' not required content type of object key
  392. * ACL: '' not required private | public-read
  393. * File: object required, 需要上传的文件
  394. * TotalSize: not required, 需要限定文件总大小时使用
  395. * Signature: '' not required, 请求签名,从服务端获取
  396. * callbackurl: '' not required, 回调url
  397. * callbackbody: '' not require, 回调自定义参数
  398. * }
  399. * @param cb
  400. */
  401. function multipartUpload (params, cb) {
  402. var config;
  403. var bucketName = params.Bucket || Ks3.config.bucket || '';
  404. var key = params.Key || params.File.name;
  405. key = Ks3.encodeKey(key);
  406. var region = params.region || Ks3.config.region;
  407. if (region ) {
  408. Ks3.config.baseUrl = Ks3.ENDPOINT[region];
  409. }
  410. var file = params.File;
  411. var totalSize = file.size; //文件总大小
  412. var progressKey = getProgressKey(file.name, file.lastModified, bucketName, key);
  413. console.log(progressKey);
  414. // 会根据文件大小,进行简单上传和分块上传
  415. var contentType = params.ContentType || '';
  416. var progressBar = document.getElementById("multipartUploadProgressBar");
  417. // 分块上传
  418. console.log(async);
  419. async.auto({
  420. /**
  421. * 初始化配置文件,如果没有就新建一个
  422. */
  423. init: function(callback) {
  424. //重置暂停标识
  425. Ks3.config.stopFlag = false;
  426. if ( !localStorage[progressKey]) {
  427. configInit(file, progressKey, function(err) {
  428. callback(err);
  429. })
  430. } else {
  431. callback(null);
  432. }
  433. },
  434. show: ['init', function(callback) {
  435. console.log(' 开始上传文件: ' + progressKey)
  436. config = JSON.parse(localStorage.getItem(progressKey));
  437. progressBar.max = config['count'];
  438. progressBar.value = config['index'];
  439. callback(null);
  440. }],
  441. /**
  442. * 获取uploadId,如果有就直接读取,没有就从服务器获取一个
  443. */
  444. getUploadId: ['init', function(callback) {
  445. config = JSON.parse(localStorage.getItem(progressKey));
  446. var uploadId = config['uploadId'];
  447. if ( !! uploadId) {
  448. callback(null, uploadId);
  449. } else {
  450. Ks3.multitpart_upload_init(params, function(err, uploadId) {
  451. if(err) {
  452. callback(err);
  453. }else {
  454. config['uploadId'] = uploadId;
  455. localStorage.setItem(progressKey, JSON.stringify(config));
  456. callback(null, uploadId);
  457. }
  458. });
  459. }
  460. }],
  461. /**
  462. * 对文件进行上传
  463. * 上传后要把信息写到本地存储配置文件中
  464. * 如果都上传完了,就把相关本地存储信息删除
  465. * 并通知服务器,合并分块文件
  466. */
  467. upload: ['getUploadId', function(callback, result) {
  468. if(result.getUploadId) {
  469. var uploadId = result.getUploadId;
  470. Ks3.config.currentUploadId = uploadId;
  471. config = JSON.parse(localStorage.getItem(progressKey));
  472. var count = config['count'];
  473. var index = config['index'];
  474. var chunkSize = config['chunkSize'];
  475. var currentRetries = config['retries'];
  476. up();
  477. }else {
  478. callback({'msg':'no uploadId'});
  479. }
  480. // 在报错的时候重试
  481. function retry(err) {
  482. console.log('upload ERROR:', err);
  483. if (currentRetries > Ks3.config.retries) {
  484. throw err;
  485. } else {
  486. currentRetries = currentRetries + 1;
  487. config['retries'] = currentRetries;
  488. localStorage.setItem(progressKey, JSON.stringify(config));
  489. console.log('第 ' + currentRetries + ' 次重试');
  490. up();
  491. }
  492. }
  493. // 真正往服务端传递数据
  494. function up() {
  495. console.log('正在上传 ', 'index: ' + index);
  496. var start = (index - 1) * chunkSize;
  497. // 判断是否已经全部都传完了
  498. if (index <= count) {
  499. getFileContent(file, chunkSize, start, function(body) {
  500. delete params.File;
  501. params.UploadId = uploadId;
  502. params.PartNumber = index;
  503. params.body = body;
  504. params.type = contentType;
  505. console.log('正在上传第 ', index, ' 块,总共: ', + count + ' 块');
  506. try {
  507. Ks3.upload_part(params, function(err, partNumber, etag) {
  508. if (err) {
  509. if(err.status == 413 || err.status == 415) {
  510. callback(err);
  511. }else {
  512. retry(err);
  513. }
  514. } else {
  515. if(!Ks3.config.stopFlag) {
  516. config['index'] = index;
  517. progressBar.value = config['index'];
  518. config['etags'][index] = etag;
  519. localStorage.setItem(progressKey, JSON.stringify(config));
  520. index = index + 1;
  521. up();
  522. }else {
  523. callback({
  524. msg: "stop"
  525. });
  526. }
  527. }
  528. });
  529. } catch(e) {
  530. retry(e);
  531. }
  532. })
  533. } else {
  534. console.log('发送合并请求');
  535. delete params.File;
  536. params.UploadId = uploadId;
  537. params.body = generateCompleteXML(progressKey);
  538. Ks3.upload_complete(params, function(err, res) {
  539. if (err) throw err;
  540. callback(err, res);
  541. })
  542. }
  543. };
  544. }]
  545. },
  546. function(err, results) {
  547. if (err) {
  548. //throw err;
  549. }else{
  550. //删除配置
  551. localStorage.removeItem(progressKey);
  552. }
  553. if (cb) {
  554. cb(err, results);
  555. }
  556. });
  557. }
  558. /**
  559. * 计算用于记录上传任务进度的key
  560. * @param name
  561. * @param lastModified
  562. * @param bucket
  563. * @param key
  564. */
  565. function getProgressKey(name, lastModified, bucket, key) {
  566. var result = name + "-" + lastModified + "-" + bucket + "-" + key;
  567. return result;
  568. }
  569. /**
  570. * 把配置信息写到localStorage里,作为缓存
  571. * @param file 上传文件的句柄
  572. * @param progressKey 文件上传进度缓存在localStorage中的标记key
  573. * @param cb
  574. */
  575. function configInit(file, progressKey, cb) {
  576. var fileSize = file.size;
  577. var count = parseInt(fileSize / Ks3.config.chunkSize) + ((fileSize % Ks3.config.chunkSize == 0 ? 0: 1));
  578. if (count == 0) {
  579. cb({
  580. msg: 'The file is empty.'
  581. })
  582. } else {
  583. config = {
  584. name: file.name,
  585. size: fileSize,
  586. chunkSize: Ks3.config.chunkSize,
  587. count:count,
  588. index: 1,
  589. etags:{},
  590. retries: 0
  591. }
  592. localStorage.setItem(progressKey, JSON.stringify(config));
  593. if(cb) {
  594. cb(null);
  595. }
  596. }
  597. }
  598. /**
  599. * 获取指定的文件部分内容
  600. */
  601. function getFileContent(file, chunkSize, start, cb) {
  602. var start = start;
  603. var bufferSize = file.size;
  604. var index = start / chunkSize;
  605. console.log('正在读取下一个块的文件内容 index:' + index);
  606. if (start + chunkSize > bufferSize) {
  607. chunkSize = bufferSize - start;
  608. }
  609. console.log('分块大小:', chunkSize);
  610. if(file.slice) {
  611. var blob = file.slice(start, start + Ks3.config.chunkSize);
  612. }else if(file.webkitSlice) {
  613. var blob = file.webkitSlice(start, start + Ks3.config.chunkSize);
  614. }else if(file.mozSlice) {
  615. var blob = file.mozSlice(start, start + Ks3.config.chunkSize);
  616. }else{
  617. throw new Error("blob API doesn't work!");
  618. }
  619. var reader = new FileReader();
  620. reader.onload = function(e) {
  621. cb(e.target.result);
  622. };
  623. reader.readAsArrayBuffer(blob);
  624. }
  625. /**
  626. * 生成合并分块上传使用的xml
  627. */
  628. function generateCompleteXML(progressKey) {
  629. var content = JSON.parse(localStorage.getItem(progressKey));
  630. var index = content.index;
  631. var str = '';
  632. if (index > 0) {
  633. str = '<CompleteMultipartUpload>';
  634. for (var i = 1; i <= index; i++) {
  635. str += '<Part><PartNumber>' + i + '</PartNumber><ETag>' + content.etags[i] + '</ETag></Part>'
  636. }
  637. str += '</CompleteMultipartUpload>';
  638. }
  639. return str;
  640. }
  641. /**
  642. * 断点续传下载
  643. * 文件分片下载进度存储于localStorage(以filePath做关键字索引文件)
  644. * @param {object} params
  645. * {
  646. * Bucket: '' not required, bucket name
  647. * Key : '' Required , object key
  648. * filePath: '' Required, file path to save
  649. * chunk: '' not required, chunk size by bytes, default is 100 * 1024 B
  650. * }
  651. */
  652. function blockDownload (params, cb) {
  653. var bucketName = params.Bucket || Ks3.config.bucket;
  654. // 这个地方不能进行encode
  655. var key = params.Key;
  656. var filePath = params.filePath || bucketName + '-' + key.replace('/','-') ; //因为浏览器文件系统不能在不存在的目录下直接创建文件,故将目录转为文件名的前缀
  657. if (!key) {
  658. alert('require the object Key');
  659. }
  660. var TEMP_SPACE = 1024 * 1024 * 1024 ; // 1GB
  661. window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
  662. function onError(e) {
  663. console.log('Error', e);
  664. }
  665. // 用户要下载生成的文件
  666. var downFileName = filePath + '.download';
  667. var config = null; //配置信息集合
  668. // 分块大小
  669. var chunk = params.chunk || 1 * 1024 * 100;
  670. var count = 0;
  671. var index = 0;
  672. async.auto({
  673. /**
  674. * 初始化或者读取configFile
  675. * 1. 获取文件大小
  676. * 2. 并且根据分块大小计算出总共请求次数
  677. */
  678. init: function(callback) {
  679. /**
  680. * 重置记录进度的配置文件,删除下载了部分的重名文件
  681. * @param config
  682. */
  683. function resetConfig(conf) {
  684. localStorage.setItem(filePath, JSON.stringify(conf));
  685. index = 0;
  686. //如果有重名文件downFileName,删除
  687. window.requestFileSystem(window.TEMPORARY, TEMP_SPACE, function(fs) {
  688. fs.root.getFile(downFileName, {create: false}, function(fileEntry) {
  689. fileEntry.remove(function() {
  690. console.log(downFileName + ' File removed.');
  691. }, onError);
  692. }, onError);
  693. }, onError);
  694. };
  695. //重置暂停标示
  696. Ks3.config.stopFlag = false;
  697. /**
  698. * 从云端获取文件元数据
  699. */
  700. console.log('远程获取元数据');
  701. Ks3.headObject(params, function(err, res) {
  702. if (err) {
  703. callback(err);
  704. } else {
  705. var length = res.getResponseHeader('content-length');
  706. count = parseInt(length / chunk) + (length % chunk == 0 ? 0 : 1);
  707. if (count == 0) {
  708. callback({
  709. msg: '文件大小为0'
  710. })
  711. } else if (localStorage && localStorage[filePath]) { // 之前已经有配置信息了
  712. config = JSON.parse(localStorage.getItem(filePath));
  713. if (config['lastModifyTime'] == res.getResponseHeader('Last-Modified')) {
  714. console.log('本地读取数据');
  715. count = config['count'];
  716. index = config['index'];
  717. } else {
  718. config = {
  719. "BUCKET": bucketName,
  720. "KEY": key,
  721. "path": filePath,
  722. "chunk": chunk,
  723. "count": count,
  724. "index": 0,
  725. "lastModifyTime": res.getResponseHeader('Last-Modified')
  726. };
  727. resetConfig(config);
  728. }
  729. } else { //无配置信息
  730. config = {
  731. "BUCKET": bucketName,
  732. "KEY": key,
  733. "path": filePath,
  734. "chunk": chunk,
  735. "count": count,
  736. "index": 0,
  737. "lastModifyTime": res.getResponseHeader('Last-Modified')
  738. };
  739. resetConfig(config);
  740. }
  741. callback(null);
  742. }
  743. });
  744. },
  745. /**
  746. * 下载分块数据,并追加到文件末尾
  747. */
  748. down: ['init', function(callback, result) {
  749. // 下载逻辑
  750. var progressBar = document.getElementById("downloadProgressBar");
  751. progressBar.max = count;
  752. function writeToFileSystem(blob, path) {
  753. window.requestFileSystem(window.TEMPORARY, TEMP_SPACE, function(fs) {
  754. fs.root.getFile(path, {create: true}, function(fileEntry) {
  755. fileEntry.createWriter(function(writer) {
  756. var len = writer.length;
  757. writer.seek(len); // Start write position at EOF.
  758. console.log('download file length: ' + len);
  759. writer.write(blob);
  760. }, onError);
  761. }, onError);
  762. }, onError);
  763. }
  764. var downHandler = function() {
  765. if(Ks3.config.stopFlag) {
  766. callback({
  767. msg: 'stop'
  768. });
  769. }else{
  770. //更新下载进度
  771. progressBar.value = index;
  772. var percent = index + '/' + count;
  773. document.getElementById('downloadPercent').innerText = percent;
  774. if (index + 1 > count) { // 下载结束
  775. console.log('下载结束')
  776. callback(null);
  777. } else { // 还没下载完,继续进行下载
  778. console.log('进行下载:', index, '/', count);
  779. Ks3.getObject({
  780. Bucket:bucketName,
  781. Key: key,
  782. range: 'bytes=' + index * chunk + '-' + ((index + 1) * chunk - 1)
  783. },
  784. function(err, data, res) {
  785. if (err) {
  786. callback(err, data);
  787. } else {
  788. writeToFileSystem(data, downFileName);
  789. index = index + 1;
  790. config['index'] = index;
  791. localStorage.setItem(filePath, JSON.stringify(config));
  792. downHandler();
  793. }
  794. });
  795. }
  796. }
  797. };
  798. downHandler();
  799. }]
  800. },
  801. function(err, results) {
  802. /**
  803. * 将浏览器文件系统中的文件拷贝到用户文件系统中
  804. */
  805. function copyFileInBrowserRootToUserOS(fromFilePath, toFilePath) {
  806. window.requestFileSystem(window.TEMPORARY, TEMP_SPACE, function(fs) {
  807. fs.root.getFile(fromFilePath, {create: false}, function(fileEntry) {
  808. fileEntry.file(function(file) {
  809. //downloadFile
  810. var aLink = document.createElement('a');
  811. var evt = document.createEvent("HTMLEvents");
  812. evt.initEvent("click", false, false);//initEvent 不加后两个参数在FF下会报错
  813. aLink.download = toFilePath;
  814. window.URL = window.URL || window.webkitURL;
  815. aLink.href = window.URL.createObjectURL(file);
  816. aLink.dispatchEvent(evt);
  817. }, onError);
  818. }, onError);
  819. }, onError);
  820. }
  821. if (err) {
  822. if (cb) {
  823. cb(err, results);
  824. }
  825. } else {
  826. localStorage.removeItem(filePath);
  827. copyFileInBrowserRootToUserOS(downFileName, filePath);
  828. if (cb) {
  829. cb(err, {msg:'success',path:filePath});
  830. }
  831. }
  832. })
  833. }