├── node ├── install.bat ├── start.bat ├── package.json ├── app.js └── package-lock.json ├── .gitignore ├── www ├── demo │ ├── bg-tab.png │ ├── simple.html │ ├── simple-single.html │ ├── targets.html │ ├── folder.html │ ├── simple-manual.html │ ├── scroll-view.html │ ├── fixed-view.html │ ├── drag-drop.html │ ├── image-and-file.html │ ├── simple-filetype.html │ ├── demo.js │ ├── Q.tabs.js │ ├── demo.css │ ├── image.html │ ├── custom.html │ ├── tabs.html │ ├── image-single.html │ ├── default.html │ └── slice.html ├── images │ └── Q │ │ └── progress.gif ├── dist │ ├── js │ │ ├── Q.md5File.js │ │ ├── Q.Uploader.slice.js │ │ ├── Q.Uploader.UI.File.js │ │ ├── Q.Uploader.UI.Image.js │ │ ├── Q.js │ │ ├── spark-md5.js │ │ └── Q.Uploader.js │ ├── Q.Uploader.file.all.js │ └── Q.Uploader.image.all.js ├── js │ ├── Q.md5File.js │ ├── Q.Uploader.slice.js │ ├── Q.Uploader.UI.File.js │ ├── Q.Uploader.UI.Image.js │ ├── Q.js │ └── spark-md5.js └── css │ └── uploader.all.css └── README.md /node/install.bat: -------------------------------------------------------------------------------- 1 | npm install 2 | pause -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node/node_modules 2 | /upload -------------------------------------------------------------------------------- /node/start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | node app.js 3 | pause -------------------------------------------------------------------------------- /www/demo/bg-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devin87/web-uploader-node/HEAD/www/demo/bg-tab.png -------------------------------------------------------------------------------- /www/images/Q/progress.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devin87/web-uploader-node/HEAD/www/images/Q/progress.gif -------------------------------------------------------------------------------- /node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-uploader-node", 3 | "version": "1.1.0", 4 | "description": "web-uploader node �ϴ���ʾ.", 5 | "main": "app.js", 6 | "repository": "", 7 | "author": "Devin ", 8 | "dependencies": { 9 | "express": "4.x", 10 | "formidable": "" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /www/dist/js/Q.md5File.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2020/08/25 18:26:41 3 | !function(e){"use strict";if(e.SparkMD5&&e.File){var f=File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSlice,u=new SparkMD5.ArrayBuffer,d=2097152;Q.md5File=function(t,r,i){var o=t.size,n=Math.ceil(o/d),a=0,l=new FileReader,c=Date.now();function p(){var e=a*d,r=o<=e+d?o:e+d;l.readAsArrayBuffer(f.call(t,e,r))}u.reset(),l.onload=function(e){u.append(e.target.result),a++,i&&i(a/n),a 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 18 |
19 |
20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 37 | 38 | -------------------------------------------------------------------------------- /www/demo/simple-single.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件单选 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 18 |
19 |
20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 39 | 40 | -------------------------------------------------------------------------------- /www/demo/targets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 上传按钮1 17 | 上传按钮2 18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 38 | 39 | -------------------------------------------------------------------------------- /www/dist/js/Q.Uploader.slice.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2020/08/25 18:26:42 3 | !function(e){"use strict";var m=Q.Uploader;if(m.support.html5){var o=e.File?File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSlice:void 0;m.extend({_upload_slice:function(a){var s,c=this,e=a.blob||a.file,p=e.size,n=c.chunkSize,d=a.sliceStart||0,u=c.sliceRetryCount,f=function(e,i,n){n=+n||0;var t=new XMLHttpRequest,o=a.url,r=s==p;a.xhr=t,o+=(-1==o.indexOf("?")?"?":"&")+"action=upload&hash="+(a.hash||a.name)+"&ok="+(r?"1":"0"),t.upload.addEventListener("progress",function(e){c.progress(a,p,d+e.loaded)},!1),t.addEventListener("load",function(e){var n=e.target.responseText;return r?c.complete(a,m.COMPLETE,n):1==n||'"1"'==n?i():void c.complete(a,m.ERROR,n)},!1),t.addEventListener("error",function(){if(++n>u)return c.complete(a,m.ERROR);f(e,i,n)},!1);var l=new FormData;c._process_params(a,function(e,n){l.append(e,n)}),l.append("fileName",a.name),-1!=a.size&&l.append("fileSize",a.size),l.append(c.upName,e,a.name),l.append("sliceCount",a.sliceCount),l.append("sliceIndex",a.sliceIndex),t.open("POST",o),c._process_xhr_headers(t),r?c.fire("send",a,function(e){if(!1===e)return c.complete(a,m.SKIP);t.send(l)}):t.send(l)};a.sliceCount=Math.ceil(p/n);function i(){p<=d||(p<(s=d+n)&&(s=p),a.sliceStart=d,a.sliceEnd=s,a.sliceIndex=Math.ceil(s/n),a.sliceBlob=o.call(e,d,s),c.fire("sliceUpload",a,function(e){if(!1===e)return t();f(a.sliceBlob,t)}))}var t=function(){d=s,i()};i(),c._afterSend(a)}})}}(window); -------------------------------------------------------------------------------- /www/demo/folder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
注:仅部分浏览器(如Webkit内核浏览器或新版火狐)支持文件夹上传。
16 | 19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 40 | 41 | -------------------------------------------------------------------------------- /www/demo/simple-manual.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 手动上传 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 添加文件 17 | 确认上传 18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 44 | 45 | -------------------------------------------------------------------------------- /www/dist/js/Q.Uploader.UI.File.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2021/08/18 17:20:23 3 | !function(){"use strict";var c=Q.def,r=Q.getFirst,n=Q.getLast,u=Q.getNext,v=Q.createEle,p=Q.formatSize,m=Q.event.add,h=Q.Uploader,f=h.Lang;function s(e,s){e.className+=" "+s}function g(e,s){e&&(e.innerHTML=s||"")}h.UI.File={init:function(){var e=this.ops.view;e&&s(e,"ui-file "+(this.html5?"html5":"html4"))},draw:function(e){var s,i,a,t,d=this,l=d.ops,o=l.view;o&&(s=l.button||{},t=c(f.cancel||s.cancel,"取消"),l=c(f.remove||s.remove,"删除"),t='
'+s+'
',i=e.id,(a=v("div","u-item",t)).taskId=i,e.box=a,o.appendChild(a),l=n(a.childNodes[1]),t=r(l),l=n(l),m(t,"click",function(){d.cancel(i)}),m(l,"click",function(){d.remove(i),o.removeChild(a)}),d.update(e))},update:function(e){var s,i,a,t,d,l,o,c,n,v;e&&e.box&&(s=e.total||e.size,i=e.loaded,a=e.state,o=(t=e.box).childNodes[1],d=r(o),l=u(d),c=u(l),o=r(c),c=u(c),g(u(c),h.getStatusText(a)),a==h.ERROR&&(v=e.json||{},t.title=v.msg||v.errMsg||e.response||h.Lang.upload_error),s<0||(v="",this.html5&&null!=i&&0<=i&&(a==h.PROCESSING?"100.0"==(n=Math.min(100*i/s,100).toFixed(1))&&(n="99.9"):a==h.COMPLETE&&(n="100"),n&&(n+="%",g(c,o.style.width=n)),v=''+p(i)+" / ",e=e.avgSpeed||e.speed,g(l,p(e)+"/s")),g(d,v+=''+p(s)+"")))},over:function(e){e&&e.box&&s(e.box,"u-over")}},h.extend(h.UI.File)}(window); -------------------------------------------------------------------------------- /www/js/Q.md5File.js: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /* 4 | * Q.Uploader.md5.js 计算文件md5值 5 | * author:devin87@qq.com 6 | * update:2016/06/17 13:00 7 | */ 8 | (function (window, undefined) { 9 | "use strict"; 10 | 11 | if (!window.SparkMD5 || !window.File) return; 12 | 13 | var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, 14 | spark = new SparkMD5.ArrayBuffer(), 15 | chunkSize = 2 * 1024 * 1024; 16 | 17 | //计算文件md5值 18 | //file:文件对象 eg: input.files[0] 19 | //callback:回调函数 eg: callback(md5,time) 20 | //progress:进度函数 21 | function computeMd5(file, callback, progress) { 22 | var size = file.size, 23 | chunks = Math.ceil(size / chunkSize), 24 | currentChunk = 0, 25 | fr = new FileReader(), 26 | startTime = Date.now(); 27 | 28 | spark.reset(); 29 | 30 | fr.onload = function (e) { 31 | spark.append(e.target.result); 32 | currentChunk++; 33 | progress && progress(currentChunk / chunks); 34 | 35 | if (currentChunk < chunks) { 36 | loadNext(); 37 | } else { 38 | callback && callback(spark.end(), Date.now() - startTime); 39 | } 40 | }; 41 | 42 | fr.onerror = callback; 43 | 44 | function loadNext() { 45 | var start = currentChunk * chunkSize, 46 | end = ((start + chunkSize) >= size) ? size : start + chunkSize; 47 | 48 | fr.readAsArrayBuffer(blobSlice.call(file, start, end)); 49 | } 50 | 51 | loadNext(); 52 | } 53 | 54 | 55 | Q.md5File = computeMd5; 56 | 57 | })(window); -------------------------------------------------------------------------------- /www/demo/scroll-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | html4+滚动区域 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 | 23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 52 | 53 | -------------------------------------------------------------------------------- /www/demo/fixed-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | html4+固定区域 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 | 22 |
23 |
24 |
25 |
26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 51 | 52 | -------------------------------------------------------------------------------- /www/demo/drag-drop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 18 |
将文件拖拽到此区域
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 74 | 75 | -------------------------------------------------------------------------------- /www/demo/image-and-file.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 19 |
20 |
21 |
22 |
23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 75 | 76 | -------------------------------------------------------------------------------- /www/demo/simple-filetype.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 - 指定上传类型 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 | 选择图片 20 | 选择视频 21 | 选择音乐 22 |
23 |
24 |
25 |
26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 71 | 72 | -------------------------------------------------------------------------------- /www/demo/demo.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | (function () { 3 | "use strict"; 4 | 5 | //获取页名称 6 | function get_page_name(pathname) { 7 | pathname = pathname || location.pathname; 8 | pathname = pathname.toLowerCase().replace(/\\/g, "/"); 9 | 10 | var index = pathname.lastIndexOf("/"); 11 | 12 | return index != -1 ? pathname.slice(index + 1) : pathname; 13 | } 14 | 15 | var PAGE_NAME = get_page_name(); 16 | 17 | var list_page = [ 18 | { title: "默认上传", href: "default.html" }, 19 | { title: "简单上传", href: "simple.html" }, 20 | { title: "秒传+分片+断点续传(仅html5)", href: "slice.html" }, 21 | { title: "指定上传类型", href: "simple-filetype.html" }, 22 | { title: "图片预览+缩放+上传", href: "image.html" }, 23 | { title: "单张图片上传", href: "image-single.html" }, 24 | { title: "html4+滚动区域", href: "scroll-view.html" }, 25 | { title: "html4+fixed区域", href: "fixed-view.html" }, 26 | { title: "文件单选", href: "simple-single.html" }, 27 | { title: "手动上传", href: "simple-manual.html" }, 28 | { title: "一个上传管理器多个按钮", href: "targets.html" }, 29 | { title: "多个上传管理器", href: "tabs.html" }, 30 | { title: "拖拽上传(仅html5)", href: "drag-drop.html" }, 31 | { title: "多UI(文件上传+图片上传)", href: "image-and-file.html" }, 32 | { title: "文件夹上传", href: "folder.html" }, 33 | { title: "自定义UI", href: "custom.html" } 34 | ]; 35 | 36 | var map_page = {}; 37 | 38 | function draw_sidebar() { 39 | var html = []; 40 | html.push('
导航
'); 41 | html.push('
    '); 42 | for (var i = 0, len = list_page.length; i < len; i++) { 43 | var item = list_page[i], 44 | href = item.href; 45 | 46 | item.index = i; 47 | map_page[href] = item; 48 | 49 | html.push('' + item.title + ''); 50 | } 51 | html.push('
'); 52 | 53 | document.getElementById("sidebar").innerHTML = html.join(''); 54 | } 55 | 56 | function init() { 57 | draw_sidebar(); 58 | 59 | var boxHeader = document.getElementById("header"), 60 | page = map_page[PAGE_NAME]; 61 | 62 | if (!boxHeader || !page) return; 63 | 64 | if (boxHeader.innerHTML == "Header") boxHeader.innerHTML = page.title; 65 | } 66 | 67 | init(); 68 | 69 | //------------------- export ------------------- 70 | 71 | var UPLOAD_URL = window.localStorage ? localStorage.getItem("UPLOAD_URL") : undefined; 72 | 73 | window.UPLOAD_URL = UPLOAD_URL || "/upload"; 74 | 75 | })(); -------------------------------------------------------------------------------- /www/demo/Q.tabs.js: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /* 4 | * Q.tabs.js 选项卡插件 5 | * author:devin87@qq.com 6 | * update:2014/09/23 11:15 7 | */ 8 | (function () { 9 | "use strict"; 10 | 11 | var isFunc = Q.isFunc, 12 | getFirst = Q.getFirst; 13 | 14 | //设置选项卡切换 15 | function setTabs(ops) { 16 | ops = ops || {}; 17 | 18 | var context = ops.context, 19 | 20 | list_li = $(".tabTitle>li", context), 21 | list_cont = $(".tabCont>.turn-box", context), 22 | 23 | lastIndex; 24 | 25 | if (list_li.size() <= 0) return; 26 | 27 | var map_tab_index = {}; 28 | 29 | $.each(list_li, function (i, li) { 30 | //优先显示 31 | if ($(li).attr("x-def") == "1") ops.index = i; 32 | 33 | var link = getFirst(li); 34 | if (!link) return; 35 | 36 | var hash = link.href.split("#")[1]; 37 | if (hash) map_tab_index[hash] = i; 38 | }); 39 | 40 | list_li.removeClass("on"); 41 | list_cont.hide(); 42 | 43 | //切换操作 44 | var showTab = function (index) { 45 | if (index === lastIndex) return; 46 | 47 | if (lastIndex !== undefined) { 48 | var lastTab = list_li[lastIndex], 49 | lastCont = list_cont[lastIndex]; 50 | 51 | $(lastTab).removeClass("on"); 52 | $(lastCont).hide(); 53 | } 54 | 55 | var tab = list_li[index], 56 | cont = list_cont[index]; 57 | 58 | $(tab).addClass("on"); 59 | $(cont).show(); 60 | 61 | lastIndex = index; 62 | 63 | //回调函数(自定义切换事件) 64 | var callback = window.onTabChange; 65 | 66 | if (isFunc(callback)) { 67 | setTimeout(function () { 68 | callback({ index: index, tab: tab, cont: cont }); 69 | }, 100); 70 | } 71 | }; 72 | 73 | //点击切换事件 74 | list_li.each(function (i) { 75 | $(this).click(function () { 76 | showTab(i); 77 | }); 78 | }); 79 | 80 | //初始化 81 | setTimeout(function () { 82 | var hash = location.hash.slice(1), 83 | index = map_tab_index[hash]; 84 | 85 | if (index == undefined) index = ops.index || 0; 86 | 87 | //默认显示顺序 location hash -> html定义(x-def属性) -> ops.index -> 0 88 | showTab(index); 89 | }, 20); 90 | } 91 | 92 | //------------------------- export ------------------------- 93 | 94 | Q.setTabs = setTabs; 95 | 96 | })(); -------------------------------------------------------------------------------- /www/demo/demo.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | body { width: 100%; height: 100%; color: #333; font-size: 12px; font-family: arial,"微软雅黑","宋体",sans-serif; } 3 | body, dl, dd, h1, h2, h3, h4, h5, h6, p, form { margin: 0; } 4 | ul, ol { margin: 0; padding: 0; } 5 | li { list-style: none; } 6 | img { border: none; } 7 | a { text-decoration: none; } 8 | 9 | .clearfix:after { content: ''; clear: both; display: table; } 10 | .clearfix { zoom: 1; } 11 | 12 | .header { height: 60px; line-height: 60px; font-size: 16px; text-align: center; border-bottom: 1px solid #ccc; } 13 | .main { overflow: hidden; } 14 | 15 | .sidebar { float: left; width: 220px; border-right: 1px solid #ccc; } 16 | .sidebar .title { font-size: 16px; padding: 8px 0 8px 15px; } 17 | .sidebar li { padding: 8px 0 8px 35px; border-top: 1px solid #ddd; } 18 | .sidebar li a { color: #0094ff; font-size: 14px; } 19 | .sidebar li.on, .sidebar li:hover { background-color: #efefef; } 20 | 21 | .content { float: right; width: 100%; margin-left: -240px; } 22 | .contentin { margin-left: 240px; min-width: 760px; _width: 760px; padding-top: 20px; margin-right: 20px; } 23 | .x-button { padding: 10px 25px; border-radius: 3px; text-align: center; text-decoration: none; background-color: #0a82e4; color: #ffffff; font-size: 17px; margin: 0; white-space: nowrap; cursor: pointer; min-width: 60px; _width: 60px; } 24 | 25 | .content .x-button { display: inline-block; vertical-align: top; margin-right: 5px; } 26 | 27 | #upload-target { margin-bottom: 15px; } 28 | #upload-view { min-height: 200px; _height: 200px; background: #fff; border: 1px solid green; } 29 | 30 | #log { margin: 10px 0; white-space: nowrap; clear: both; } 31 | 32 | .scroll-view { overflow: auto; height: 300px; } 33 | .h100 { height: 100px; } 34 | .h1000 { height: 1000px; } 35 | 36 | .tabTitle { overflow: hidden; zoom: 1; } 37 | .tabTitle li { float: left; margin-right: 4px; z-index: 10; position: relative; } 38 | .tabTitle li a { float: left; padding: 0 18px; border: 1px solid #cdcdcd; border-bottom: none; line-height: 44px; font-size: 14px; color: #333; text-decoration: none; font-weight: bold; background: url(bg-tab.png) repeat-x left bottom; } 39 | .tabTitle li a:hover, .tabTitle li.on a { border-top: 2px solid #336699; line-height: 44px; background: #fff; } 40 | 41 | .tabCont { margin-top: -1px; padding: 10px; border: 1px solid #cdcdcd; background: #fff; } 42 | 43 | .turn-box { min-height: 200px; _height: 200px; } 44 | .turn-box .x-button { margin: 10px; } 45 | 46 | #drop-area { width: 400px; height: 200px; line-height: 200px; text-align: center; font-size: 18px; color: #777; border: 1px solid red; margin: 10px 0; } 47 | 48 | @media screen and (min-width: 1200px) { 49 | .hd .con { width: 1280px; } 50 | .hd .side_r { width: 1039px; } 51 | } 52 | -------------------------------------------------------------------------------- /www/css/uploader.all.css: -------------------------------------------------------------------------------- 1 | .fl { float: left; } 2 | .fr { float: right; } 3 | .gray { color: gray; } 4 | 5 | .upload-input { position: absolute; overflow: hidden; z-index: 10000; } 6 | .upload-html4 { position: absolute; left: -10000px; top: -10000px; } 7 | 8 | .u-loaded { color: green; } 9 | .u-total { color: #FC5900; } 10 | 11 | /* ----------------- 文件上传 -------------------*/ 12 | .ui-file .u-item { background-color: #f5f5f5; height: 36px; line-height: 36px; font-size: 13px; margin: 10px; padding: 0 10px; position: relative; } 13 | 14 | .ui-file .u-name { min-width: 100px; color: #369; margin-right: 10px; } 15 | 16 | .ui-file .u-size, .ui-file .u-speed, .ui-file .u-progress-bar, .ui-file .u-detail, .ui-file .u-state, .ui-file .u-op { float: left; text-align: center; } 17 | 18 | .ui-file .u-progress-bar, .ui-file .u-progress { background: url(../images/Q/progress.gif) 0 1px repeat-x; } 19 | .ui-file .u-progress-bar { width: 150px; height: 7px; overflow: hidden; margin: 15px 6px 0 0; } 20 | .ui-file .u-progress { width: 1px; height: 7px; background-position: 0 -10px; } 21 | 22 | .ui-file .u-detail { width: 55px; } 23 | .ui-file .u-state { min-width: 45px; } 24 | 25 | .ui-file .u-size { } 26 | 27 | .ui-file .u-speed { width: 85px; } 28 | .ui-file .u-op { margin-left: 5px; } 29 | .ui-file .u-op a { cursor: pointer; margin-left: 5px; color: #369; text-decoration: none; } 30 | .ui-file .u-op a:hover { text-decoration: underline; } 31 | 32 | .html4 .u-progress-bar, .html4 .u-detail, .html4 .u-speed { display: none; } 33 | .html4 .u-size { margin: 0 10px; } 34 | 35 | .ui-file .u-over .u-state { color: #999; } 36 | .ui-file .u-over .u-op .u-op-cancel { color: #999; text-decoration: none; cursor: default; } 37 | 38 | /* ----------------- 图片上传 -------------------*/ 39 | .ui-image .u-item { float: left; width: 136px; height: 174px; margin-left: 10px; position: relative; } 40 | .ui-image .u-first { margin: 0; } 41 | 42 | .ui-image .u-img, .ui-image .u-img img { width: 136px; height: 150px; overflow: hidden; } 43 | .ui-image .u-name { text-align: center; height: 24px; line-height: 24px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } 44 | .ui-image .u-close-bg, .ui-image .u-close-text { position: absolute; top: 0; right: 0; width: 24px; height: 24px; line-height: 24px; color: #fff; text-align: center; cursor: pointer; display: none; } 45 | .ui-image .u-close-bg { background-color: #7ce4e6; opacity: 0.3; } 46 | 47 | .ui-image .u-progress-bar, .ui-image .u-detail { position: absolute; left: 0; bottom: 24px; width: 100%; height: 20px; } 48 | .ui-image .u-progress-bar { background-color: #7ce4e6; opacity: 0.3; } 49 | .ui-image .u-progress { width: 0; height: 100%; background-color: green; opacity: 0.5; } 50 | .ui-image .u-detail { line-height: 20px; text-align: center; color: #fff; } 51 | -------------------------------------------------------------------------------- /www/demo/image.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 | 22 |
23 |
24 |
25 |
26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 92 | 93 | -------------------------------------------------------------------------------- /www/dist/js/Q.Uploader.UI.Image.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2021/08/18 17:20:24 3 | !function(o,s){"use strict";var f=Q.getFirst,p=Q.getNext,v=Q.createEle,m=Q.setOpacity,n=Q.ie,l=Q.Uploader;function i(e,t){e.className+=" "+t}function c(t,e,i){var a=e.input,e=e.file||(a.files?a.files[0]:s);if(e)!function(e,t){var i=o.URL||o.webkitURL;if(i)return t(i.createObjectURL(e));o.FileReader?((i=new FileReader).onload=function(e){t(e.target.result)},i.readAsDataURL(e)):e.readAsDataURL&&t(e.readAsDataURL())}(e,function(e){e&&(t.innerHTML=''),i&&i(e)});else if(a){var r=a.value;if(r&&!/^\w:\\fakepath/.test(r)||(a.select(),parent.document.body.focus(),document.selection&&(r=document.selection.createRange().text)),r){t.innerHTML='';try{6
'+l+'
X
',t=e.id,(i=v("div","u-item",c)).taskId=t,a=f(i),r=p(a),o=f(r),n=p(r),s=p(n),l=p(s),c=p(l),m(r,.3),m(o,.5),m(l,.3),c.onclick=function(){g.removeChild(i),d.remove(t)},e.box=i,e.boxProgress=o,e.boxDetail=n,e.boxName=s,g.appendChild(i),d.previewImage(a,e,u).update(e))},update:function(e){var t,i,a,r,o,n;e&&e.box&&(t=e.total||e.size,i=e.loaded,a=e.state,r=e.boxProgress,n=e.boxDetail,e=l.getStatusText(a),this.html5&&i!=s&&0<=i&&(a==l.PROCESSING?"100.0"==(o=Math.min(100*i/t,100).toFixed(1))&&(o="99.9"):a==l.COMPLETE&&(o="100"),o&&(e+=" "+(o+="%"),r.style.width=o)),e=e,(n=n)&&(n.innerHTML=e||""))},over:function(e){e&&e.box&&i(e.box,"u-over")}},l.extend(l.UI.Image)}(window); -------------------------------------------------------------------------------- /node/app.js: -------------------------------------------------------------------------------- 1 | //处理文件上传页面 2 | var fs = require('fs'), 3 | path = require('path'), 4 | 5 | express = require('express'), 6 | formidable = require("formidable"), 7 | 8 | Q = require('./lib/Q.js'), 9 | app = express(); 10 | 11 | var NODE_DIR = __dirname, 12 | WWW_DIR = path.join(NODE_DIR, '../www'), 13 | UPLOAD_DIR = path.join(NODE_DIR, '../upload'); 14 | 15 | if (fs.existsSync(WWW_DIR)) app.use(express.static(WWW_DIR)); 16 | 17 | Q.mkdir(UPLOAD_DIR); 18 | 19 | app.get('/', function (req, res) { 20 | res.redirect("/demo/default.html"); 21 | }); 22 | 23 | function process_upload_file(req, res, callback) { 24 | var form = new formidable.IncomingForm(); 25 | 26 | form.uploadDir = UPLOAD_DIR; 27 | form.maxFieldsSize = 4 * 1024 * 1024 * 1024; //2G 28 | 29 | form.parse(req, function (err, fields, files) { 30 | var upfile; 31 | 32 | Object.forEach(files, function (key, file) { 33 | upfile = file; 34 | }); 35 | 36 | callback && callback(form, upfile, fields); 37 | }); 38 | } 39 | 40 | function finish_upload(req, res, fields) { 41 | var result = { 42 | time: Date.now(), 43 | type: req.query["type"], 44 | user: fields["user"], 45 | name: fields["name"] 46 | }; 47 | 48 | res.send(JSON.stringify(result)); 49 | } 50 | 51 | app.all('/upload', function (req, res) { 52 | res.setHeader('Access-Control-Allow-Origin', '*'); 53 | 54 | if (req.method == 'OPTIONS') { 55 | res.send('1'); 56 | return; 57 | } 58 | 59 | var action = req.query["action"], 60 | hash = req.query["hash"]; 61 | 62 | if (!hash) { 63 | process_upload_file(req, res, function (form, file, fields) { 64 | var fileName = fields["fileName"] || file.name, 65 | savePath = path.join(UPLOAD_DIR, fileName), 66 | dir = path.dirname(savePath); 67 | 68 | if (dir && dir != '.') Q.mkdir(dir); 69 | 70 | fs.renameSync(file.path, savePath); 71 | console.log('Upload: ' + fileName + ' => ' + Q.formatSize(file.size)); 72 | 73 | finish_upload(req, res, fields); 74 | }); 75 | } else { 76 | var path_tmp = path.join(UPLOAD_DIR, hash), 77 | path_ok = path_tmp + ".ok"; 78 | 79 | if (action == "query") { 80 | if (fs.existsSync(path_ok)) res.send('ok'); //秒传成功可以返回json对象 eg:{ ret:1, test:"aaa" } 81 | else if (fs.existsSync(path_tmp)) res.send(fs.statSync(path_tmp).size + "");//等同于 { ret:0,start:fs.statSync(path_tmp).size } 82 | else res.send("0"); //等同于 { ret:0,start:0 } 83 | } else { 84 | process_upload_file(req, res, function (form, file, fields) { 85 | if (fields["retry"] != "1") fs.appendFileSync(path_tmp, fs.readFileSync(file.path)); 86 | fs.unlinkSync(file.path); 87 | 88 | var isOk = req.query["ok"] == "1"; 89 | if (!isOk) { 90 | res.send(fields["retry"] == "1" ? "0" : "1"); 91 | } else { 92 | var fileName = fields["fileName"] || file.name; 93 | fs.renameSync(path_tmp, path_ok); 94 | console.log('Upload: ' + fileName + ' => ' + Q.formatSize(fs.statSync(path_ok).size)); 95 | 96 | finish_upload(req, res, fields); 97 | } 98 | }); 99 | } 100 | } 101 | }); 102 | 103 | app.listen(3000, function () { 104 | console.log("server running at 127.0.0.1:3000"); 105 | console.log(); 106 | }); -------------------------------------------------------------------------------- /www/demo/custom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 | 24 |
25 |
26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 91 | 92 | -------------------------------------------------------------------------------- /www/demo/tabs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 19 |
20 |
21 | 24 |
25 |
26 |
27 | 30 |
31 |
32 |
33 |
34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 98 | 99 | -------------------------------------------------------------------------------- /www/dist/js/Q.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2020/08/25 18:26:41 3 | !function(n,o){"use strict";var t,a,l,e=Object.prototype.toString,i=Object.prototype.hasOwnProperty,r=Array.prototype.slice;function u(t,e){return t!==o?t:e}function c(t){return"[object Function]"===e.call(t)}function f(t){return"number"==typeof t&&0 2 | 3 | 4 | 5 | 文件上传 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 | 22 |
23 |
24 |
25 | 26 | 27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 108 | 109 | -------------------------------------------------------------------------------- /www/demo/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 - 带上传参数、回调函数 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 113 | 114 | -------------------------------------------------------------------------------- /www/js/Q.Uploader.slice.js: -------------------------------------------------------------------------------- 1 | /// 2 | /* 3 | * Q.Uploader.slice.js 分片上传 4 | * author:devin87@qq.com 5 | * update:2020/08/25 18:25 6 | */ 7 | (function (window, undefined) { 8 | "use strict"; 9 | 10 | var Uploader = Q.Uploader; 11 | if (!Uploader.support.html5) return; 12 | 13 | var blobSlice = window.File ? File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice : undefined; 14 | 15 | Uploader.extend({ 16 | //分片上传+断点续传 17 | _upload_slice: function (task) { 18 | var self = this, 19 | file = task.blob || task.file, 20 | size = file.size, 21 | chunkSize = self.chunkSize, 22 | start = task.sliceStart || 0, 23 | retryCount = self.sliceRetryCount, 24 | end; 25 | 26 | //分片上传 27 | var upload = function (blob, callback, c) { 28 | c = +c || 0; 29 | 30 | var xhr = new XMLHttpRequest(), 31 | url = task.url, 32 | completed = end == size; 33 | 34 | task.xhr = xhr; 35 | 36 | url += (url.indexOf("?") == -1 ? "?" : "&") + "action=upload&hash=" + (task.hash || task.name) + "&ok=" + (completed ? "1" : "0"); 37 | 38 | xhr.upload.addEventListener("progress", function (e) { 39 | self.progress(task, size, start + e.loaded); 40 | }, false); 41 | 42 | xhr.addEventListener("load", function (e) { 43 | var responseText = e.target.responseText; 44 | if (completed) return self.complete(task, Uploader.COMPLETE, responseText); 45 | 46 | //分片上传成功继续上传 47 | if (responseText == 1 || responseText == '"1"') return callback(); 48 | 49 | //服务器返回失败 50 | self.complete(task, Uploader.ERROR, responseText); 51 | }, false); 52 | 53 | //分片上传失败 54 | xhr.addEventListener("error", function () { 55 | if (++c > retryCount) return self.complete(task, Uploader.ERROR); 56 | 57 | //重新上传 58 | upload(blob, callback, c); 59 | }, false); 60 | 61 | var fd = new FormData; 62 | 63 | //处理上传参数 64 | self._process_params(task, function (k, v) { 65 | fd.append(k, v); 66 | }); 67 | 68 | fd.append("fileName", task.name); 69 | if (task.size != -1) fd.append("fileSize", task.size); 70 | fd.append(self.upName, blob, task.name); 71 | fd.append("sliceCount", task.sliceCount); 72 | fd.append("sliceIndex", task.sliceIndex); 73 | 74 | xhr.open("POST", url); 75 | self._process_xhr_headers(xhr); 76 | 77 | //上传完毕 78 | if (completed) { 79 | self.fire("send", task, function (result) { 80 | if (result === false) return self.complete(task, Uploader.SKIP); 81 | 82 | xhr.send(fd); 83 | }); 84 | } else { 85 | xhr.send(fd); 86 | } 87 | }; 88 | 89 | //分片总数 90 | task.sliceCount = Math.ceil(size / chunkSize); 91 | 92 | //递归上传直至上传完毕 93 | var start_upload = function () { 94 | if (start >= size) { 95 | return; 96 | } 97 | 98 | end = start + chunkSize; 99 | if (end > size) end = size; 100 | 101 | task.sliceStart = start; 102 | task.sliceEnd = end; 103 | task.sliceIndex = Math.ceil(end / chunkSize); 104 | task.sliceBlob = blobSlice.call(file, start, end); 105 | 106 | //分片上传事件,分片上传之前触发,返回false将跳过该分片 107 | self.fire("sliceUpload", task, function (result) { 108 | if (result === false) return next_upload(); 109 | 110 | upload(task.sliceBlob, next_upload); 111 | }); 112 | }; 113 | 114 | //上传下一个分片 115 | var next_upload = function () { 116 | start = end; 117 | start_upload(); 118 | }; 119 | 120 | start_upload(); 121 | self._afterSend(task); 122 | } 123 | }); 124 | 125 | })(window); -------------------------------------------------------------------------------- /www/js/Q.Uploader.UI.File.js: -------------------------------------------------------------------------------- 1 | /// 2 | /* 3 | * Q.Uploader.UI.File.js 上传管理器界面 4 | * author:devin87@qq.com 5 | * update:2017/08/14 15:28 6 | */ 7 | (function (window, undefined) { 8 | "use strict"; 9 | 10 | var def = Q.def, 11 | 12 | getFirst = Q.getFirst, 13 | getLast = Q.getLast, 14 | getNext = Q.getNext, 15 | 16 | createEle = Q.createEle, 17 | formatSize = Q.formatSize, 18 | 19 | E = Q.event, 20 | addEvent = E.add, 21 | 22 | Uploader = Q.Uploader, 23 | Lang = Uploader.Lang; 24 | 25 | //追加css样式名 26 | function addClass(ele, className) { 27 | ele.className += " " + className; 28 | } 29 | 30 | //设置元素内容 31 | function setHtml(ele, html) { 32 | if (ele) ele.innerHTML = html || ""; 33 | } 34 | 35 | //文件上传UI(默认UI) 36 | Uploader.UI.File = { 37 | init: function () { 38 | var boxView = this.ops.view; 39 | if (!boxView) return; 40 | 41 | addClass(boxView, "ui-file " + (this.html5 ? "html5" : "html4")); 42 | }, 43 | 44 | //绘制任务UI 45 | draw: function (task) { 46 | var self = this, 47 | ops = self.ops, 48 | boxView = ops.view; 49 | 50 | if (!boxView) return; 51 | 52 | var ops_button = ops.button || {}, 53 | 54 | text_button_cancel = def(Lang.cancel || ops_button.cancel, "取消"), 55 | text_button_remove = def(Lang.remove || ops_button.remove, "删除"), 56 | 57 | name = task.name; 58 | 59 | var html = 60 | '
' + 61 | '
' + name + '
' + 62 | '
' + 63 | '
' + 64 | '
' + 65 | '
--/s
' + 66 | '
' + 67 | '
' + 68 | '
' + 69 | '
0%
' + 70 | '
' + 71 | '' + 75 | '
' + 76 | '
'; 77 | 78 | var taskId = task.id, 79 | box = createEle("div", "u-item", html); 80 | 81 | box.taskId = taskId; 82 | 83 | task.box = box; 84 | 85 | //添加到视图中 86 | boxView.appendChild(box); 87 | 88 | //---------------- 事件绑定 ---------------- 89 | var boxButton = getLast(box.childNodes[1]), 90 | buttonCancel = getFirst(boxButton), 91 | buttonRemove = getLast(boxButton); 92 | 93 | //取消上传任务 94 | addEvent(buttonCancel, "click", function () { 95 | self.cancel(taskId); 96 | }); 97 | 98 | //移除上传任务 99 | addEvent(buttonRemove, "click", function () { 100 | self.remove(taskId); 101 | 102 | boxView.removeChild(box); 103 | }); 104 | 105 | //---------------- 更新UI ---------------- 106 | self.update(task); 107 | }, 108 | 109 | //更新任务界面 110 | update: function (task) { 111 | if (!task || !task.box) return; 112 | 113 | var total = task.total || task.size, 114 | loaded = task.loaded, 115 | 116 | state = task.state; 117 | 118 | var box = task.box, 119 | boxInfo = box.childNodes[1], 120 | boxSize = getFirst(boxInfo), 121 | boxSpeed = getNext(boxSize), 122 | boxProgressbar = getNext(boxSpeed), 123 | boxProgress = getFirst(boxProgressbar), 124 | boxDetail = getNext(boxProgressbar), 125 | boxState = getNext(boxDetail); 126 | 127 | //更新任务状态 128 | setHtml(boxState, Uploader.getStatusText(state)); 129 | 130 | if (state == Uploader.ERROR) { 131 | var json = task.json || {}; 132 | box.title = json.msg || json.errMsg || task.response || Uploader.Lang.upload_error; 133 | } 134 | 135 | if (total < 0) return; 136 | 137 | var html_size = ''; 138 | 139 | //更新上传进度(for html5) 140 | if (this.html5 && loaded != undefined && loaded >= 0) { 141 | var percentText; 142 | 143 | if (state == Uploader.PROCESSING) { 144 | var percent = Math.min(loaded * 100 / total, 100); 145 | 146 | percentText = percent.toFixed(1); 147 | if (percentText == "100.0") percentText = "99.9"; 148 | 149 | } else if (state == Uploader.COMPLETE) { 150 | percentText = "100"; 151 | } 152 | 153 | //进度百分比 154 | if (percentText) { 155 | percentText += "%"; 156 | 157 | boxProgress.style.width = percentText; 158 | setHtml(boxDetail, percentText); 159 | } 160 | 161 | //已上传的文件大小 162 | html_size = '' + formatSize(loaded) + ' / '; 163 | 164 | //上传速度; 165 | var speed = task.avgSpeed || task.speed; 166 | setHtml(boxSpeed, formatSize(speed) + "/s"); 167 | } 168 | 169 | //文件总大小 170 | html_size += '' + formatSize(total) + ''; 171 | 172 | setHtml(boxSize, html_size); 173 | }, 174 | 175 | //上传完毕 176 | over: function (task) { 177 | if (!task || !task.box) return; 178 | 179 | addClass(task.box, "u-over"); 180 | } 181 | }; 182 | 183 | //实现默认的UI接口 184 | Uploader.extend(Uploader.UI.File); 185 | 186 | })(window); -------------------------------------------------------------------------------- /www/dist/js/spark-md5.js: -------------------------------------------------------------------------------- 1 | //spark-md5 v2.0.2 https://github.com/satazor/js-spark-md5 2 | //build:2016/06/16 14:06:36 3 | !function(t){if("object"==typeof exports)module.exports=t();else if("function"==typeof define&&define.amd)define(t);else{var r;try{r=window}catch(n){r=self}r.SparkMD5=t()}}(function(t){"use strict";function r(t,r,n,e,f,i){return r=d(d(r,t),d(e,i)),d(r<>>32-f,n)}function n(t,n,e,f,i,h,o){return r(n&e|~n&f,t,n,i,h,o)}function e(t,n,e,f,i,h,o){return r(n&f|e&~f,t,n,i,h,o)}function f(t,n,e,f,i,h,o){return r(n^e^f,t,n,i,h,o)}function i(t,n,e,f,i,h,o){return r(e^(n|~f),t,n,i,h,o)}function h(t,r){var h=t[0],o=t[1],u=t[2],s=t[3];h=n(h,o,u,s,r[0],7,-680876936),s=n(s,h,o,u,r[1],12,-389564586),u=n(u,s,h,o,r[2],17,606105819),o=n(o,u,s,h,r[3],22,-1044525330),h=n(h,o,u,s,r[4],7,-176418897),s=n(s,h,o,u,r[5],12,1200080426),u=n(u,s,h,o,r[6],17,-1473231341),o=n(o,u,s,h,r[7],22,-45705983),h=n(h,o,u,s,r[8],7,1770035416),s=n(s,h,o,u,r[9],12,-1958414417),u=n(u,s,h,o,r[10],17,-42063),o=n(o,u,s,h,r[11],22,-1990404162),h=n(h,o,u,s,r[12],7,1804603682),s=n(s,h,o,u,r[13],12,-40341101),u=n(u,s,h,o,r[14],17,-1502002290),o=n(o,u,s,h,r[15],22,1236535329),h=e(h,o,u,s,r[1],5,-165796510),s=e(s,h,o,u,r[6],9,-1069501632),u=e(u,s,h,o,r[11],14,643717713),o=e(o,u,s,h,r[0],20,-373897302),h=e(h,o,u,s,r[5],5,-701558691),s=e(s,h,o,u,r[10],9,38016083),u=e(u,s,h,o,r[15],14,-660478335),o=e(o,u,s,h,r[4],20,-405537848),h=e(h,o,u,s,r[9],5,568446438),s=e(s,h,o,u,r[14],9,-1019803690),u=e(u,s,h,o,r[3],14,-187363961),o=e(o,u,s,h,r[8],20,1163531501),h=e(h,o,u,s,r[13],5,-1444681467),s=e(s,h,o,u,r[2],9,-51403784),u=e(u,s,h,o,r[7],14,1735328473),o=e(o,u,s,h,r[12],20,-1926607734),h=f(h,o,u,s,r[5],4,-378558),s=f(s,h,o,u,r[8],11,-2022574463),u=f(u,s,h,o,r[11],16,1839030562),o=f(o,u,s,h,r[14],23,-35309556),h=f(h,o,u,s,r[1],4,-1530992060),s=f(s,h,o,u,r[4],11,1272893353),u=f(u,s,h,o,r[7],16,-155497632),o=f(o,u,s,h,r[10],23,-1094730640),h=f(h,o,u,s,r[13],4,681279174),s=f(s,h,o,u,r[0],11,-358537222),u=f(u,s,h,o,r[3],16,-722521979),o=f(o,u,s,h,r[6],23,76029189),h=f(h,o,u,s,r[9],4,-640364487),s=f(s,h,o,u,r[12],11,-421815835),u=f(u,s,h,o,r[15],16,530742520),o=f(o,u,s,h,r[2],23,-995338651),h=i(h,o,u,s,r[0],6,-198630844),s=i(s,h,o,u,r[7],10,1126891415),u=i(u,s,h,o,r[14],15,-1416354905),o=i(o,u,s,h,r[5],21,-57434055),h=i(h,o,u,s,r[12],6,1700485571),s=i(s,h,o,u,r[3],10,-1894986606),u=i(u,s,h,o,r[10],15,-1051523),o=i(o,u,s,h,r[1],21,-2054922799),h=i(h,o,u,s,r[8],6,1873313359),s=i(s,h,o,u,r[15],10,-30611744),u=i(u,s,h,o,r[6],15,-1560198380),o=i(o,u,s,h,r[13],21,1309151649),h=i(h,o,u,s,r[4],6,-145523070),s=i(s,h,o,u,r[11],10,-1120210379),u=i(u,s,h,o,r[2],15,718787259),o=i(o,u,s,h,r[9],21,-343485551),t[0]=d(h,t[0]),t[1]=d(o,t[1]),t[2]=d(u,t[2]),t[3]=d(s,t[3])}function o(t){var r,n=[];for(r=0;64>r;r+=4)n[r>>2]=t.charCodeAt(r)+(t.charCodeAt(r+1)<<8)+(t.charCodeAt(r+2)<<16)+(t.charCodeAt(r+3)<<24);return n}function u(t){var r,n=[];for(r=0;64>r;r+=4)n[r>>2]=t[r]+(t[r+1]<<8)+(t[r+2]<<16)+(t[r+3]<<24);return n}function s(t){var r,n,e,f,i,u,s=t.length,a=[1732584193,-271733879,-1732584194,271733878];for(r=64;s>=r;r+=64)h(a,o(t.substring(r-64,r)));for(t=t.substring(r-64),n=t.length,e=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],r=0;n>r;r+=1)e[r>>2]|=t.charCodeAt(r)<<(r%4<<3);if(e[r>>2]|=128<<(r%4<<3),r>55)for(h(a,e),r=0;16>r;r+=1)e[r]=0;return f=8*s,f=f.toString(16).match(/(.*?)(.{0,8})$/),i=parseInt(f[2],16),u=parseInt(f[1],16)||0,e[14]=i,e[15]=u,h(a,e),a}function a(t){var r,n,e,f,i,o,s=t.length,a=[1732584193,-271733879,-1732584194,271733878];for(r=64;s>=r;r+=64)h(a,u(t.subarray(r-64,r)));for(t=s>r-64?t.subarray(r-64):new Uint8Array(0),n=t.length,e=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],r=0;n>r;r+=1)e[r>>2]|=t[r]<<(r%4<<3);if(e[r>>2]|=128<<(r%4<<3),r>55)for(h(a,e),r=0;16>r;r+=1)e[r]=0;return f=8*s,f=f.toString(16).match(/(.*?)(.{0,8})$/),i=parseInt(f[2],16),o=parseInt(f[1],16)||0,e[14]=i,e[15]=o,h(a,e),a}function p(t){var r,n="";for(r=0;4>r;r+=1)n+=v[t>>8*r+4&15]+v[t>>8*r&15];return n}function y(t){var r;for(r=0;rn;n+=1)i[n]=t.charCodeAt(n);return r?i:f}function b(t){return String.fromCharCode.apply(null,new Uint8Array(t))}function g(t,r,n){var e=new Uint8Array(t.byteLength+r.byteLength);return e.set(new Uint8Array(t)),e.set(new Uint8Array(r),t.byteLength),n?e:e.buffer}function _(t){var r,n=[],e=t.length;for(r=0;e-1>r;r+=2)n.push(parseInt(t.substr(r,2),16));return String.fromCharCode.apply(String,n)}function A(){this.reset()}var d=function(t,r){return t+r&4294967295},v=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];return"5d41402abc4b2a76b9719d911017c592"!==y(s("hello"))&&(d=function(t,r){var n=(65535&t)+(65535&r),e=(t>>16)+(r>>16)+(n>>16);return e<<16|65535&n}),"undefined"==typeof ArrayBuffer||ArrayBuffer.prototype.slice||!function(){function r(t,r){return t=0|t||0,0>t?Math.max(t+r,0):Math.min(t,r)}ArrayBuffer.prototype.slice=function(n,e){var f,i,h,o,u=this.byteLength,s=r(n,u),a=u;return e!==t&&(a=r(e,u)),s>a?new ArrayBuffer(0):(f=a-s,i=new ArrayBuffer(f),h=new Uint8Array(i),o=new Uint8Array(this,s,f),h.set(o),i)}}(),A.prototype.append=function(t){return this.appendBinary(c(t)),this},A.prototype.appendBinary=function(t){this._buff+=t,this._length+=t.length;var r,n=this._buff.length;for(r=64;n>=r;r+=64)h(this._hash,o(this._buff.substring(r-64,r)));return this._buff=this._buff.substring(r-64),this},A.prototype.end=function(t){var r,n,e=this._buff,f=e.length,i=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(r=0;f>r;r+=1)i[r>>2]|=e.charCodeAt(r)<<(r%4<<3);return this._finish(i,f),n=y(this._hash),t&&(n=_(n)),this.reset(),n},A.prototype.reset=function(){return this._buff="",this._length=0,this._hash=[1732584193,-271733879,-1732584194,271733878],this},A.prototype.getState=function(){return{buff:this._buff,length:this._length,hash:this._hash}},A.prototype.setState=function(t){return this._buff=t.buff,this._length=t.length,this._hash=t.hash,this},A.prototype.destroy=function(){delete this._hash,delete this._buff,delete this._length},A.prototype._finish=function(t,r){var n,e,f,i=r;if(t[i>>2]|=128<<(i%4<<3),i>55)for(h(this._hash,t),i=0;16>i;i+=1)t[i]=0;n=8*this._length,n=n.toString(16).match(/(.*?)(.{0,8})$/),e=parseInt(n[2],16),f=parseInt(n[1],16)||0,t[14]=e,t[15]=f,h(this._hash,t)},A.hash=function(t,r){return A.hashBinary(c(t),r)},A.hashBinary=function(t,r){var n=s(t),e=y(n);return r?_(e):e},A.ArrayBuffer=function(){this.reset()},A.ArrayBuffer.prototype.append=function(t){var r,n=g(this._buff.buffer,t,!0),e=n.length;for(this._length+=t.byteLength,r=64;e>=r;r+=64)h(this._hash,u(n.subarray(r-64,r)));return this._buff=e>r-64?new Uint8Array(n.buffer.slice(r-64)):new Uint8Array(0),this},A.ArrayBuffer.prototype.end=function(t){var r,n,e=this._buff,f=e.length,i=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(r=0;f>r;r+=1)i[r>>2]|=e[r]<<(r%4<<3);return this._finish(i,f),n=y(this._hash),t&&(n=_(n)),this.reset(),n},A.ArrayBuffer.prototype.reset=function(){return this._buff=new Uint8Array(0),this._length=0,this._hash=[1732584193,-271733879,-1732584194,271733878],this},A.ArrayBuffer.prototype.getState=function(){var t=A.prototype.getState.call(this);return t.buff=b(t.buff),t},A.ArrayBuffer.prototype.setState=function(t){return t.buff=l(t.buff,!0),A.prototype.setState.call(this,t)},A.ArrayBuffer.prototype.destroy=A.prototype.destroy,A.ArrayBuffer.prototype._finish=A.prototype._finish,A.ArrayBuffer.hash=function(t,r){var n=a(new Uint8Array(t)),e=y(n);return r?_(e):e},A}); -------------------------------------------------------------------------------- /www/demo/slice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传 - 带上传参数、回调函数 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 229 | 230 | -------------------------------------------------------------------------------- /www/dist/js/Q.Uploader.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2021/08/18 17:20:23 3 | !function(e,o){"use strict";var a=Q.def,u=Q.fire,i=Q.extend,l=Q.getFirst,n=Q.getLast,d=JSON.parse,s=Q.createEle,c=Q.parseHTML,p=Q.setOpacity,f=Q.getOffset||Q.offset,h=Q.md5File,t=Q.event,m=t.add,g=t.trigger,_=t.stop,v=!1,y=!1,w=!1,r=0,T=0,k=0,x=-1,S={};function b(e,t){t=e.lastIndexOf(t);return-1!=t?e.slice(t):""}function I(e){if(e){for(var t=e.split(","),r={},a=0,i=t.length;a
'),a.appendChild(e),r=l(e),a=n(e),t.iframe=r,t.form=a,e=function(){if(0==t.workerIdle){var e;try{e=r.contentWindow.document.body.innerHTML}catch(e){}t.complete(o,2,e)}},(a=r).attachEvent?a.attachEvent("onload",e):a.addEventListener("load",e,!1)),t.targets.forEach(function(e){t.clickTrigger?m(e,"click",function(e){!1!==t.fire("select",e)&&(t.resetInput(),g(t.inputFile,"click"))}):m(e,"mouseover",function(e){t.target=this,t.updatePos()})}),t.clickTrigger||(m(i,"click",function(e){!1===t.fire("select",e)&&_(e)}),p(i,0),t.resetInput()),t.run("init",o,"init")}},resetInput:function(){var t=this,e=t.boxInput;if(!e)return t;e.innerHTML='";e=l(e);return m(e,"change",function(e){t.add(this),t.html5||t.resetInput()}),t.inputFile=e,t.updatePos()},updatePos:function(e){var t=this;if(t.clickTrigger)return t;var r=t.getPos||f,a=t.boxInput,i=l(a),n=t.target,s=n.offsetWidth,o=n.offsetHeight,n=0==s?{left:-1e4,top:-1e4}:r(n);return a.style.width=i.style.width=s+"px",a.style.height=i.style.height=o+"px",a.style.left=n.left+"px",a.style.top=n.top+"px",e&&(a.style.zIndex=++k),t},fire:function(e,t,r){if(!r)return u(this.fns[e],this,t);var a=this.fns[e+"Async"];if(a)return u(a,this,t,r);r(u(this.fns[e],this,t))},run:function(e,t,r){e=this[e];return e&&u(e,this,t),r&&u(this.fns[r],this,t),this},addTask:function(e,t){if(e||t){var r,a,i=t?(r=t.webkitRelativePath||t.name||t.fileName,0===t.size?0:t.size||t.fileSize):(r=b(e.value,"\\").slice(1)||e.value,-1),n=this,s=b(r,".").toLowerCase();n.disallows&&n.disallows[s]||n.allows&&!n.allows[s]?a="ext":-1!=i&&n.maxSize&&i>n.maxSize&&(a="size");var o={id:++T,name:r,ext:s,size:i,input:e,file:t,state:a?x:0};return a&&(o.limited=a,o.disabled=!0),n.fire("add",o,function(e){!1===e||o.disabled||o.limited||(o.index=n.list.length,n.list.push(o),n.map[o.id]=o,n.run("draw",o,"draw"),n.auto&&n.start())}),o}},add:function(e){if("INPUT"==e.tagName){var t=e.files;if(t)for(var r=0,a=t.length;r'))}),r.fire("send",t,function(e){return!1===e?r.complete(t,x):(a.submit(),void r._afterSend(t))})},_afterSend:function(e){e.lastTime=e.startTime=Date.now(),e.state=1,this._lastTask=e,this.progress(e)},progress:function(e,t,r){t=t||e.size,(!r||r<0)&&(r=0);var a=e.state||0;0<(r=t 8 |
  • 轻量级,不依赖任何JS库,核心代码(Q.Uploader.js)仅约700行,min版本加起来不到12KB
  • 9 |
  • 纯JS代码,无需Flash,无需更改后台代码即可实现带进度条(IE10+、其它标准浏览器)的上传,其它(eg:IE6+)自动降级为传统方式上传
  • 10 |
  • 单独的图片上传UI,支持图片预览(IE6+、其它浏览器)和缩放(IE10+、其它浏览器)
  • 11 |
  • 支持秒传+分片上传+断点续传(IE10+、其它标准浏览器),适应多种上传环境(默认基于md5,不限于浏览器和使用场景,只要文件相同即可;也可自行实现hash计算)
  • 12 |
  • 上传文件的同时可以指定上传参数,支持上传类型过滤
  • 13 |
  • 完善的事件回调,可针对上传的每个过程进行单独处理
  • 14 |
  • 上传核心与UI界面分离,方便的UI接口,可以很方便的定制上传界面包括上传按钮
  • 15 | 16 | 17 | ### 演示环境(其它语言可自行实现服务端接收和网站部署) 18 | 演示前请在根目录下创建upload文件夹,以保存上传文件
    19 | 执行程序:app.js 上传处理:/upload 20 | ``` 21 | 1. 执行演示程序前请确保已安装node环境(http://www.nodejs.org/download/) 22 | 2. 安装依赖,windows 环境可双击运行 install.bat 23 | 3. 命令行执行 node app.js ,windows 环境可双击运行 start.bat 来快速启动程序 24 | 4. 打开浏览器访问 http://127.0.0.1:3000/ 25 | 26 | ``` 27 | 28 | 源码见[web-uploader](https://github.com/devin87/web-uploader) 29 | 30 | ### 简单调用示例 31 | 例:一般文件上传,使用默认的UI 32 | ``` 33 | 1. 导入样式文件(若自己实现UI接口,则无需导入默认的样式文件) 34 | 35 | 36 | 2. 导入js文件(可自行合并,若自己实现UI接口,则无需导入 Q.Uploader.UI.js 文件) 37 | 38 | 39 | 40 | 41 | 或 42 | 43 | 44 | 3. 调用 45 | new Q.Uploader({ 46 | url:"api/upload.ashx", 47 | 48 | target: element, //上传按钮 49 | view: element //上传任务视图 50 | }); 51 | ``` 52 | 53 | 例:图片上传+预览+缩放 54 | ``` 55 | 1. 导入样式文件(若自己实现UI接口,则无需导入默认的样式文件) 56 | 57 | 58 | 2. 导入js文件(可自行合并) 59 | 60 | 61 | 62 | 63 | 或 64 | 65 | 66 | 3. 调用 67 | new Q.Uploader({ 68 | url:"api/upload.ashx", 69 | 70 | target: element, //上传按钮 71 | view: element, //上传任务视图 72 | 73 | allows: ".jpg,.png,.gif,.bmp", 74 | 75 | //图片缩放 76 | scale: { 77 | //要缩放的图片格式 78 | types: ".jpg", 79 | //最大图片宽度(maxWidth)或高度(maxHeight) 80 | maxWidth: 1024 81 | } 82 | }); 83 | ``` 84 | 85 | ### 完整调用示例 86 | ```javascript 87 | new Q.Uploader({ 88 | //--------------- 必填 --------------- 89 | url: "", //上传路径 90 | target: element, //上传按钮,可为数组 eg:[element1,element2] 91 | view: element, //上传任务视图(若自己实现UI接口,则无需指定此参数) 92 | 93 | //--------------- 可选 --------------- 94 | html5: true, //是否启用html5上传,若浏览器不支持,则自动禁用 95 | multiple: true, //选择文件时是否允许多选,若浏览器不支持,则自动禁用(仅html5模式有效) 96 | 97 | clickTrigger:true, //是否启用click触发文件选择 eg: input.click() => IE9及以下不支持 98 | 99 | auto: true, //添加任务后是否立即上传 100 | 101 | data: {}, //上传文件的同时可以指定其它参数,该参数将以POST的方式提交到服务器 102 | 103 | dataType: "json", //服务器返回值类型 104 | 105 | workerThread: 1, //同时允许上传的任务数(仅html5模式有效) 106 | 107 | upName: "upfile", //上传参数名称,若后台需要根据name来获取上传数据,可配置此项 108 | accept: "", //指定浏览器接受的文件类型 eg:image/*,video/* => IE9及以下不支持 109 | isDir: false, //是否是文件夹上传(仅Webkit内核浏览器和新版火狐有效) 110 | 111 | allows: "", //允许上传的文件类型(扩展名),多个之间用逗号隔开 112 | disallows: "", //禁止上传的文件类型(扩展名) 113 | 114 | maxSize: 0, //允许上传的最大文件大小,字节,为0表示不限(仅对支持的浏览器生效,eg: IE10+、Firefox、Chrome) 115 | 116 | //秒传+分片上传+断点续传,具体见示例(demo/slice.html) 117 | isSlice: false, //是否启用分片上传,若为true,则isQueryState和isMd5默认为true 118 | chunkSize: 2 * 1024 * 1024, //默认分片大小为2MB 119 | //查询路径为: url?action=query&hash=file hash 120 | isQueryState:false, //是否查询文件状态(for 秒传或续传) 121 | isMd5: false, //是否计算上传文件md5值 122 | isUploadAfterHash:true, //是否在Hash计算完毕后再上传 123 | sliceRetryCount:2, //分片上传失败重试次数 124 | 125 | container:document.body, //一般无需指定 126 | getPos: function(){ }, //一般无需指定 127 | 128 | //上传回调事件(function) 129 | on: { 130 | init: function(){ }, //上传管理器初始化完毕后触发 131 | 132 | select: function(task){ }, //点击上传按钮准备选择上传文件之前触发,返回false可禁止选择文件 133 | add: function(task){ }, //添加任务之前触发,返回false将跳过该任务 134 | upload: function(task){ }, //上传任务之前触发,返回false将跳过该任务 135 | hashProgress: function(task){ }, //文件hash进度(仅isMd5为true时有效) 136 | hash: function(task){ }, //查询状态之前触发(for 秒传或续传) 137 | sliceQuery: function(task){ }, //秒传查询之前触发 138 | sliceUpload: function(task){ }, //分片上传之前触发,返回false将跳过该分片 139 | send: function(task){ }, //发送数据之前触发,返回false将跳过该任务 140 | 141 | cancel: function(task){ }, //取消上传任务后触发 142 | remove: function(task){ }, //移除上传任务后触发 143 | 144 | progress: function(task){ }, //上传进度发生变化后触发(仅html5模式有效) 145 | complete: function(task){ } //上传完成后触发 146 | }, 147 | 148 | //UI接口(function),若指定了以下方法,将忽略默认实现 149 | UI:{ 150 | init: function(){ }, //执行初始化操作 151 | draw: function(task){ }, //添加任务后绘制任务界面 152 | update: function(task){ }, //更新任务界面 153 | over: function(){ } //任务上传完成 154 | } 155 | }); 156 | ``` 157 | 158 | task属性说明 159 | ``` 160 | task = { 161 | id, //任务编号 162 | 163 | name, //上传文件名(包括扩展名) 164 | ext, //上传文件扩展名 165 | size, //上传文件大小(单位:Byte,若获取不到大小,则值为-1) 166 | 167 | input, //上传控件 168 | file, //上传数据(仅 html5) 169 | 170 | state, //上传状态 171 | 172 | limited, //若存在值,表示禁止上传的文件类型 173 | skip, //若为true,表示要跳过的任务 174 | 175 | //分片上传 176 | sliceCount, //分片总数 177 | sliceIndex, //当前分片数 178 | sliceStart, //当前分片上传的起始点 179 | sliceEnd, //当前分片上传的结束点 180 | sliceBlob, //当前分片数据 181 | 182 | //上传后会有如下属性(由于浏览器支持问题,以下部分属性可能不存在) 183 | xhr, //XMLHttpRequest对象(仅 html5) 184 | 185 | total, //总上传大小(单位:Byte) 186 | loaded, //已上传大小(单位:Byte) 187 | speed, //上传速度(单位:Byte/s) 188 | 189 | avgSpeed, //平均上传速度(仅上传完毕) 190 | 191 | startTime, //开始上传的时间 192 | endTime, //结束上传的时间(仅上传完毕) 193 | 194 | timeHash, //文件hash所用时间(毫秒,仅当isMd5为true) 195 | time, //上传所用时间(毫秒) 196 | 197 | deleted, //若为true,表示已删除的文件 198 | 199 | //文件成功上传 200 | queryOK, //仅秒传成功时为true 201 | response, //服务器返回的字符串 202 | json //response解析后的JSON对象(仅当 dataType 为json) 203 | }; 204 | ``` 205 | 206 | 回调事件示例 207 | ```javascript 208 | on: { 209 | add: function(task) { 210 | //task.limited存在值的任务不会上传,此处无需返回false 211 | switch(task.limited){ 212 | case 'ext':return alert("允许上传的文件格式为:" + this.ops.allows); 213 | case 'size':return alert("允许上传的最大文件大小为:" + Q.formatSize(this.ops.maxSize)); 214 | } 215 | 216 | //自定义判断,返回false时该文件不会添加到上传队列 217 | //return false; 218 | }, 219 | upload: function(task) { 220 | //可以针对单个task指定上传参数,该参数将以POST的方式提交到服务器 221 | task.data = {}; 222 | }, 223 | complete: function (task) { 224 | var json = task.json; 225 | if (!json) return alert("上传已完成,但无法获取服务器返回数据!"); 226 | if (json.ret != 1) return alert(json.msg || "上传失败!"); 227 | 228 | alert("上传成功!"); 229 | } 230 | } 231 | ``` 232 | 233 | 234 | 说明:回调事件(add、upload、hash、sliceUpload、send)支持异步调用,只需在后面加上Async即可,比如在上传之前需要访问服务器验证数据,通过的就上传,否则跳过 235 | ```javascript 236 | on: { 237 | uploadAsync: function (task, callback) { 238 | $.postJSON(url, function (json) { 239 | //若 json.ok 返回false,该任务不会上传 240 | callback(json.ok); 241 | }); 242 | }, 243 | //计算文件hash 244 | hashAsync: function(task, callback){ 245 | Q.md5File(task.file, function(md5){ 246 | //task.hash:秒传或断点续传唯一标识 247 | task.hash = md5; 248 | callback(); 249 | }); 250 | }, 251 | //分片上传之前触发 252 | sliceUploadAsync: function (task, callback) { 253 | log(task.name + ": 上传分片 " + task.sliceIndex + " / " + task.sliceCount); 254 | 255 | callback(); 256 | } 257 | } 258 | ``` 259 | 260 | 261 | ### 手动操作(api) 262 | ``` 263 | var uploader = new Q.Uploader(ops); 264 | 265 | //上传任务列表,数组 266 | uploader.list 267 | 268 | //当前上传任务索引 => var task = uploader.list[uploader.index]; 269 | uploader.index 270 | 271 | //更改上传配置 272 | uploader.set(settings); 273 | 274 | //添加上传任务,支持文件多选、input元素和文件对象 => input.files | input | file 275 | uploader.add(input_or_file); 276 | 277 | //批量添加上传任务 list => [input_or_file] 278 | uploader.addList(list); 279 | 280 | //手动开始上传(默认自动上传,实例化时可配置 ops.auto=false) 281 | uploader.start(); 282 | 283 | //上传一个任务 284 | uploader.upload(task); 285 | 286 | //取消上传任务 287 | //onlyCancel: 若为true,则仅取消上传而不触发任务完成事件 288 | uploader.cancel(taskId, onlyCancel); 289 | 290 | //移除上传任务,会先调用 uploader.cancel(taskId) 291 | uploader.remove(taskId); 292 | 293 | //更新上传进度 294 | //total : 总上传数据(byte) 295 | //loaded : 已上传数据(byte) 296 | uploader.progress(task, total, loaded); 297 | 298 | ``` 299 | 300 | ### 自定义UI实现 301 | 可以在初始化时指定UI处理函数,亦可以通过扩展的方式实现 302 | ``` 303 | Uploader.extend({ 304 | //初始化操作,一般无需处理 305 | init: function () { }, 306 | 307 | //绘制任务界面 308 | draw: function (task) { }, 309 | 310 | //更新上传进度 311 | update: function (task) { }, 312 | 313 | //上传完毕,一般无需处理 314 | over: function () { } 315 | }); 316 | ``` -------------------------------------------------------------------------------- /www/js/Q.Uploader.UI.Image.js: -------------------------------------------------------------------------------- 1 | /// 2 | /* 3 | * Q.Uploader.UI.Image.js 图片上传管理器界面 4 | * author:devin87@qq.com 5 | * update:2018/04/12 10:05 6 | */ 7 | (function (window, undefined) { 8 | "use strict"; 9 | 10 | var getFirst = Q.getFirst, 11 | //getLast = Q.getLast, 12 | getNext = Q.getNext, 13 | 14 | createEle = Q.createEle, 15 | //formatSize = Q.formatSize, 16 | setOpacity = Q.setOpacity, 17 | 18 | browser_ie = Q.ie, 19 | 20 | Uploader = Q.Uploader; 21 | 22 | //追加css样式名 23 | function addClass(ele, className) { 24 | ele.className += " " + className; 25 | } 26 | 27 | //设置元素内容 28 | function setHtml(ele, html) { 29 | if (ele) ele.innerHTML = html || ""; 30 | } 31 | 32 | //生成图片预览地址(html5) 33 | function readAsURL(file, callback) { 34 | var URL = window.URL || window.webkitURL; 35 | if (URL) return callback(URL.createObjectURL(file)); 36 | 37 | if (window.FileReader) { 38 | var fr = new FileReader(); 39 | fr.onload = function (e) { 40 | callback(e.target.result); 41 | }; 42 | fr.readAsDataURL(file); 43 | } else if (file.readAsDataURL) { 44 | callback(file.readAsDataURL()); 45 | } 46 | } 47 | 48 | //图片预览 49 | function previewImage(box, task, callback) { 50 | var input = task.input, 51 | file = task.file || (input.files ? input.files[0] : undefined); 52 | 53 | if (file) { 54 | //IE10+、Webkit、Firefox etc 55 | readAsURL(file, function (src) { 56 | if (src) box.innerHTML = ''; 57 | 58 | callback && callback(src); 59 | }); 60 | } else if (input) { 61 | var src = input.value; 62 | 63 | if (!src || /^\w:\\fakepath/.test(src)) { 64 | input.select(); 65 | //解决ie报拒绝访问的问题 66 | parent.document.body.focus(); 67 | //获取图片真实地址 68 | if (document.selection) src = document.selection.createRange().text; 69 | } 70 | 71 | if (src) { 72 | box.innerHTML = ''; 73 | 74 | try { 75 | if (browser_ie > 6) box.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src='" + src + "')"; 76 | } catch (e) { } 77 | } 78 | 79 | callback && callback(src); 80 | } 81 | } 82 | 83 | var Blob = window.Blob || window.WebkitBlob || window.MozBlob, 84 | BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder; 85 | 86 | //是否支持图片缩放 87 | var support_image_scale = (function () { 88 | if (!window.FileReader || !window.atob || !(Blob || BlobBuilder)) return false; 89 | 90 | var canvas = document.createElement("canvas"); 91 | return !!canvas.getContext && !!canvas.getContext("2d"); 92 | })(); 93 | 94 | //获取mimetype 95 | function get_image_mimetype(ext) { 96 | var type = ext.slice(1); 97 | return "image/" + (type == "jpg" ? "jpeg" : type); 98 | } 99 | 100 | //将dataURL转为Blob对象,以用于ajax上传 101 | function dataURLtoBlob(base64, mimetype) { 102 | var ds = base64.split(','), 103 | data = atob(ds[1]), 104 | 105 | arr = []; 106 | 107 | for (var i = 0, len = data.length; i < len; i++) { 108 | arr[i] = data.charCodeAt(i); 109 | } 110 | 111 | if (Blob) return new Blob([new Uint8Array(arr)], { type: mimetype }); 112 | 113 | var builder = new BlobBuilder(); 114 | builder.append(arr); 115 | return builder.getBlob(mimetype); 116 | } 117 | 118 | //图片缩放 119 | function scaleImage(src, mimetype, ops, callback) { 120 | var image = new Image(); 121 | image.src = src; 122 | 123 | image.onload = function () { 124 | var width = image.width, 125 | height = image.height, 126 | 127 | maxWidth = ops.maxWidth, 128 | maxHeight = ops.maxHeight, 129 | 130 | hasWidthScale = maxWidth && width > maxWidth, 131 | hasHeightScale = maxHeight && height > maxHeight, 132 | 133 | hasScale = hasWidthScale || hasHeightScale; 134 | 135 | //无需压缩 136 | if (!hasScale) return callback && callback(false); 137 | 138 | //根据宽度缩放 139 | if (hasWidthScale) { 140 | width = maxWidth; 141 | height = Math.floor(image.height * width / image.width); 142 | } 143 | 144 | //根据高度缩放 145 | if (hasHeightScale) { 146 | height = maxHeight; 147 | width = Math.floor(image.width * height / image.height); 148 | } 149 | 150 | var canvas = document.createElement("canvas"), 151 | ctx = canvas.getContext("2d"); 152 | 153 | canvas.width = width; 154 | canvas.height = height; 155 | 156 | ctx.drawImage(image, 0, 0, width, height); 157 | 158 | callback && callback(canvas.toDataURL(mimetype), mimetype); 159 | }; 160 | } 161 | 162 | //默认图片格式 163 | var DEF_IMAGE_TYPES = ".jpg,.png,.gif,.bmp,.webp,.tif,.tiff", 164 | //默认缩放的图片类型 165 | DEF_IMAGE_SCALE_TYPES = ".jpg"; 166 | 167 | //是否支持图片缩放 168 | Uploader.support.imageScale = support_image_scale; 169 | 170 | Uploader.previewImage = previewImage; 171 | Uploader.scaleImage = scaleImage; 172 | 173 | //图片上传UI 174 | Uploader.UI.Image = { 175 | //初始化 176 | init: function () { 177 | var ops = this.ops, 178 | boxView = ops.view; 179 | 180 | if (!ops.allows) ops.allows = DEF_IMAGE_TYPES; 181 | 182 | if (boxView) addClass(boxView, "ui-image " + (this.html5 ? "html5" : "html4")); 183 | }, 184 | 185 | //是否支持图片压缩 186 | supportScale: function (type) { 187 | if (!support_image_scale) return false; 188 | 189 | if (type == ".jpeg") type = ".jpg"; 190 | 191 | var types = (this.ops.scale || {}).types || DEF_IMAGE_SCALE_TYPES, 192 | isImage = DEF_IMAGE_TYPES.indexOf(type) != -1; 193 | 194 | if (!isImage) return false; 195 | 196 | return types == "*" || types.indexOf(type) != -1; 197 | }, 198 | 199 | //预览并压缩图片 200 | previewImage: function (boxImage, task, ops) { 201 | var self = this, 202 | scale_data = task.scale || ops.scale, 203 | support_scale = scale_data && self.supportScale(task.ext); 204 | 205 | if (support_scale) task.skip = true; 206 | 207 | previewImage(boxImage, task, function (src) { 208 | self.fire("preview", { task: task, src: src }); 209 | 210 | if (!src || !support_scale) return; 211 | 212 | scaleImage(src, get_image_mimetype(task.ext), scale_data, function (base64, mimetype) { 213 | if (base64) { 214 | var blob = dataURLtoBlob(base64, mimetype); 215 | task.blob = blob; 216 | 217 | self.fire("scale", { task: task, base64: base64, type: mimetype, blob: blob }); 218 | } 219 | 220 | task.skip = false; 221 | self.list.push(task); 222 | 223 | if (self.auto) self.start(); 224 | }); 225 | }); 226 | 227 | return self; 228 | }, 229 | 230 | //绘制任务UI 231 | draw: function (task) { 232 | var self = this, 233 | ops = self.ops, 234 | boxView = ops.view; 235 | 236 | if (!boxView) return; 237 | 238 | var name = task.name; 239 | 240 | var html = 241 | '
    ' + 242 | '
    ' + 243 | '
    ' + 244 | '
    ' + 245 | '
    ' + 246 | '
    ' + name + '
    ' + 247 | '
    ' + 248 | '
    X
    '; 249 | 250 | var taskId = task.id, 251 | box = createEle("div", "u-item", html); 252 | 253 | box.taskId = taskId; 254 | 255 | var boxImage = getFirst(box), 256 | boxProgressbar = getNext(boxImage), 257 | boxProgress = getFirst(boxProgressbar), 258 | boxDetail = getNext(boxProgressbar), 259 | boxName = getNext(boxDetail), 260 | boxCloseBg = getNext(boxName), 261 | boxCloseBtn = getNext(boxCloseBg); 262 | 263 | setOpacity(boxProgressbar, 0.3); 264 | setOpacity(boxProgress, 0.5); 265 | setOpacity(boxCloseBg, 0.3); 266 | 267 | boxCloseBtn.onclick = function () { 268 | boxView.removeChild(box); 269 | self.remove(taskId); 270 | }; 271 | 272 | task.box = box; 273 | task.boxProgress = boxProgress; 274 | task.boxDetail = boxDetail; 275 | task.boxName = boxName; 276 | 277 | //添加到视图中 278 | boxView.appendChild(box); 279 | 280 | //---------------- 预览图片并更新UI ---------------- 281 | self.previewImage(boxImage, task, ops).update(task); 282 | }, 283 | 284 | //更新任务界面 285 | update: function (task) { 286 | if (!task || !task.box) return; 287 | 288 | var self = this, 289 | 290 | total = task.total || task.size, 291 | loaded = task.loaded, 292 | 293 | state = task.state, 294 | 295 | boxProgress = task.boxProgress, 296 | boxDetail = task.boxDetail, 297 | 298 | html_detail = Uploader.getStatusText(state); 299 | 300 | if (self.html5 && loaded != undefined && loaded >= 0) { 301 | var percentText; 302 | 303 | if (state == Uploader.PROCESSING) { 304 | var percent = Math.min(loaded * 100 / total, 100); 305 | 306 | percentText = percent.toFixed(1); 307 | if (percentText == "100.0") percentText = "99.9"; 308 | 309 | } else if (state == Uploader.COMPLETE) { 310 | percentText = "100"; 311 | } 312 | 313 | //进度百分比 314 | if (percentText) { 315 | percentText += "%"; 316 | html_detail += " " + percentText; 317 | 318 | boxProgress.style.width = percentText; 319 | } 320 | } 321 | 322 | setHtml(boxDetail, html_detail); 323 | }, 324 | 325 | //上传完毕 326 | over: function (task) { 327 | if (!task || !task.box) return; 328 | 329 | addClass(task.box, "u-over"); 330 | } 331 | }; 332 | 333 | //实现默认的UI接口 334 | Uploader.extend(Uploader.UI.Image); 335 | 336 | })(window); -------------------------------------------------------------------------------- /www/js/Q.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Q.js for Uploader 3 | * author:devin87@qq.com 4 | * update:2017/09/22 14:50 5 | */ 6 | (function (window, undefined) { 7 | "use strict"; 8 | 9 | var toString = Object.prototype.toString, 10 | has = Object.prototype.hasOwnProperty, 11 | slice = Array.prototype.slice; 12 | 13 | //若value不为undefine,则返回value;否则返回defValue 14 | function def(value, defValue) { 15 | return value !== undefined ? value : defValue; 16 | } 17 | 18 | //检测是否为函数 19 | function isFunc(fn) { 20 | //在ie11兼容模式(ie6-8)下存在bug,当调用次数过多时可能返回不正确的结果 21 | //return typeof fn == "function"; 22 | 23 | return toString.call(fn) === "[object Function]"; 24 | } 25 | 26 | //检测是否为正整数 27 | function isUInt(n) { 28 | return typeof n == "number" && n > 0 && n === Math.floor(n); 29 | } 30 | 31 | //触发指定函数,如果函数不存在,则不触发 32 | function fire(fn, bind) { 33 | if (isFunc(fn)) return fn.apply(bind, slice.call(arguments, 2)); 34 | } 35 | 36 | //扩展对象 37 | //forced:是否强制扩展 38 | function extend(destination, source, forced) { 39 | if (!destination || !source) return destination; 40 | 41 | for (var key in source) { 42 | if (key == undefined || !has.call(source, key)) continue; 43 | 44 | if (forced || destination[key] === undefined) destination[key] = source[key]; 45 | } 46 | return destination; 47 | } 48 | 49 | //Object.forEach 50 | extend(Object, { 51 | //遍历对象 52 | forEach: function (obj, fn, bind) { 53 | for (var key in obj) { 54 | if (has.call(obj, key)) fn.call(bind, key, obj[key], obj); 55 | } 56 | } 57 | }); 58 | 59 | extend(Array.prototype, { 60 | //遍历对象 61 | forEach: function (fn, bind) { 62 | var self = this; 63 | for (var i = 0, len = self.length; i < len; i++) { 64 | if (i in self) fn.call(bind, self[i], i, self); 65 | } 66 | } 67 | }); 68 | 69 | extend(Date, { 70 | //获取当前日期和时间所代表的毫秒数 71 | now: function () { 72 | return +new Date; 73 | } 74 | }); 75 | 76 | //-------------------------- browser --------------------------- 77 | var browser_ie; 78 | 79 | //ie11 开始不再保持向下兼容(例如,不再支持 ActiveXObject、attachEvent 等特性) 80 | if (window.ActiveXObject || window.msIndexedDB) { 81 | //window.ActiveXObject => ie10- 82 | //window.msIndexedDB => ie11+ 83 | 84 | browser_ie = document.documentMode || (!!window.XMLHttpRequest ? 7 : 6); 85 | } 86 | 87 | //-------------------------- json --------------------------- 88 | 89 | //json解析 90 | //secure:是否进行安全检测 91 | function json_decode(text, secure) { 92 | //安全检测 93 | if (secure !== false && !/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) throw new Error("JSON SyntaxError"); 94 | try { 95 | return (new Function("return " + text))(); 96 | } catch (e) { } 97 | } 98 | 99 | if (!window.JSON) window.JSON = {}; 100 | if (!JSON.parse) JSON.parse = json_decode; 101 | 102 | //-------------------------- DOM --------------------------- 103 | 104 | //设置元素透明 105 | function setOpacity(ele, value) { 106 | if (value <= 1) value *= 100; 107 | 108 | if (ele.style.opacity != undefined) ele.style.opacity = value / 100; 109 | else if (ele.style.filter != undefined) ele.style.filter = "alpha(opacity=" + parseInt(value) + ")"; 110 | } 111 | 112 | //获取元素绝对定位 113 | function getOffset(ele, root) { 114 | var left = 0, top = 0, width = ele.offsetWidth, height = ele.offsetHeight; 115 | 116 | do { 117 | left += ele.offsetLeft; 118 | top += ele.offsetTop; 119 | ele = ele.offsetParent; 120 | } while (ele && ele != root); 121 | 122 | return { left: left, top: top, width: width, height: height }; 123 | } 124 | 125 | //遍历元素节点 126 | function walk(ele, walk, start, all) { 127 | var el = ele[start || walk]; 128 | var list = []; 129 | while (el) { 130 | if (el.nodeType == 1) { 131 | if (!all) return el; 132 | list.push(el); 133 | } 134 | el = el[walk]; 135 | } 136 | return all ? list : null; 137 | } 138 | 139 | //获取上一个元素节点 140 | function getPrev(ele) { 141 | return ele.previousElementSibling || walk(ele, "previousSibling", null, false); 142 | } 143 | 144 | //获取下一个元素节点 145 | function getNext(ele) { 146 | return ele.nextElementSibling || walk(ele, "nextSibling", null, false); 147 | } 148 | 149 | //获取第一个元素子节点 150 | function getFirst(ele) { 151 | return ele.firstElementChild || walk(ele, "nextSibling", "firstChild", false); 152 | } 153 | 154 | //获取最后一个元素子节点 155 | function getLast(ele) { 156 | return ele.lastElementChild || walk(ele, "previousSibling", "lastChild", false); 157 | } 158 | 159 | //获取所有子元素节点 160 | function getChilds(ele) { 161 | return ele.children || walk(ele, "nextSibling", "firstChild", true); 162 | } 163 | 164 | //创建元素 165 | function createEle(tagName, className, html) { 166 | var ele = document.createElement(tagName); 167 | if (className) ele.className = className; 168 | if (html) ele.innerHTML = html; 169 | 170 | return ele; 171 | } 172 | 173 | //解析html标签 174 | function parseHTML(html, all) { 175 | var box = createEle("div", "", html); 176 | return all ? box.childNodes : getFirst(box); 177 | } 178 | 179 | //-------------------------- event --------------------------- 180 | 181 | var addEvent, 182 | removeEvent; 183 | 184 | if (document.addEventListener) { //w3c 185 | addEvent = function (ele, type, fn) { 186 | ele.addEventListener(type, fn, false); 187 | }; 188 | 189 | removeEvent = function (ele, type, fn) { 190 | ele.removeEventListener(type, fn, false); 191 | }; 192 | } else if (document.attachEvent) { //IE 193 | addEvent = function (ele, type, fn) { 194 | ele.attachEvent("on" + type, fn); 195 | }; 196 | 197 | removeEvent = function (ele, type, fn) { 198 | ele.detachEvent("on" + type, fn); 199 | }; 200 | } 201 | 202 | //event简单处理 203 | function fix_event(event) { 204 | var e = event || window.event; 205 | 206 | //for ie 207 | if (!e.target) e.target = e.srcElement; 208 | 209 | return e; 210 | } 211 | 212 | //添加事件 213 | function add_event(element, type, handler, once) { 214 | var fn = function (e) { 215 | handler.call(element, fix_event(e)); 216 | 217 | if (once) removeEvent(element, type, fn); 218 | }; 219 | 220 | addEvent(element, type, fn); 221 | 222 | if (!once) { 223 | return { 224 | //直接返回停止句柄 eg:var api=add_event();api.stop(); 225 | stop: function () { 226 | removeEvent(element, type, fn); 227 | } 228 | }; 229 | } 230 | } 231 | 232 | //触发事件 233 | function trigger_event(ele, type) { 234 | if (isFunc(ele[type])) ele[type](); 235 | else if (ele.fireEvent) ele.fireEvent("on" + type); //ie10- 236 | else if (ele.dispatchEvent) { 237 | var evt = document.createEvent("HTMLEvents"); 238 | 239 | //initEvent接受3个参数:事件类型,是否冒泡,是否阻止浏览器的默认行为 240 | evt.initEvent(type, true, true); 241 | 242 | //鼠标事件,设置更多参数 243 | //var evt = document.createEvent("MouseEvents"); 244 | //evt.initMouseEvent(type, true, true, ele.ownerDocument.defaultView, 1, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null); 245 | 246 | ele.dispatchEvent(evt); 247 | } 248 | } 249 | 250 | //阻止事件默认行为并停止事件冒泡 251 | function stop_event(event, isPreventDefault, isStopPropagation) { 252 | var e = fix_event(event); 253 | 254 | //阻止事件默认行为 255 | if (isPreventDefault !== false) { 256 | if (e.preventDefault) e.preventDefault(); 257 | else e.returnValue = false; 258 | } 259 | 260 | //停止事件冒泡 261 | if (isStopPropagation !== false) { 262 | if (e.stopPropagation) e.stopPropagation(); 263 | else e.cancelBubble = true; 264 | } 265 | } 266 | 267 | //---------------------- other ---------------------- 268 | 269 | var RE_HTTP = /^https?:\/\//i; 270 | 271 | //是否http路径(以 http:// 或 https:// 开头) 272 | function isHttpURL(url) { 273 | return RE_HTTP.test(url); 274 | } 275 | 276 | //判断指定路径与当前页面是否同域(包括协议检测 eg:http与https不同域) 277 | function isSameHost(url) { 278 | if (!isHttpURL(url)) return true; 279 | 280 | var start = RegExp.lastMatch.length, 281 | end = url.indexOf("/", start), 282 | host = url.slice(0, end != -1 ? end : undefined); 283 | 284 | return host.toLowerCase() == (location.protocol + "//" + location.host).toLowerCase(); 285 | } 286 | 287 | //按照进制解析数字的层级 eg:时间转化 -> parseLevel(86400,[60,60,24]) => { value=1, level=3 } 288 | //steps:步进,可以是固定的数字(eg:1024),也可以是具有层次关系的数组(eg:[60,60,24]) 289 | //limit:限制解析的层级,正整数,默认为100 290 | function parseLevel(size, steps, limit) { 291 | size = +size; 292 | steps = steps || 1024; 293 | 294 | var level = 0, 295 | isNum = typeof steps == "number", 296 | stepNow = 1, 297 | count = isUInt(limit) ? limit : (isNum ? 100 : steps.length); 298 | 299 | while (size >= stepNow && level < count) { 300 | stepNow *= (isNum ? steps : steps[level]); 301 | level++; 302 | } 303 | 304 | if (level && size < stepNow) { 305 | stepNow /= (isNum ? steps : steps.last()); 306 | level--; 307 | } 308 | 309 | return { value: level ? size / stepNow : size, level: level }; 310 | } 311 | 312 | var UNITS_FILE_SIZE = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]; 313 | 314 | //格式化数字输出,将数字转为合适的单位输出,默认按照1024层级转为文件单位输出 315 | function formatSize(size, ops) { 316 | ops = ops === true ? { all: true } : ops || {}; 317 | 318 | if (isNaN(size) || size == undefined || size < 0) { 319 | var error = ops.error || "--"; 320 | 321 | return ops.all ? { text: error } : error; 322 | } 323 | 324 | var pl = parseLevel(size, ops.steps, ops.limit), 325 | 326 | value = pl.value, 327 | text = value.toFixed(def(ops.digit, 2)); 328 | 329 | if (ops.trim !== false && text.lastIndexOf(".") != -1) text = text.replace(/\.?0+$/, ""); 330 | 331 | pl.text = text + (ops.join || "") + (ops.units || UNITS_FILE_SIZE)[pl.level + (ops.start || 0)]; 332 | 333 | return ops.all ? pl : pl.text; 334 | } 335 | 336 | //---------------------- export ---------------------- 337 | 338 | var Q = { 339 | def: def, 340 | isFunc: isFunc, 341 | isUInt: isUInt, 342 | 343 | fire: fire, 344 | extend: extend, 345 | 346 | ie: browser_ie, 347 | 348 | setOpacity: setOpacity, 349 | getOffset: getOffset, 350 | 351 | walk: walk, 352 | getPrev: getPrev, 353 | getNext: getNext, 354 | getFirst: getFirst, 355 | getLast: getLast, 356 | getChilds: getChilds, 357 | 358 | createEle: createEle, 359 | parseHTML: parseHTML, 360 | 361 | isHttpURL: isHttpURL, 362 | isSameHost: isSameHost, 363 | 364 | parseLevel: parseLevel, 365 | formatSize: formatSize 366 | }; 367 | 368 | if (browser_ie) Q["ie" + (browser_ie < 6 ? 6 : browser_ie)] = true; 369 | 370 | Q.event = { 371 | fix: fix_event, 372 | stop: stop_event, 373 | trigger: trigger_event, 374 | 375 | add: add_event 376 | }; 377 | 378 | window.Q = Q; 379 | 380 | })(window); -------------------------------------------------------------------------------- /www/dist/Q.Uploader.file.all.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2021/08/18 17:20:21 3 | !function(t,i){"use strict";var e,s,o,n=Object.prototype.toString,a=Object.prototype.hasOwnProperty,r=Array.prototype.slice;function l(e,t){return e!==i?e:t}function u(e){return"[object Function]"===n.call(e)}function c(e){return"number"==typeof e&&0
    '),r.appendChild(e),n=u(e),r=a(e),t.iframe=n,t.form=r,e=function(){if(0==t.workerIdle){var e;try{e=n.contentWindow.document.body.innerHTML}catch(e){}t.complete(o,2,e)}},(r=n).attachEvent?r.attachEvent("onload",e):r.addEventListener("load",e,!1)),t.targets.forEach(function(e){t.clickTrigger?m(e,"click",function(e){!1!==t.fire("select",e)&&(t.resetInput(),v(t.inputFile,"click"))}):m(e,"mouseover",function(e){t.target=this,t.updatePos()})}),t.clickTrigger||(m(i,"click",function(e){!1===t.fire("select",e)&&g(e)}),p(i,0),t.resetInput()),t.run("init",o,"init")}},resetInput:function(){var t=this,e=t.boxInput;if(!e)return t;e.innerHTML='";e=u(e);return m(e,"change",function(e){t.add(this),t.html5||t.resetInput()}),t.inputFile=e,t.updatePos()},updatePos:function(e){var t=this;if(t.clickTrigger)return t;var n=t.getPos||f,r=t.boxInput,i=u(r),a=t.target,s=a.offsetWidth,o=a.offsetHeight,a=0==s?{left:-1e4,top:-1e4}:n(a);return r.style.width=i.style.width=s+"px",r.style.height=i.style.height=o+"px",r.style.left=a.left+"px",r.style.top=a.top+"px",e&&(r.style.zIndex=++E),t},fire:function(e,t,n){if(!n)return l(this.fns[e],this,t);var r=this.fns[e+"Async"];if(r)return l(r,this,t,n);n(l(this.fns[e],this,t))},run:function(e,t,n){e=this[e];return e&&l(e,this,t),n&&l(this.fns[n],this,t),this},addTask:function(e,t){if(e||t){var n,r,i=t?(n=t.webkitRelativePath||t.name||t.fileName,0===t.size?0:t.size||t.fileSize):(n=b(e.value,"\\").slice(1)||e.value,-1),a=this,s=b(n,".").toLowerCase();a.disallows&&a.disallows[s]||a.allows&&!a.allows[s]?r="ext":-1!=i&&a.maxSize&&i>a.maxSize&&(r="size");var o={id:++w,name:n,ext:s,size:i,input:e,file:t,state:r?-1:0};return r&&(o.limited=r,o.disabled=!0),a.fire("add",o,function(e){!1===e||o.disabled||o.limited||(o.index=a.list.length,a.list.push(o),a.map[o.id]=o,a.run("draw",o,"draw"),a.auto&&a.start())}),o}},add:function(e){if("INPUT"==e.tagName){var t=e.files;if(t)for(var n=0,r=t.length;n'))}),n.fire("send",t,function(e){return!1===e?n.complete(t,-1):(r.submit(),void n._afterSend(t))})},_afterSend:function(e){e.lastTime=e.startTime=Date.now(),e.state=1,this._lastTask=e,this.progress(e)},progress:function(e,t,n){t=t||e.size,(!n||n<0)&&(n=0);var r=e.state||0;0<(n=tp?u.complete(o,h.ERROR):void f(e,t,n)},!1);var s=new FormData;u._process_params(o,function(e,t){s.append(e,t)}),s.append("fileName",o.name),-1!=o.size&&s.append("fileSize",o.size),s.append(u.upName,e,o.name),s.append("sliceCount",o.sliceCount),s.append("sliceIndex",o.sliceIndex),r.open("POST",i),u._process_xhr_headers(r),a?u.fire("send",o,function(e){return!1===e?u.complete(o,h.SKIP):void r.send(s)}):r.send(s)};o.sliceCount=Math.ceil(c/t);function n(){c<=d||(c<(l=d+t)&&(l=c),o.sliceStart=d,o.sliceEnd=l,o.sliceIndex=Math.ceil(l/t),o.sliceBlob=i.call(e,d,l),u.fire("sliceUpload",o,function(e){return!1===e?r():void f(o.sliceBlob,r)}))}var r=function(){d=l,n()};n(),u._afterSend(o)}}))}(window),function(){"use strict";var l=Q.def,d=Q.getFirst,u=Q.getLast,p=Q.getNext,c=Q.createEle,f=Q.formatSize,h=Q.event.add,m=Q.Uploader,v=m.Lang;function t(e,t){e.className+=" "+t}function g(e,t){e&&(e.innerHTML=t||"")}m.UI.File={init:function(){var e=this.ops.view;e&&t(e,"ui-file "+(this.html5?"html5":"html4"))},draw:function(e){var t,n,r,i,a=this,s=a.ops,o=s.view;o&&(t=s.button||{},i=l(v.cancel||t.cancel,"取消"),s=l(v.remove||t.remove,"删除"),i='
    '+t+'
    ',n=e.id,(r=c("div","u-item",i)).taskId=n,e.box=r,o.appendChild(r),s=u(r.childNodes[1]),i=d(s),s=u(s),h(i,"click",function(){a.cancel(n)}),h(s,"click",function(){a.remove(n),o.removeChild(r)}),a.update(e))},update:function(e){var t,n,r,i,a,s,o,l,u,c;e&&e.box&&(t=e.total||e.size,n=e.loaded,r=e.state,o=(i=e.box).childNodes[1],a=d(o),s=p(a),l=p(s),o=d(l),l=p(l),g(p(l),m.getStatusText(r)),r==m.ERROR&&(c=e.json||{},i.title=c.msg||c.errMsg||e.response||m.Lang.upload_error),t<0||(c="",this.html5&&null!=n&&0<=n&&(r==m.PROCESSING?"100.0"==(u=Math.min(100*n/t,100).toFixed(1))&&(u="99.9"):r==m.COMPLETE&&(u="100"),u&&(u+="%",g(l,o.style.width=u)),c=''+f(n)+" / ",e=e.avgSpeed||e.speed,g(s,f(e)+"/s")),g(a,c+=''+f(t)+"")))},over:function(e){e&&e.box&&t(e.box,"u-over")}},m.extend(m.UI.File)}(window); -------------------------------------------------------------------------------- /www/dist/Q.Uploader.image.all.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2021/08/18 17:20:22 3 | !function(t,i){"use strict";var e,s,o,r=Object.prototype.toString,a=Object.prototype.hasOwnProperty,n=Array.prototype.slice;function u(e,t){return e!==i?e:t}function l(e){return"[object Function]"===r.call(e)}function c(e){return"number"==typeof e&&0
    '),n.appendChild(e),r=l(e),n=a(e),t.iframe=r,t.form=n,e=function(){if(0==t.workerIdle){var e;try{e=r.contentWindow.document.body.innerHTML}catch(e){}t.complete(o,2,e)}},(n=r).attachEvent?n.attachEvent("onload",e):n.addEventListener("load",e,!1)),t.targets.forEach(function(e){t.clickTrigger?m(e,"click",function(e){!1!==t.fire("select",e)&&(t.resetInput(),v(t.inputFile,"click"))}):m(e,"mouseover",function(e){t.target=this,t.updatePos()})}),t.clickTrigger||(m(i,"click",function(e){!1===t.fire("select",e)&&g(e)}),f(i,0),t.resetInput()),t.run("init",o,"init")}},resetInput:function(){var t=this,e=t.boxInput;if(!e)return t;e.innerHTML='";e=l(e);return m(e,"change",function(e){t.add(this),t.html5||t.resetInput()}),t.inputFile=e,t.updatePos()},updatePos:function(e){var t=this;if(t.clickTrigger)return t;var r=t.getPos||p,n=t.boxInput,i=l(n),a=t.target,s=a.offsetWidth,o=a.offsetHeight,a=0==s?{left:-1e4,top:-1e4}:r(a);return n.style.width=i.style.width=s+"px",n.style.height=i.style.height=o+"px",n.style.left=a.left+"px",n.style.top=a.top+"px",e&&(n.style.zIndex=++_),t},fire:function(e,t,r){if(!r)return u(this.fns[e],this,t);var n=this.fns[e+"Async"];if(n)return u(n,this,t,r);r(u(this.fns[e],this,t))},run:function(e,t,r){e=this[e];return e&&u(e,this,t),r&&u(this.fns[r],this,t),this},addTask:function(e,t){if(e||t){var r,n,i=t?(r=t.webkitRelativePath||t.name||t.fileName,0===t.size?0:t.size||t.fileSize):(r=S(e.value,"\\").slice(1)||e.value,-1),a=this,s=S(r,".").toLowerCase();a.disallows&&a.disallows[s]||a.allows&&!a.allows[s]?n="ext":-1!=i&&a.maxSize&&i>a.maxSize&&(n="size");var o={id:++x,name:r,ext:s,size:i,input:e,file:t,state:n?-1:0};return n&&(o.limited=n,o.disabled=!0),a.fire("add",o,function(e){!1===e||o.disabled||o.limited||(o.index=a.list.length,a.list.push(o),a.map[o.id]=o,a.run("draw",o,"draw"),a.auto&&a.start())}),o}},add:function(e){if("INPUT"==e.tagName){var t=e.files;if(t)for(var r=0,n=t.length;r'))}),r.fire("send",t,function(e){return!1===e?r.complete(t,-1):(n.submit(),void r._afterSend(t))})},_afterSend:function(e){e.lastTime=e.startTime=Date.now(),e.state=1,this._lastTask=e,this.progress(e)},progress:function(e,t,r){t=t||e.size,(!r||r<0)&&(r=0);var n=e.state||0;0<(r=t'),r&&r(e)});else if(n){var i=n.value;if(i&&!/^\w:\\fakepath/.test(i)||(n.select(),parent.document.body.focus(),document.selection&&(i=document.selection.createRange().text)),i){t.innerHTML='';try{6
    '+u+'
    X
    ',t=e.id,(r=m("div","u-item",l)).taskId=t,n=p(r),i=h(n),a=p(i),s=h(i),o=h(s),u=h(o),l=h(u),v(i,.3),v(a,.5),v(u,.3),l.onclick=function(){f.removeChild(r),c.remove(t)},e.box=r,e.boxProgress=a,e.boxDetail=s,e.boxName=o,f.appendChild(r),c.previewImage(n,e,d).update(e))},update:function(e){var t,r,n,i,a,s;e&&e.box&&(t=e.total||e.size,r=e.loaded,n=e.state,i=e.boxProgress,s=e.boxDetail,e=o.getStatusText(n),this.html5&&null!=r&&0<=r&&(n==o.PROCESSING?"100.0"==(a=Math.min(100*r/t,100).toFixed(1))&&(a="99.9"):n==o.COMPLETE&&(a="100"),a&&(e+=" "+(a+="%"),i.style.width=a)),e=e,(s=s)&&(s.innerHTML=e||""))},over:function(e){e&&e.box&&r(e.box,"u-over")}},o.extend(o.UI.Image)}(window); -------------------------------------------------------------------------------- /www/js/spark-md5.js: -------------------------------------------------------------------------------- 1 | (function (factory) { 2 | if (typeof exports === 'object') { 3 | // Node/CommonJS 4 | module.exports = factory(); 5 | } else if (typeof define === 'function' && define.amd) { 6 | // AMD 7 | define(factory); 8 | } else { 9 | // Browser globals (with support for web workers) 10 | var glob; 11 | 12 | try { 13 | glob = window; 14 | } catch (e) { 15 | glob = self; 16 | } 17 | 18 | glob.SparkMD5 = factory(); 19 | } 20 | }(function (undefined) { 21 | 22 | 'use strict'; 23 | 24 | /* 25 | * Fastest md5 implementation around (JKM md5). 26 | * Credits: Joseph Myers 27 | * 28 | * @see http://www.myersdaily.org/joseph/javascript/md5-text.html 29 | * @see http://jsperf.com/md5-shootout/7 30 | */ 31 | 32 | /* this function is much faster, 33 | so if possible we use it. Some IEs 34 | are the only ones I know of that 35 | need the idiotic second function, 36 | generated by an if clause. */ 37 | var add32 = function (a, b) { 38 | return (a + b) & 0xFFFFFFFF; 39 | }, 40 | hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; 41 | 42 | 43 | function cmn(q, a, b, x, s, t) { 44 | a = add32(add32(a, q), add32(x, t)); 45 | return add32((a << s) | (a >>> (32 - s)), b); 46 | } 47 | 48 | function ff(a, b, c, d, x, s, t) { 49 | return cmn((b & c) | ((~b) & d), a, b, x, s, t); 50 | } 51 | 52 | function gg(a, b, c, d, x, s, t) { 53 | return cmn((b & d) | (c & (~d)), a, b, x, s, t); 54 | } 55 | 56 | function hh(a, b, c, d, x, s, t) { 57 | return cmn(b ^ c ^ d, a, b, x, s, t); 58 | } 59 | 60 | function ii(a, b, c, d, x, s, t) { 61 | return cmn(c ^ (b | (~d)), a, b, x, s, t); 62 | } 63 | 64 | function md5cycle(x, k) { 65 | var a = x[0], 66 | b = x[1], 67 | c = x[2], 68 | d = x[3]; 69 | 70 | a = ff(a, b, c, d, k[0], 7, -680876936); 71 | d = ff(d, a, b, c, k[1], 12, -389564586); 72 | c = ff(c, d, a, b, k[2], 17, 606105819); 73 | b = ff(b, c, d, a, k[3], 22, -1044525330); 74 | a = ff(a, b, c, d, k[4], 7, -176418897); 75 | d = ff(d, a, b, c, k[5], 12, 1200080426); 76 | c = ff(c, d, a, b, k[6], 17, -1473231341); 77 | b = ff(b, c, d, a, k[7], 22, -45705983); 78 | a = ff(a, b, c, d, k[8], 7, 1770035416); 79 | d = ff(d, a, b, c, k[9], 12, -1958414417); 80 | c = ff(c, d, a, b, k[10], 17, -42063); 81 | b = ff(b, c, d, a, k[11], 22, -1990404162); 82 | a = ff(a, b, c, d, k[12], 7, 1804603682); 83 | d = ff(d, a, b, c, k[13], 12, -40341101); 84 | c = ff(c, d, a, b, k[14], 17, -1502002290); 85 | b = ff(b, c, d, a, k[15], 22, 1236535329); 86 | 87 | a = gg(a, b, c, d, k[1], 5, -165796510); 88 | d = gg(d, a, b, c, k[6], 9, -1069501632); 89 | c = gg(c, d, a, b, k[11], 14, 643717713); 90 | b = gg(b, c, d, a, k[0], 20, -373897302); 91 | a = gg(a, b, c, d, k[5], 5, -701558691); 92 | d = gg(d, a, b, c, k[10], 9, 38016083); 93 | c = gg(c, d, a, b, k[15], 14, -660478335); 94 | b = gg(b, c, d, a, k[4], 20, -405537848); 95 | a = gg(a, b, c, d, k[9], 5, 568446438); 96 | d = gg(d, a, b, c, k[14], 9, -1019803690); 97 | c = gg(c, d, a, b, k[3], 14, -187363961); 98 | b = gg(b, c, d, a, k[8], 20, 1163531501); 99 | a = gg(a, b, c, d, k[13], 5, -1444681467); 100 | d = gg(d, a, b, c, k[2], 9, -51403784); 101 | c = gg(c, d, a, b, k[7], 14, 1735328473); 102 | b = gg(b, c, d, a, k[12], 20, -1926607734); 103 | 104 | a = hh(a, b, c, d, k[5], 4, -378558); 105 | d = hh(d, a, b, c, k[8], 11, -2022574463); 106 | c = hh(c, d, a, b, k[11], 16, 1839030562); 107 | b = hh(b, c, d, a, k[14], 23, -35309556); 108 | a = hh(a, b, c, d, k[1], 4, -1530992060); 109 | d = hh(d, a, b, c, k[4], 11, 1272893353); 110 | c = hh(c, d, a, b, k[7], 16, -155497632); 111 | b = hh(b, c, d, a, k[10], 23, -1094730640); 112 | a = hh(a, b, c, d, k[13], 4, 681279174); 113 | d = hh(d, a, b, c, k[0], 11, -358537222); 114 | c = hh(c, d, a, b, k[3], 16, -722521979); 115 | b = hh(b, c, d, a, k[6], 23, 76029189); 116 | a = hh(a, b, c, d, k[9], 4, -640364487); 117 | d = hh(d, a, b, c, k[12], 11, -421815835); 118 | c = hh(c, d, a, b, k[15], 16, 530742520); 119 | b = hh(b, c, d, a, k[2], 23, -995338651); 120 | 121 | a = ii(a, b, c, d, k[0], 6, -198630844); 122 | d = ii(d, a, b, c, k[7], 10, 1126891415); 123 | c = ii(c, d, a, b, k[14], 15, -1416354905); 124 | b = ii(b, c, d, a, k[5], 21, -57434055); 125 | a = ii(a, b, c, d, k[12], 6, 1700485571); 126 | d = ii(d, a, b, c, k[3], 10, -1894986606); 127 | c = ii(c, d, a, b, k[10], 15, -1051523); 128 | b = ii(b, c, d, a, k[1], 21, -2054922799); 129 | a = ii(a, b, c, d, k[8], 6, 1873313359); 130 | d = ii(d, a, b, c, k[15], 10, -30611744); 131 | c = ii(c, d, a, b, k[6], 15, -1560198380); 132 | b = ii(b, c, d, a, k[13], 21, 1309151649); 133 | a = ii(a, b, c, d, k[4], 6, -145523070); 134 | d = ii(d, a, b, c, k[11], 10, -1120210379); 135 | c = ii(c, d, a, b, k[2], 15, 718787259); 136 | b = ii(b, c, d, a, k[9], 21, -343485551); 137 | 138 | x[0] = add32(a, x[0]); 139 | x[1] = add32(b, x[1]); 140 | x[2] = add32(c, x[2]); 141 | x[3] = add32(d, x[3]); 142 | } 143 | 144 | function md5blk(s) { 145 | var md5blks = [], 146 | i; /* Andy King said do it this way. */ 147 | 148 | for (i = 0; i < 64; i += 4) { 149 | md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); 150 | } 151 | return md5blks; 152 | } 153 | 154 | function md5blk_array(a) { 155 | var md5blks = [], 156 | i; /* Andy King said do it this way. */ 157 | 158 | for (i = 0; i < 64; i += 4) { 159 | md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); 160 | } 161 | return md5blks; 162 | } 163 | 164 | function md51(s) { 165 | var n = s.length, 166 | state = [1732584193, -271733879, -1732584194, 271733878], 167 | i, 168 | length, 169 | tail, 170 | tmp, 171 | lo, 172 | hi; 173 | 174 | for (i = 64; i <= n; i += 64) { 175 | md5cycle(state, md5blk(s.substring(i - 64, i))); 176 | } 177 | s = s.substring(i - 64); 178 | length = s.length; 179 | tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 180 | for (i = 0; i < length; i += 1) { 181 | tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); 182 | } 183 | tail[i >> 2] |= 0x80 << ((i % 4) << 3); 184 | if (i > 55) { 185 | md5cycle(state, tail); 186 | for (i = 0; i < 16; i += 1) { 187 | tail[i] = 0; 188 | } 189 | } 190 | 191 | // Beware that the final length might not fit in 32 bits so we take care of that 192 | tmp = n * 8; 193 | tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); 194 | lo = parseInt(tmp[2], 16); 195 | hi = parseInt(tmp[1], 16) || 0; 196 | 197 | tail[14] = lo; 198 | tail[15] = hi; 199 | 200 | md5cycle(state, tail); 201 | return state; 202 | } 203 | 204 | function md51_array(a) { 205 | var n = a.length, 206 | state = [1732584193, -271733879, -1732584194, 271733878], 207 | i, 208 | length, 209 | tail, 210 | tmp, 211 | lo, 212 | hi; 213 | 214 | for (i = 64; i <= n; i += 64) { 215 | md5cycle(state, md5blk_array(a.subarray(i - 64, i))); 216 | } 217 | 218 | // Not sure if it is a bug, however IE10 will always produce a sub array of length 1 219 | // containing the last element of the parent array if the sub array specified starts 220 | // beyond the length of the parent array - weird. 221 | // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue 222 | a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0); 223 | 224 | length = a.length; 225 | tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 226 | for (i = 0; i < length; i += 1) { 227 | tail[i >> 2] |= a[i] << ((i % 4) << 3); 228 | } 229 | 230 | tail[i >> 2] |= 0x80 << ((i % 4) << 3); 231 | if (i > 55) { 232 | md5cycle(state, tail); 233 | for (i = 0; i < 16; i += 1) { 234 | tail[i] = 0; 235 | } 236 | } 237 | 238 | // Beware that the final length might not fit in 32 bits so we take care of that 239 | tmp = n * 8; 240 | tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); 241 | lo = parseInt(tmp[2], 16); 242 | hi = parseInt(tmp[1], 16) || 0; 243 | 244 | tail[14] = lo; 245 | tail[15] = hi; 246 | 247 | md5cycle(state, tail); 248 | 249 | return state; 250 | } 251 | 252 | function rhex(n) { 253 | var s = '', 254 | j; 255 | for (j = 0; j < 4; j += 1) { 256 | s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; 257 | } 258 | return s; 259 | } 260 | 261 | function hex(x) { 262 | var i; 263 | for (i = 0; i < x.length; i += 1) { 264 | x[i] = rhex(x[i]); 265 | } 266 | return x.join(''); 267 | } 268 | 269 | // In some cases the fast add32 function cannot be used.. 270 | if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') { 271 | add32 = function (x, y) { 272 | var lsw = (x & 0xFFFF) + (y & 0xFFFF), 273 | msw = (x >> 16) + (y >> 16) + (lsw >> 16); 274 | return (msw << 16) | (lsw & 0xFFFF); 275 | }; 276 | } 277 | 278 | // --------------------------------------------------- 279 | 280 | /** 281 | * ArrayBuffer slice polyfill. 282 | * 283 | * @see https://github.com/ttaubert/node-arraybuffer-slice 284 | */ 285 | 286 | if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) { 287 | (function () { 288 | function clamp(val, length) { 289 | val = (val | 0) || 0; 290 | 291 | if (val < 0) { 292 | return Math.max(val + length, 0); 293 | } 294 | 295 | return Math.min(val, length); 296 | } 297 | 298 | ArrayBuffer.prototype.slice = function (from, to) { 299 | var length = this.byteLength, 300 | begin = clamp(from, length), 301 | end = length, 302 | num, 303 | target, 304 | targetArray, 305 | sourceArray; 306 | 307 | if (to !== undefined) { 308 | end = clamp(to, length); 309 | } 310 | 311 | if (begin > end) { 312 | return new ArrayBuffer(0); 313 | } 314 | 315 | num = end - begin; 316 | target = new ArrayBuffer(num); 317 | targetArray = new Uint8Array(target); 318 | 319 | sourceArray = new Uint8Array(this, begin, num); 320 | targetArray.set(sourceArray); 321 | 322 | return target; 323 | }; 324 | })(); 325 | } 326 | 327 | // --------------------------------------------------- 328 | 329 | /** 330 | * Helpers. 331 | */ 332 | 333 | function toUtf8(str) { 334 | if (/[\u0080-\uFFFF]/.test(str)) { 335 | str = unescape(encodeURIComponent(str)); 336 | } 337 | 338 | return str; 339 | } 340 | 341 | function utf8Str2ArrayBuffer(str, returnUInt8Array) { 342 | var length = str.length, 343 | buff = new ArrayBuffer(length), 344 | arr = new Uint8Array(buff), 345 | i; 346 | 347 | for (i = 0; i < length; i += 1) { 348 | arr[i] = str.charCodeAt(i); 349 | } 350 | 351 | return returnUInt8Array ? arr : buff; 352 | } 353 | 354 | function arrayBuffer2Utf8Str(buff) { 355 | return String.fromCharCode.apply(null, new Uint8Array(buff)); 356 | } 357 | 358 | function concatenateArrayBuffers(first, second, returnUInt8Array) { 359 | var result = new Uint8Array(first.byteLength + second.byteLength); 360 | 361 | result.set(new Uint8Array(first)); 362 | result.set(new Uint8Array(second), first.byteLength); 363 | 364 | return returnUInt8Array ? result : result.buffer; 365 | } 366 | 367 | function hexToBinaryString(hex) { 368 | var bytes = [], 369 | length = hex.length, 370 | x; 371 | 372 | for (x = 0; x < length - 1; x += 2) { 373 | bytes.push(parseInt(hex.substr(x, 2), 16)); 374 | } 375 | 376 | return String.fromCharCode.apply(String, bytes); 377 | } 378 | 379 | // --------------------------------------------------- 380 | 381 | /** 382 | * SparkMD5 OOP implementation. 383 | * 384 | * Use this class to perform an incremental md5, otherwise use the 385 | * static methods instead. 386 | */ 387 | 388 | function SparkMD5() { 389 | // call reset to init the instance 390 | this.reset(); 391 | } 392 | 393 | /** 394 | * Appends a string. 395 | * A conversion will be applied if an utf8 string is detected. 396 | * 397 | * @param {String} str The string to be appended 398 | * 399 | * @return {SparkMD5} The instance itself 400 | */ 401 | SparkMD5.prototype.append = function (str) { 402 | // Converts the string to utf8 bytes if necessary 403 | // Then append as binary 404 | this.appendBinary(toUtf8(str)); 405 | 406 | return this; 407 | }; 408 | 409 | /** 410 | * Appends a binary string. 411 | * 412 | * @param {String} contents The binary string to be appended 413 | * 414 | * @return {SparkMD5} The instance itself 415 | */ 416 | SparkMD5.prototype.appendBinary = function (contents) { 417 | this._buff += contents; 418 | this._length += contents.length; 419 | 420 | var length = this._buff.length, 421 | i; 422 | 423 | for (i = 64; i <= length; i += 64) { 424 | md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i))); 425 | } 426 | 427 | this._buff = this._buff.substring(i - 64); 428 | 429 | return this; 430 | }; 431 | 432 | /** 433 | * Finishes the incremental computation, reseting the internal state and 434 | * returning the result. 435 | * 436 | * @param {Boolean} raw True to get the raw string, false to get the hex string 437 | * 438 | * @return {String} The result 439 | */ 440 | SparkMD5.prototype.end = function (raw) { 441 | var buff = this._buff, 442 | length = buff.length, 443 | i, 444 | tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 445 | ret; 446 | 447 | for (i = 0; i < length; i += 1) { 448 | tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3); 449 | } 450 | 451 | this._finish(tail, length); 452 | ret = hex(this._hash); 453 | 454 | if (raw) { 455 | ret = hexToBinaryString(ret); 456 | } 457 | 458 | this.reset(); 459 | 460 | return ret; 461 | }; 462 | 463 | /** 464 | * Resets the internal state of the computation. 465 | * 466 | * @return {SparkMD5} The instance itself 467 | */ 468 | SparkMD5.prototype.reset = function () { 469 | this._buff = ''; 470 | this._length = 0; 471 | this._hash = [1732584193, -271733879, -1732584194, 271733878]; 472 | 473 | return this; 474 | }; 475 | 476 | /** 477 | * Gets the internal state of the computation. 478 | * 479 | * @return {Object} The state 480 | */ 481 | SparkMD5.prototype.getState = function () { 482 | return { 483 | buff: this._buff, 484 | length: this._length, 485 | hash: this._hash 486 | }; 487 | }; 488 | 489 | /** 490 | * Gets the internal state of the computation. 491 | * 492 | * @param {Object} state The state 493 | * 494 | * @return {SparkMD5} The instance itself 495 | */ 496 | SparkMD5.prototype.setState = function (state) { 497 | this._buff = state.buff; 498 | this._length = state.length; 499 | this._hash = state.hash; 500 | 501 | return this; 502 | }; 503 | 504 | /** 505 | * Releases memory used by the incremental buffer and other additional 506 | * resources. If you plan to use the instance again, use reset instead. 507 | */ 508 | SparkMD5.prototype.destroy = function () { 509 | delete this._hash; 510 | delete this._buff; 511 | delete this._length; 512 | }; 513 | 514 | /** 515 | * Finish the final calculation based on the tail. 516 | * 517 | * @param {Array} tail The tail (will be modified) 518 | * @param {Number} length The length of the remaining buffer 519 | */ 520 | SparkMD5.prototype._finish = function (tail, length) { 521 | var i = length, 522 | tmp, 523 | lo, 524 | hi; 525 | 526 | tail[i >> 2] |= 0x80 << ((i % 4) << 3); 527 | if (i > 55) { 528 | md5cycle(this._hash, tail); 529 | for (i = 0; i < 16; i += 1) { 530 | tail[i] = 0; 531 | } 532 | } 533 | 534 | // Do the final computation based on the tail and length 535 | // Beware that the final length may not fit in 32 bits so we take care of that 536 | tmp = this._length * 8; 537 | tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); 538 | lo = parseInt(tmp[2], 16); 539 | hi = parseInt(tmp[1], 16) || 0; 540 | 541 | tail[14] = lo; 542 | tail[15] = hi; 543 | md5cycle(this._hash, tail); 544 | }; 545 | 546 | /** 547 | * Performs the md5 hash on a string. 548 | * A conversion will be applied if utf8 string is detected. 549 | * 550 | * @param {String} str The string 551 | * @param {Boolean} raw True to get the raw string, false to get the hex string 552 | * 553 | * @return {String} The result 554 | */ 555 | SparkMD5.hash = function (str, raw) { 556 | // Converts the string to utf8 bytes if necessary 557 | // Then compute it using the binary function 558 | return SparkMD5.hashBinary(toUtf8(str), raw); 559 | }; 560 | 561 | /** 562 | * Performs the md5 hash on a binary string. 563 | * 564 | * @param {String} content The binary string 565 | * @param {Boolean} raw True to get the raw string, false to get the hex string 566 | * 567 | * @return {String} The result 568 | */ 569 | SparkMD5.hashBinary = function (content, raw) { 570 | var hash = md51(content), 571 | ret = hex(hash); 572 | 573 | return raw ? hexToBinaryString(ret) : ret; 574 | }; 575 | 576 | // --------------------------------------------------- 577 | 578 | /** 579 | * SparkMD5 OOP implementation for array buffers. 580 | * 581 | * Use this class to perform an incremental md5 ONLY for array buffers. 582 | */ 583 | SparkMD5.ArrayBuffer = function () { 584 | // call reset to init the instance 585 | this.reset(); 586 | }; 587 | 588 | /** 589 | * Appends an array buffer. 590 | * 591 | * @param {ArrayBuffer} arr The array to be appended 592 | * 593 | * @return {SparkMD5.ArrayBuffer} The instance itself 594 | */ 595 | SparkMD5.ArrayBuffer.prototype.append = function (arr) { 596 | var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), 597 | length = buff.length, 598 | i; 599 | 600 | this._length += arr.byteLength; 601 | 602 | for (i = 64; i <= length; i += 64) { 603 | md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i))); 604 | } 605 | 606 | this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0); 607 | 608 | return this; 609 | }; 610 | 611 | /** 612 | * Finishes the incremental computation, reseting the internal state and 613 | * returning the result. 614 | * 615 | * @param {Boolean} raw True to get the raw string, false to get the hex string 616 | * 617 | * @return {String} The result 618 | */ 619 | SparkMD5.ArrayBuffer.prototype.end = function (raw) { 620 | var buff = this._buff, 621 | length = buff.length, 622 | tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 623 | i, 624 | ret; 625 | 626 | for (i = 0; i < length; i += 1) { 627 | tail[i >> 2] |= buff[i] << ((i % 4) << 3); 628 | } 629 | 630 | this._finish(tail, length); 631 | ret = hex(this._hash); 632 | 633 | if (raw) { 634 | ret = hexToBinaryString(ret); 635 | } 636 | 637 | this.reset(); 638 | 639 | return ret; 640 | }; 641 | 642 | /** 643 | * Resets the internal state of the computation. 644 | * 645 | * @return {SparkMD5.ArrayBuffer} The instance itself 646 | */ 647 | SparkMD5.ArrayBuffer.prototype.reset = function () { 648 | this._buff = new Uint8Array(0); 649 | this._length = 0; 650 | this._hash = [1732584193, -271733879, -1732584194, 271733878]; 651 | 652 | return this; 653 | }; 654 | 655 | /** 656 | * Gets the internal state of the computation. 657 | * 658 | * @return {Object} The state 659 | */ 660 | SparkMD5.ArrayBuffer.prototype.getState = function () { 661 | var state = SparkMD5.prototype.getState.call(this); 662 | 663 | // Convert buffer to a string 664 | state.buff = arrayBuffer2Utf8Str(state.buff); 665 | 666 | return state; 667 | }; 668 | 669 | /** 670 | * Gets the internal state of the computation. 671 | * 672 | * @param {Object} state The state 673 | * 674 | * @return {SparkMD5.ArrayBuffer} The instance itself 675 | */ 676 | SparkMD5.ArrayBuffer.prototype.setState = function (state) { 677 | // Convert string to buffer 678 | state.buff = utf8Str2ArrayBuffer(state.buff, true); 679 | 680 | return SparkMD5.prototype.setState.call(this, state); 681 | }; 682 | 683 | SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; 684 | 685 | SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; 686 | 687 | /** 688 | * Performs the md5 hash on an array buffer. 689 | * 690 | * @param {ArrayBuffer} arr The array buffer 691 | * @param {Boolean} raw True to get the raw string, false to get the hex one 692 | * 693 | * @return {String} The result 694 | */ 695 | SparkMD5.ArrayBuffer.hash = function (arr, raw) { 696 | var hash = md51_array(new Uint8Array(arr)), 697 | ret = hex(hash); 698 | 699 | return raw ? hexToBinaryString(ret) : ret; 700 | }; 701 | 702 | return SparkMD5; 703 | })); 704 | -------------------------------------------------------------------------------- /node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-uploader-node", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.8", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 10 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 11 | "requires": { 12 | "mime-types": "~2.1.34", 13 | "negotiator": "0.6.3" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 20 | }, 21 | "asap": { 22 | "version": "2.0.6", 23 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 24 | "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" 25 | }, 26 | "body-parser": { 27 | "version": "1.20.2", 28 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", 29 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 30 | "requires": { 31 | "bytes": "3.1.2", 32 | "content-type": "~1.0.5", 33 | "debug": "2.6.9", 34 | "depd": "2.0.0", 35 | "destroy": "1.2.0", 36 | "http-errors": "2.0.0", 37 | "iconv-lite": "0.4.24", 38 | "on-finished": "2.4.1", 39 | "qs": "6.11.0", 40 | "raw-body": "2.5.2", 41 | "type-is": "~1.6.18", 42 | "unpipe": "1.0.0" 43 | } 44 | }, 45 | "bytes": { 46 | "version": "3.1.2", 47 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 48 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 49 | }, 50 | "call-bind": { 51 | "version": "1.0.7", 52 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 53 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 54 | "requires": { 55 | "es-define-property": "^1.0.0", 56 | "es-errors": "^1.3.0", 57 | "function-bind": "^1.1.2", 58 | "get-intrinsic": "^1.2.4", 59 | "set-function-length": "^1.2.1" 60 | } 61 | }, 62 | "content-disposition": { 63 | "version": "0.5.4", 64 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 65 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 66 | "requires": { 67 | "safe-buffer": "5.2.1" 68 | } 69 | }, 70 | "content-type": { 71 | "version": "1.0.5", 72 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 73 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" 74 | }, 75 | "cookie": { 76 | "version": "0.6.0", 77 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", 78 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" 79 | }, 80 | "cookie-signature": { 81 | "version": "1.0.6", 82 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 83 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 84 | }, 85 | "debug": { 86 | "version": "2.6.9", 87 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 88 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 89 | "requires": { 90 | "ms": "2.0.0" 91 | } 92 | }, 93 | "define-data-property": { 94 | "version": "1.1.4", 95 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 96 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 97 | "requires": { 98 | "es-define-property": "^1.0.0", 99 | "es-errors": "^1.3.0", 100 | "gopd": "^1.0.1" 101 | } 102 | }, 103 | "depd": { 104 | "version": "2.0.0", 105 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 106 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 107 | }, 108 | "destroy": { 109 | "version": "1.2.0", 110 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 111 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" 112 | }, 113 | "dezalgo": { 114 | "version": "1.0.4", 115 | "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", 116 | "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", 117 | "requires": { 118 | "asap": "^2.0.0", 119 | "wrappy": "1" 120 | } 121 | }, 122 | "ee-first": { 123 | "version": "1.1.1", 124 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 125 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 126 | }, 127 | "encodeurl": { 128 | "version": "1.0.2", 129 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 130 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" 131 | }, 132 | "es-define-property": { 133 | "version": "1.0.0", 134 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 135 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 136 | "requires": { 137 | "get-intrinsic": "^1.2.4" 138 | } 139 | }, 140 | "es-errors": { 141 | "version": "1.3.0", 142 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 143 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" 144 | }, 145 | "escape-html": { 146 | "version": "1.0.3", 147 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 148 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 149 | }, 150 | "etag": { 151 | "version": "1.8.1", 152 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 153 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" 154 | }, 155 | "express": { 156 | "version": "4.19.2", 157 | "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", 158 | "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", 159 | "requires": { 160 | "accepts": "~1.3.8", 161 | "array-flatten": "1.1.1", 162 | "body-parser": "1.20.2", 163 | "content-disposition": "0.5.4", 164 | "content-type": "~1.0.4", 165 | "cookie": "0.6.0", 166 | "cookie-signature": "1.0.6", 167 | "debug": "2.6.9", 168 | "depd": "2.0.0", 169 | "encodeurl": "~1.0.2", 170 | "escape-html": "~1.0.3", 171 | "etag": "~1.8.1", 172 | "finalhandler": "1.2.0", 173 | "fresh": "0.5.2", 174 | "http-errors": "2.0.0", 175 | "merge-descriptors": "1.0.1", 176 | "methods": "~1.1.2", 177 | "on-finished": "2.4.1", 178 | "parseurl": "~1.3.3", 179 | "path-to-regexp": "0.1.7", 180 | "proxy-addr": "~2.0.7", 181 | "qs": "6.11.0", 182 | "range-parser": "~1.2.1", 183 | "safe-buffer": "5.2.1", 184 | "send": "0.18.0", 185 | "serve-static": "1.15.0", 186 | "setprototypeof": "1.2.0", 187 | "statuses": "2.0.1", 188 | "type-is": "~1.6.18", 189 | "utils-merge": "1.0.1", 190 | "vary": "~1.1.2" 191 | } 192 | }, 193 | "finalhandler": { 194 | "version": "1.2.0", 195 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 196 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 197 | "requires": { 198 | "debug": "2.6.9", 199 | "encodeurl": "~1.0.2", 200 | "escape-html": "~1.0.3", 201 | "on-finished": "2.4.1", 202 | "parseurl": "~1.3.3", 203 | "statuses": "2.0.1", 204 | "unpipe": "~1.0.0" 205 | } 206 | }, 207 | "formidable": { 208 | "version": "3.5.1", 209 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", 210 | "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==", 211 | "requires": { 212 | "dezalgo": "^1.0.4", 213 | "hexoid": "^1.0.0", 214 | "once": "^1.4.0" 215 | } 216 | }, 217 | "forwarded": { 218 | "version": "0.2.0", 219 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 220 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 221 | }, 222 | "fresh": { 223 | "version": "0.5.2", 224 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 225 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 226 | }, 227 | "function-bind": { 228 | "version": "1.1.2", 229 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 230 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" 231 | }, 232 | "get-intrinsic": { 233 | "version": "1.2.4", 234 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 235 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 236 | "requires": { 237 | "es-errors": "^1.3.0", 238 | "function-bind": "^1.1.2", 239 | "has-proto": "^1.0.1", 240 | "has-symbols": "^1.0.3", 241 | "hasown": "^2.0.0" 242 | } 243 | }, 244 | "gopd": { 245 | "version": "1.0.1", 246 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 247 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 248 | "requires": { 249 | "get-intrinsic": "^1.1.3" 250 | } 251 | }, 252 | "has-property-descriptors": { 253 | "version": "1.0.2", 254 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 255 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 256 | "requires": { 257 | "es-define-property": "^1.0.0" 258 | } 259 | }, 260 | "has-proto": { 261 | "version": "1.0.3", 262 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 263 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" 264 | }, 265 | "has-symbols": { 266 | "version": "1.0.3", 267 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 268 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 269 | }, 270 | "hasown": { 271 | "version": "2.0.2", 272 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 273 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 274 | "requires": { 275 | "function-bind": "^1.1.2" 276 | } 277 | }, 278 | "hexoid": { 279 | "version": "1.0.0", 280 | "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", 281 | "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==" 282 | }, 283 | "http-errors": { 284 | "version": "2.0.0", 285 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 286 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 287 | "requires": { 288 | "depd": "2.0.0", 289 | "inherits": "2.0.4", 290 | "setprototypeof": "1.2.0", 291 | "statuses": "2.0.1", 292 | "toidentifier": "1.0.1" 293 | } 294 | }, 295 | "iconv-lite": { 296 | "version": "0.4.24", 297 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 298 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 299 | "requires": { 300 | "safer-buffer": ">= 2.1.2 < 3" 301 | } 302 | }, 303 | "inherits": { 304 | "version": "2.0.4", 305 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 306 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 307 | }, 308 | "ipaddr.js": { 309 | "version": "1.9.1", 310 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 311 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 312 | }, 313 | "media-typer": { 314 | "version": "0.3.0", 315 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 316 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" 317 | }, 318 | "merge-descriptors": { 319 | "version": "1.0.1", 320 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 321 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 322 | }, 323 | "methods": { 324 | "version": "1.1.2", 325 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 326 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" 327 | }, 328 | "mime": { 329 | "version": "1.6.0", 330 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 331 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 332 | }, 333 | "mime-db": { 334 | "version": "1.52.0", 335 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 336 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 337 | }, 338 | "mime-types": { 339 | "version": "2.1.35", 340 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 341 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 342 | "requires": { 343 | "mime-db": "1.52.0" 344 | } 345 | }, 346 | "ms": { 347 | "version": "2.0.0", 348 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 349 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 350 | }, 351 | "negotiator": { 352 | "version": "0.6.3", 353 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 354 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 355 | }, 356 | "object-inspect": { 357 | "version": "1.13.1", 358 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", 359 | "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" 360 | }, 361 | "on-finished": { 362 | "version": "2.4.1", 363 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 364 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 365 | "requires": { 366 | "ee-first": "1.1.1" 367 | } 368 | }, 369 | "once": { 370 | "version": "1.4.0", 371 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 372 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 373 | "requires": { 374 | "wrappy": "1" 375 | } 376 | }, 377 | "parseurl": { 378 | "version": "1.3.3", 379 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 380 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 381 | }, 382 | "path-to-regexp": { 383 | "version": "0.1.7", 384 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 385 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 386 | }, 387 | "proxy-addr": { 388 | "version": "2.0.7", 389 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 390 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 391 | "requires": { 392 | "forwarded": "0.2.0", 393 | "ipaddr.js": "1.9.1" 394 | } 395 | }, 396 | "qs": { 397 | "version": "6.11.0", 398 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 399 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 400 | "requires": { 401 | "side-channel": "^1.0.4" 402 | } 403 | }, 404 | "range-parser": { 405 | "version": "1.2.1", 406 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 407 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 408 | }, 409 | "raw-body": { 410 | "version": "2.5.2", 411 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 412 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 413 | "requires": { 414 | "bytes": "3.1.2", 415 | "http-errors": "2.0.0", 416 | "iconv-lite": "0.4.24", 417 | "unpipe": "1.0.0" 418 | } 419 | }, 420 | "safe-buffer": { 421 | "version": "5.2.1", 422 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 423 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 424 | }, 425 | "safer-buffer": { 426 | "version": "2.1.2", 427 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 428 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 429 | }, 430 | "send": { 431 | "version": "0.18.0", 432 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 433 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 434 | "requires": { 435 | "debug": "2.6.9", 436 | "depd": "2.0.0", 437 | "destroy": "1.2.0", 438 | "encodeurl": "~1.0.2", 439 | "escape-html": "~1.0.3", 440 | "etag": "~1.8.1", 441 | "fresh": "0.5.2", 442 | "http-errors": "2.0.0", 443 | "mime": "1.6.0", 444 | "ms": "2.1.3", 445 | "on-finished": "2.4.1", 446 | "range-parser": "~1.2.1", 447 | "statuses": "2.0.1" 448 | }, 449 | "dependencies": { 450 | "ms": { 451 | "version": "2.1.3", 452 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 453 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 454 | } 455 | } 456 | }, 457 | "serve-static": { 458 | "version": "1.15.0", 459 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 460 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 461 | "requires": { 462 | "encodeurl": "~1.0.2", 463 | "escape-html": "~1.0.3", 464 | "parseurl": "~1.3.3", 465 | "send": "0.18.0" 466 | } 467 | }, 468 | "set-function-length": { 469 | "version": "1.2.2", 470 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 471 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 472 | "requires": { 473 | "define-data-property": "^1.1.4", 474 | "es-errors": "^1.3.0", 475 | "function-bind": "^1.1.2", 476 | "get-intrinsic": "^1.2.4", 477 | "gopd": "^1.0.1", 478 | "has-property-descriptors": "^1.0.2" 479 | } 480 | }, 481 | "setprototypeof": { 482 | "version": "1.2.0", 483 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 484 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 485 | }, 486 | "side-channel": { 487 | "version": "1.0.6", 488 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 489 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 490 | "requires": { 491 | "call-bind": "^1.0.7", 492 | "es-errors": "^1.3.0", 493 | "get-intrinsic": "^1.2.4", 494 | "object-inspect": "^1.13.1" 495 | } 496 | }, 497 | "statuses": { 498 | "version": "2.0.1", 499 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 500 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 501 | }, 502 | "toidentifier": { 503 | "version": "1.0.1", 504 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 505 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 506 | }, 507 | "type-is": { 508 | "version": "1.6.18", 509 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 510 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 511 | "requires": { 512 | "media-typer": "0.3.0", 513 | "mime-types": "~2.1.24" 514 | } 515 | }, 516 | "unpipe": { 517 | "version": "1.0.0", 518 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 519 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" 520 | }, 521 | "utils-merge": { 522 | "version": "1.0.1", 523 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 524 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" 525 | }, 526 | "vary": { 527 | "version": "1.1.2", 528 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 529 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" 530 | }, 531 | "wrappy": { 532 | "version": "1.0.2", 533 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 534 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 535 | } 536 | } 537 | } 538 | --------------------------------------------------------------------------------