├── LICENSE ├── Q.Uploader.file.all.js ├── Q.Uploader.image.all.js ├── README.md ├── api └── upload.ashx ├── build ├── build.bat └── build.data.js ├── css └── uploader.all.css ├── demo ├── Q.tabs.js ├── bg-tab.png ├── custom.html ├── default.html ├── demo.css ├── demo.js ├── drag-drop.html ├── fixed-view.html ├── folder.html ├── image-and-file.html ├── image-single.html ├── image.html ├── jquery-1.11.1.js ├── scroll-view.html ├── simple-filetype.html ├── simple-manual.html ├── simple-single.html ├── simple.html ├── slice.html ├── tabs.html └── targets.html ├── dist ├── Q.Uploader.file.all.js ├── Q.Uploader.image.all.js └── js │ ├── Q.Uploader.UI.File.js │ ├── Q.Uploader.UI.Image.js │ ├── Q.Uploader.js │ ├── Q.Uploader.slice.js │ ├── Q.js │ ├── Q.md5File.js │ └── spark-md5.js ├── images └── Q │ └── progress.gif ├── index.html ├── js ├── Q.Uploader.UI.File.js ├── Q.Uploader.UI.Image.js ├── Q.Uploader.js ├── Q.Uploader.slice.js ├── Q.js ├── Q.md5File.js └── spark-md5.js └── web.config /LICENSE: -------------------------------------------------------------------------------- 1 | 木兰宽松许可证, 第2版 2 | 3 | 木兰宽松许可证, 第2版 4 | 2020年1月 http://license.coscl.org.cn/MulanPSL2 5 | 6 | 7 | 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 8 | 9 | 0. 定义 10 | 11 | “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 12 | 13 | “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 14 | 15 | “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 16 | 17 | “法人实体”是指提交贡献的机构及其“关联实体”。 18 | 19 | “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 20 | 21 | 1. 授予版权许可 22 | 23 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 24 | 25 | 2. 授予专利许可 26 | 27 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 28 | 29 | 3. 无商标许可 30 | 31 | “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 32 | 33 | 4. 分发限制 34 | 35 | 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 36 | 37 | 5. 免责声明与责任限制 38 | 39 | “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 40 | 41 | 6. 语言 42 | “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 43 | 44 | 条款结束 45 | 46 | 如何将木兰宽松许可证,第2版,应用到您的软件 47 | 48 | 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 49 | 50 | 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 51 | 52 | 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 53 | 54 | 3, 请将如下声明文本放入每个源文件的头部注释中。 55 | 56 | Copyright (c) [Year] [name of copyright holder] 57 | [Software Name] is licensed under Mulan PSL v2. 58 | You can use this software according to the terms and conditions of the Mulan PSL v2. 59 | You may obtain a copy of Mulan PSL v2 at: 60 | http://license.coscl.org.cn/MulanPSL2 61 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 62 | See the Mulan PSL v2 for more details. 63 | 64 | 65 | Mulan Permissive Software License,Version 2 66 | 67 | Mulan Permissive Software License,Version 2 (Mulan PSL v2) 68 | January 2020 http://license.coscl.org.cn/MulanPSL2 69 | 70 | Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 71 | 72 | 0. Definition 73 | 74 | Software means the program and related documents which are licensed under this License and comprise all Contribution(s). 75 | 76 | Contribution means the copyrightable work licensed by a particular Contributor under this License. 77 | 78 | Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. 79 | 80 | Legal Entity means the entity making a Contribution and all its Affiliates. 81 | 82 | Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 83 | 84 | 1. Grant of Copyright License 85 | 86 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 87 | 88 | 2. Grant of Patent License 89 | 90 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 91 | 92 | 3. No Trademark License 93 | 94 | No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. 95 | 96 | 4. Distribution Restriction 97 | 98 | You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 99 | 100 | 5. Disclaimer of Warranty and Limitation of Liability 101 | 102 | THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 103 | 104 | 6. Language 105 | 106 | THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. 107 | 108 | END OF THE TERMS AND CONDITIONS 109 | 110 | How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software 111 | 112 | To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: 113 | 114 | i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; 115 | 116 | ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; 117 | 118 | iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. 119 | 120 | 121 | Copyright (c) [Year] [name of copyright holder] 122 | [Software Name] is licensed under Mulan PSL v2. 123 | You can use this software according to the terms and conditions of the Mulan PSL v2. 124 | You may obtain a copy of Mulan PSL v2 at: 125 | http://license.coscl.org.cn/MulanPSL2 126 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 127 | See the Mulan PSL v2 for more details. 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | web-uploader 2 | ============ 3 | 4 | js (html5 + html4) 文件上传管理器,支持上传进度显示,支持秒传+分片上传+断点续传,支持图片预览+缩放,支持 IE6+、Firefox、Chrome等。 5 | 6 | [博客园详细介绍](http://www.cnblogs.com/devin87/p/web-uploader.html) 7 | 8 | [图片上传介绍](http://www.cnblogs.com/devin87/p/web-uploader-image.html) 9 | 10 | ### 特点: 11 | 20 | 21 | ### 演示环境(其它语言可自行实现服务端接收和网站部署) 22 | 演示前请在根目录下创建upload文件夹,以保存上传文件
23 | 执行程序:api/upload.ashx 24 | ``` 25 | asp.net 4.0+ 26 | iis 27 | ``` 28 | 29 | Node.js 演示见[web-uploader-node](https://github.com/devin87/web-uploader-node) 30 | 31 | ### 简单调用示例 32 | 例:一般文件上传,使用默认的UI 33 | ``` 34 | 1. 导入样式文件(若自己实现UI接口,则无需导入默认的样式文件) 35 | 36 | 37 | 2. 导入js文件(可自行合并,若自己实现UI接口,则无需导入 Q.Uploader.UI.File.js 文件) 38 | 39 | 40 | 41 | 42 | 或 43 | 44 | 45 | 3. 调用 46 | new Q.Uploader({ 47 | url:"api/upload.ashx", 48 | 49 | target: element, //上传按钮,可为数组 eg:[element1,element2] 50 | view: element //上传任务视图(若自己实现UI接口,则无需指定此参数) 51 | }); 52 | ``` 53 | 54 | 例:图片上传+预览+缩放 55 | ``` 56 | 1. 导入样式文件(若自己实现UI接口,则无需导入默认的样式文件) 57 | 58 | 59 | 2. 导入js文件(可自行合并) 60 | 61 | 62 | 63 | 64 | 或 65 | 66 | 67 | 3. 调用 68 | new Q.Uploader({ 69 | url:"api/upload.ashx", 70 | 71 | target: element, //上传按钮,可为数组 eg:[element1,element2] 72 | view: element, //上传任务视图(若自己实现UI接口,则无需指定此参数) 73 | 74 | allows: ".jpg,.png,.gif,.bmp", 75 | 76 | //图片缩放 77 | scale: { 78 | //要缩放的图片格式 79 | types: ".jpg", 80 | //最大图片宽度(maxWidth)或高度(maxHeight) 81 | maxWidth: 1024 82 | } 83 | }); 84 | ``` 85 | 86 | ### 完整调用示例 87 | ```javascript 88 | new Q.Uploader({ 89 | //--------------- 必填 --------------- 90 | url: "", //上传路径 91 | target: element, //上传按钮,可为数组 eg:[element1,element2] 92 | view: element, //上传任务视图(若自己实现UI接口,则无需指定此参数) 93 | 94 | //--------------- 可选 --------------- 95 | html5: true, //是否启用html5上传,若浏览器不支持,则自动禁用 96 | multiple: true, //选择文件时是否允许多选,若浏览器不支持,则自动禁用(仅html5模式有效) 97 | 98 | clickTrigger:true, //是否启用click触发文件选择 eg: input.click() => IE9及以下不支持 99 | 100 | auto: true, //添加任务后是否立即上传 101 | 102 | headers:{}, //ajax 消息头(仅html5模式有效) 103 | data: {}, //上传文件的同时可以指定其它参数,该参数将以POST的方式提交到服务器 104 | 105 | dataType: "json", //服务器返回值类型 106 | 107 | workerThread: 1, //同时允许上传的任务数(仅html5模式有效) 108 | 109 | upName: "upfile", //上传参数名称,若后台需要根据name来获取上传数据,可配置此项 110 | accept: "", //指定浏览器接受的文件类型 eg:image/*,video/* => IE9及以下不支持 111 | isDir: false, //是否是文件夹上传(仅Webkit内核浏览器和新版火狐有效) 112 | 113 | allows: "", //允许上传的文件类型(扩展名),多个之间用逗号隔开 114 | disallows: "", //禁止上传的文件类型(扩展名) 115 | 116 | maxSize: 0, //允许上传的最大文件大小,字节,为0表示不限(仅对支持的浏览器生效,eg: IE10+、Firefox、Chrome) 117 | 118 | //秒传+分片上传+断点续传,具体见示例(demo/slice.html) 119 | urlForQuery: "", //查询接口,为空时默认为上传 url 120 | isSlice: false, //是否启用分片上传,若为true,则isQueryState和isMd5默认为true 121 | chunkSize: 2 * 1024 * 1024, //默认分片大小为2MB 122 | //查询路径为: urlForQuery?action=query&hash=&fileName=&fileSize= 123 | isQueryState: false, //是否查询文件状态(for 秒传或续传) 124 | isMd5: false, //是否计算上传文件md5值作为文件hash,需存在 Q.md5File(file, callback, progress) 125 | isUploadAfterHash: true, //是否在Hash计算完毕后再上传 126 | sliceRetryCount: 2, //分片上传失败重试次数 127 | 128 | container: document.body, //一般无需指定 129 | getPos: function(){ }, //一般无需指定 130 | 131 | //回调事件(function), 回调事件(add、upload、hash、sliceUpload、send)支持异步调用,只需在后面加上Async即可,使用方法参考下面示例 132 | on: { 133 | init: function(){ }, //上传管理器初始化完毕后触发 134 | 135 | select: function(task){ }, //点击上传按钮准备选择上传文件之前触发,返回false可禁止选择文件 136 | add: function(task){ }, //添加任务之前触发,返回false将跳过该任务 137 | upload: function(task){ }, //上传任务之前触发,返回false将跳过该任务 138 | hashProgress: function(task){ }, //文件hash进度(仅isMd5为true时有效) 139 | hash: function(task){ }, //hash计算,查询之前触发(for 秒传或续传) 140 | beforeQuery: function(task){ }, //查询之前触发(for 秒传或续传) 141 | query: function(task){ }, //查询到结果后触发(for 秒传或续传),返回false表示上传失败,返回-1表示秒传成功,返回其它数字表示上传起始点 142 | sliceUpload: function(task){ }, //分片上传之前触发,返回false将跳过该分片 143 | send: function(task){ }, //发送数据之前触发,返回false将跳过该任务 144 | 145 | cancel: function(task){ }, //取消上传任务后触发 146 | remove: function(task){ }, //移除上传任务后触发 147 | 148 | progress: function(task){ }, //上传进度发生变化后触发(仅html5模式有效) 149 | uploadResponse: function(task){ },//上传完成后获取到服务器响应后触发 150 | complete: function(task){ } //上传完成后触发 151 | }, 152 | 153 | //UI接口(function),若指定了以下方法,将忽略默认实现 154 | UI:{ 155 | init: function(){ }, //执行初始化操作 156 | draw: function(task){ }, //添加任务后绘制任务界面 157 | update: function(task){ }, //更新任务界面 158 | over: function(){ } //任务上传完成 159 | } 160 | }); 161 | 162 | //全局设置 163 | Q.Uploader.setup({ 164 | headers: { }, //ajax 消息头 165 | data: { }, //附加参数 166 | //全局事件, 事件(add、upload、hash、sliceUpload、send)支持异步调用,只需在后面加上Async即可,使用方法参考下面示例 167 | on: { 168 | init: function(){ }, //上传管理器初始化完毕后触发 169 | 170 | select: function(task){ }, //点击上传按钮准备选择上传文件之前触发,返回false可禁止选择文件 171 | add: function(task){ }, //添加任务之前触发,返回false将跳过该任务 172 | upload: function(task){ }, //上传任务之前触发,返回false将跳过该任务 173 | hashProgress: function(task){ }, //文件hash进度(仅isMd5为true时有效) 174 | hash: function(task){ }, //hash计算,查询之前触发(for 秒传或续传) 175 | beforeQuery: function(task){ }, //查询之前触发(for 秒传或续传) 176 | query: function(task){ }, //查询到结果后触发(for 秒传或续传),返回false表示上传失败,返回-1表示秒传成功,返回其它数字表示上传起始点 177 | sliceUpload: function(task){ }, //分片上传之前触发,返回false将跳过该分片 178 | send: function(task){ }, //发送数据之前触发,返回false将跳过该任务 179 | 180 | cancel: function(task){ }, //取消上传任务后触发 181 | remove: function(task){ }, //移除上传任务后触发 182 | 183 | progress: function(task){ }, //上传进度发生变化后触发(仅html5模式有效) 184 | uploadResponse: function(task){ },//上传完成后获取到服务器响应后触发 185 | complete: function(task){ } //上传完成后触发 186 | } 187 | }); 188 | ``` 189 | 190 | task属性说明 191 | ``` 192 | task = { 193 | id, //任务编号 194 | 195 | name, //上传文件名(包括扩展名) 196 | ext, //上传文件扩展名 197 | size, //上传文件大小(单位:Byte,若获取不到大小,则值为-1) 198 | 199 | input, //上传控件 200 | file, //上传数据(仅 html5) 201 | 202 | state, //上传状态 203 | 204 | limited, //若存在值,表示禁止上传的文件类型 205 | skip, //若为true,表示要跳过的任务 206 | 207 | //分片上传 208 | sliceCount, //分片总数 209 | sliceIndex, //当前分片数 210 | sliceStart, //当前分片上传的起始点 211 | sliceEnd, //当前分片上传的结束点 212 | sliceBlob, //当前分片数据 213 | 214 | //上传后会有如下属性(由于浏览器支持问题,以下部分属性可能不存在) 215 | xhr, //XMLHttpRequest对象(仅 html5) 216 | 217 | total, //总上传大小(单位:Byte) 218 | loaded, //已上传大小(单位:Byte) 219 | speed, //上传速度(单位:Byte/s) 220 | 221 | avgSpeed, //平均上传速度(仅上传完毕) 222 | 223 | startTime, //开始上传的时间 224 | endTime, //结束上传的时间(仅上传完毕) 225 | 226 | timeHash, //文件hash所用时间(毫秒,仅当isMd5为true) 227 | time, //上传所用时间(毫秒) 228 | 229 | deleted, //若为true,表示已删除的文件 230 | 231 | //文件成功上传 232 | queryOK, //仅秒传成功时为true 233 | response, //服务器返回的字符串 234 | json //response解析后的JSON对象(仅当 dataType 为json) 235 | }; 236 | ``` 237 | 238 | 回调事件示例 239 | ```javascript 240 | on: { 241 | add: function(task) { 242 | //task.limited存在值的任务不会上传,此处无需返回false 243 | switch(task.limited){ 244 | case 'ext':return alert("允许上传的文件格式为:" + this.ops.allows); 245 | case 'size':return alert("允许上传的最大文件大小为:" + Q.formatSize(this.ops.maxSize)); 246 | } 247 | 248 | //自定义判断,返回false时该文件不会添加到上传队列 249 | //return false; 250 | }, 251 | upload: function(task) { 252 | //可以针对单个task指定上传参数,该参数将以POST的方式提交到服务器 253 | task.data = {}; 254 | }, 255 | complete: function (task) { 256 | var json = task.json; 257 | if (!json) return alert("上传已完成,但无法获取服务器返回数据!"); 258 | if (json.ret != 1) return alert(json.msg || "上传失败!"); 259 | 260 | alert("上传成功!"); 261 | } 262 | } 263 | ``` 264 | 265 | 说明:回调事件(add、upload、hash、sliceUpload、send)支持异步调用,只需在后面加上Async即可,比如在上传之前需要访问服务器验证数据,通过的就上传,否则跳过 266 | ```javascript 267 | on: { 268 | uploadAsync: function (task, callback) { 269 | $.postJSON(url, function (json) { 270 | //若 json.ok 返回false,该任务不会上传 271 | callback(json.ok); 272 | }); 273 | }, 274 | //计算文件hash 275 | hashAsync: function(task, callback){ 276 | Q.md5File(task.file, function(md5){ 277 | //task.hash:秒传或断点续传唯一标识 278 | task.hash = md5; 279 | callback(); 280 | }); 281 | }, 282 | //分片上传之前触发 283 | sliceUploadAsync: function (task, callback) { 284 | log(task.name + ": 上传分片 " + task.sliceIndex + " / " + task.sliceCount); 285 | 286 | callback(); 287 | } 288 | } 289 | ``` 290 | 291 | ### 手动操作(api) 292 | ``` 293 | var uploader = new Q.Uploader(ops); 294 | 295 | //上传任务列表,数组 296 | uploader.list 297 | 298 | //当前上传任务索引 => var task = uploader.list[uploader.index]; 299 | uploader.index 300 | 301 | //更改上传配置 302 | uploader.set(settings); 303 | 304 | //添加上传任务,支持文件多选、input元素和文件对象 => input.files | input | file 305 | uploader.add(input_or_file); 306 | 307 | //批量添加上传任务 list => [input_or_file] 308 | uploader.addList(list); 309 | 310 | //手动开始上传(默认自动上传,实例化时可配置 ops.auto=false) 311 | uploader.start(); 312 | 313 | //上传一个任务 314 | uploader.upload(task); 315 | 316 | //取消上传任务 317 | //onlyCancel: 若为true,则仅取消上传而不触发任务完成事件 318 | uploader.cancel(taskId, onlyCancel); 319 | 320 | //移除上传任务,会先调用 uploader.cancel(taskId) 321 | uploader.remove(taskId); 322 | 323 | //更新上传进度 324 | //total : 总上传数据(byte) 325 | //loaded : 已上传数据(byte) 326 | uploader.progress(task, total, loaded); 327 | 328 | ``` 329 | 330 | ### 自定义UI实现 331 | 可以在初始化时指定UI处理函数,亦可以通过扩展的方式实现 332 | ``` 333 | Uploader.extend({ 334 | //初始化操作,一般无需处理 335 | init: function () { }, 336 | 337 | //绘制任务界面 338 | draw: function (task) { }, 339 | 340 | //更新上传进度 341 | update: function (task) { }, 342 | 343 | //上传完毕,一般无需处理 344 | over: function () { } 345 | }); 346 | ``` -------------------------------------------------------------------------------- /api/upload.ashx: -------------------------------------------------------------------------------- 1 | <%@ WebHandler Language="C#" Class="upload" %> 2 | 3 | using System; 4 | using System.Web; 5 | using System.IO; 6 | 7 | public class upload : IHttpHandler 8 | { 9 | public void ProcessRequest(HttpContext context) 10 | { 11 | HttpRequest request = context.Request; 12 | 13 | string action = request["action"]; 14 | string hash = request["hash"]; 15 | 16 | int fileCount = request.Files.Count; 17 | 18 | if (string.IsNullOrEmpty(hash)) 19 | { 20 | //普通上传 21 | if (fileCount > 0) 22 | { 23 | HttpPostedFile file = request.Files[0]; 24 | 25 | string fileName = request["fileName"]; 26 | if (string.IsNullOrEmpty(fileName)) fileName = file.FileName; 27 | 28 | string dir = Path.GetDirectoryName(fileName); 29 | if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(context.Server.MapPath("~/upload/" + dir)); 30 | 31 | string path = context.Server.MapPath("~/upload/" + fileName); 32 | file.SaveAs(path); 33 | } 34 | } 35 | else 36 | { 37 | //秒传或断点续传 38 | string path = context.Server.MapPath("~/upload/" + hash); 39 | string path_ok = path + ".ok"; 40 | 41 | //状态查询 42 | if (action == "query") 43 | { 44 | if (File.Exists(path_ok)) Finish(GetResponseJSON(request, ",\"ret\":1")); 45 | else if (File.Exists(path)) Finish(new FileInfo(path).Length.ToString()); 46 | else Finish("0"); 47 | } 48 | else 49 | { 50 | if (fileCount > 0) 51 | { 52 | HttpPostedFile file = request.Files[0]; 53 | using (FileStream fs = File.Open(path, FileMode.Append)) 54 | { 55 | byte[] buffer = new byte[file.ContentLength]; 56 | file.InputStream.Read(buffer, 0, file.ContentLength); 57 | 58 | fs.Write(buffer, 0, buffer.Length); 59 | } 60 | } 61 | 62 | bool isOk = request["ok"] == "1"; 63 | if (!isOk) Finish("1"); 64 | 65 | if (File.Exists(path)) File.Move(path, path_ok); 66 | } 67 | } 68 | 69 | Finish(GetResponseJSON(request, "")); 70 | } 71 | 72 | /// 73 | /// 获取返回的json字符串 74 | /// 75 | /// 76 | /// 77 | private string GetResponseJSON(HttpRequest request, string result) 78 | { 79 | string type = request["type"]; 80 | string user = request["user"]; 81 | string name = request["name"]; 82 | 83 | //此处返回的JSON字符串为手动拼接,未处理字符串转义等特殊情况,仅作演示 84 | string json = "\"time\":\"" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\""; 85 | if (type != null) json += ",\"type\":\"" + type + "\""; 86 | if (user != null) json += ",\"user\":\"" + user + "\""; 87 | if (name != null) json += ",\"name\":\"" + name + "\""; 88 | 89 | return "{" + json + result + "}"; 90 | } 91 | 92 | /// 93 | /// 完成上传 94 | /// 95 | /// 回调函数参数 96 | private void Finish(string json) 97 | { 98 | HttpResponse Response = HttpContext.Current.Response; 99 | 100 | Response.Write(json); 101 | Response.End(); 102 | } 103 | 104 | public bool IsReusable 105 | { 106 | get 107 | { 108 | return false; 109 | } 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /build/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | node ../../Qbuild/build.js 3 | pause -------------------------------------------------------------------------------- /build/build.data.js: -------------------------------------------------------------------------------- 1 | //build 配置文件 2 | module.exports = { 3 | root: "../", 4 | 5 | noStore: true, 6 | 7 | concat: { 8 | title: "文件合并", 9 | 10 | dir: "js", 11 | 12 | list: [ 13 | { 14 | src: ["Q.js", "Q.Uploader.js", "Q.Uploader.slice.js", "Q.Uploader.UI.File.js"], 15 | dest: "/Q.Uploader.file.all.js" 16 | }, 17 | { 18 | src: ["Q.js", "Q.Uploader.js", "Q.Uploader.UI.Image.js"], 19 | dest: "/Q.Uploader.image.all.js" 20 | } 21 | ], 22 | 23 | replace: [ 24 | //移除\r字符 25 | [/\r/g, ""], 26 | //移除VS引用 27 | [/\/\/\/\s*\n/gi, ""] 28 | ] 29 | }, 30 | 31 | cmd: [ 32 | { 33 | title: "压缩js", 34 | 35 | //cmd: "java -jar D:\\tools\\compiler.jar --js=%f.fullname% --js_output_file=%f.dest%", 36 | cmd: "uglifyjs %f.fullname% -o %f.dest% -c -m", 37 | 38 | output: "dist", 39 | match: ["*.js", "js/*.js"], 40 | 41 | replace: [ 42 | //去掉文件头部压缩工具可能保留的注释 43 | [/^\/\*([^~]|~)*?\*\//, ""] 44 | ], 45 | 46 | //可针对单一的文件配置 before、after,def 表示默认 47 | before: [{ 48 | "def": "//devin87@qq.com\n//build:%NOW%\n", 49 | "spark-md5.js": "//spark-md5 v2.0.2 https://github.com/satazor/js-spark-md5\n//build:%NOW%\n" 50 | }] 51 | } 52 | ], 53 | 54 | run: ["concat", "cmd"] 55 | }; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | })(); -------------------------------------------------------------------------------- /demo/bg-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devin87/web-uploader/c9a4fcd77abf72b925db8ef6d3f3e0c68715f226/demo/bg-tab.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 || "../api/upload.ashx"; 74 | 75 | })(); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /demo/image-single.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 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 108 | 109 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /demo/simple.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 | 37 | 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dist/Q.Uploader.file.all.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2022/01/07 15:00:19 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=l(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(c,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",c,"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 n=t.getPos||f,r=t.boxInput,i=l(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(t,n,r){var i=this,e=i.fns||{},a=S.on||{};if(!r){var s=d(e[t],i,n),o=d(a[t],i,n);return s!=c?s:o}function l(e){if(s=e,e=a[t+"Async"])return d(e,i,n,u);u(d(a[t],i,n))}var u=function(e){r(s!=c?s:e)},o=e[t+"Async"];if(o)return d(o,i,n,l);l(d(e[t],i,n))},run:function(e,t,n){e=this[e];return e&&d(e,this,t),n&&this.fire(n,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;var r=e.state||0;0<(n=t<(n=!n||n<0?0:n)?t:n)&&0==r&&(e.state=r=1),function(e,t,n){if(t&&!(t<=0)){var r,i=Date.now();if(t<=n)return(r=i-e.startTime)?e.avgSpeed=Math.min(Math.round(1e3*t/r),t):e.speed||(e.avgSpeed=e.speed=t),e.time=r||0,e.endTime=i;(r=i-e.lastTime)<200||(t=e.lastLoaded||0,e.speed=Math.min(Math.round(1e3*(n-t)/r),e.total),e.lastTime=i,e.lastLoaded=n)}}(e,t=2==r?n=e.size:t,n),e.total=t,e.loaded=n,this.run("update",e,"progress")},_process_response:function(e,t){(e.response=t)&&"json"==this.dataType&&(e.json=o(t))},complete:function(e,t,n){var r=this;return(e=!e&&1==r.workerThread?r._lastTask:e)&&(n!==c&&(r._process_response(e,n),!1===r.fire("uploadResponse",e)&&(t=-3)),e.message||(n=e.json)&&(e.message=n.errMsg||n.msg||n.result||n.Result||n.message),t!=c&&(e.state=t),1!=e.state&&2!=t||(e.state=2,r.progress(e,e.size,e.size))),r.run("update",e,"update").run("over",e,"over"),-2==t&&r.fire("cancel",e),r.fire("complete",e),r.workerIdle++,r.started&&r.start(),r}},k.extend=function(e,t){i(k.prototype,e,t)},(t=e.XMLHttpRequest)&&(new t).upload&&e.FormData&&(y=!0),(t=document.createElement("input")).type="file",_=!!t.files,i(k,{support:{html5:x=y,multiple:_},READY:0,PROCESSING:1,COMPLETE:2,SKIP:-1,CANCEL:-2,ERROR:-3,UI:{},Lang:{status_ready:"准备中",status_processing:"上传中",status_complete:"已完成",status_skip:"已跳过",status_cancel:"已取消",status_error:"已失败",upload_error:"服务器或网络错误"},setup:function(e){i(S,e,!0)},getStatusText:function(e){var t=k.Lang;switch(e){case 0:return t.status_ready;case 1:return t.status_processing;case 2:return t.status_complete;case-1:return t.status_skip;case-2:return t.status_cancel;case-3:return t.status_error}return e}}),Q.Uploader=k}(window),function(e){"use strict";var i,h=Q.Uploader;h.support.html5&&(i=e.File?File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSlice:void 0,h.extend({_upload_slice:function(o){var l,u=this,e=o.blob||o.file,c=e.size,t=u.chunkSize,d=o.sliceStart||0,p=u.sliceRetryCount,f=function(e,t,n){n=+n||0;var r=new XMLHttpRequest,i=o.url,a=l==c;o.xhr=r,i+=(-1==i.indexOf("?")?"?":"&")+"action=upload&hash="+(o.hash||o.name)+"&ok="+(a?"1":"0"),r.upload.addEventListener("progress",function(e){u.progress(o,c,d+e.loaded)},!1),r.addEventListener("load",function(e){e=e.target.responseText;return a?u.complete(o,h.COMPLETE,e):1==e||'"1"'==e?t():void u.complete(o,h.ERROR,e)},!1),r.addEventListener("error",function(){return++n>p?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);var n=function(){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)}))},r=function(){d=l,n()};n(),u._afterSend(o)}}))}(window),function(){"use strict";var l=Q.def,c=Q.getFirst,u=Q.getLast,d=Q.getNext,p=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,"删除"),t=e.name,n=e.id,(r=p("div","u-item",'
'+t+'
')).taskId=n,e.box=r,o.appendChild(r),i=u(r.childNodes[1]),s=c(i),i=u(i),h(s,"click",function(){a.cancel(n)}),h(i,"click",function(){a.remove(n),o.removeChild(r)}),a.update(e))},update:function(e){var t,n,r,i,a,s,o,l,u;e&&e.box&&(t=e.total||e.size,n=e.loaded,r=e.state,o=e.box.childNodes[1],i=c(o),a=d(i),u=d(a),s=c(u),o=d(u),g(u=d(o),m.getStatusText(r)),u.title=e.message||"",t<0||(u="",this.html5&&null!=n&&0<=n&&(r==m.PROCESSING?"100.0"==(l=Math.min(100*n/t,100).toFixed(1))&&(l="99.9"):r==m.COMPLETE&&(l="100"),l&&g(o,s.style.width=l+="%"),u=''+f(n)+" / ",e=e.avgSpeed||e.speed,g(a,f(e)+"/s")),g(i,u+=''+f(t)+"")))},over:function(e){e&&e.box&&t(e.box,"u-over")}},m.extend(m.UI.File)}(window); -------------------------------------------------------------------------------- /dist/Q.Uploader.image.all.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2022/01/07 15:00:20 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=u(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(c,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",c,"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 r=t.getPos||p,n=t.boxInput,i=u(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(t,r,n){var i=this,e=i.fns||{},a=E.on||{};if(!n){var s=d(e[t],i,r),o=d(a[t],i,r);return s!=c?s:o}function u(e){if(s=e,e=a[t+"Async"])return d(e,i,r,l);l(d(a[t],i,r))}var l=function(e){n(s!=c?s:e)},o=e[t+"Async"];if(o)return d(o,i,r,u);u(d(e[t],i,r))},run:function(e,t,r){e=this[e];return e&&d(e,this,t),r&&this.fire(r,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;var n=e.state||0;0<(r=t<(r=!r||r<0?0:r)?t:r)&&0==n&&(e.state=n=1),function(e,t,r){if(t&&!(t<=0)){var n,i=Date.now();if(t<=r)return(n=i-e.startTime)?e.avgSpeed=Math.min(Math.round(1e3*t/n),t):e.speed||(e.avgSpeed=e.speed=t),e.time=n||0,e.endTime=i;(n=i-e.lastTime)<200||(t=e.lastLoaded||0,e.speed=Math.min(Math.round(1e3*(r-t)/n),e.total),e.lastTime=i,e.lastLoaded=r)}}(e,t=2==n?r=e.size:t,r),e.total=t,e.loaded=r,this.run("update",e,"progress")},_process_response:function(e,t){(e.response=t)&&"json"==this.dataType&&(e.json=o(t))},complete:function(e,t,r){var n=this;return(e=!e&&1==n.workerThread?n._lastTask:e)&&(r!==c&&(n._process_response(e,r),!1===n.fire("uploadResponse",e)&&(t=-3)),e.message||(r=e.json)&&(e.message=r.errMsg||r.msg||r.result||r.Result||r.message),t!=c&&(e.state=t),1!=e.state&&2!=t||(e.state=2,n.progress(e,e.size,e.size))),n.run("update",e,"update").run("over",e,"over"),-2==t&&n.fire("cancel",e),n.fire("complete",e),n.workerIdle++,n.started&&n.start(),n}},k.extend=function(e,t){i(k.prototype,e,t)},(t=e.XMLHttpRequest)&&(new t).upload&&e.FormData&&(y=!0),(t=document.createElement("input")).type="file",w=!!t.files,i(k,{support:{html5:b=y,multiple:w},READY:0,PROCESSING:1,COMPLETE:2,SKIP:-1,CANCEL:-2,ERROR:-3,UI:{},Lang:{status_ready:"准备中",status_processing:"上传中",status_complete:"已完成",status_skip:"已跳过",status_cancel:"已取消",status_error:"已失败",upload_error:"服务器或网络错误"},setup:function(e){i(E,e,!0)},getStatusText:function(e){var t=k.Lang;switch(e){case 0:return t.status_ready;case 1:return t.status_processing;case 2:return t.status_complete;case-1:return t.status_skip;case-2:return t.status_cancel;case-3:return t.status_error}return e}}),Q.Uploader=k}(window),function(i){"use strict";var p=Q.getFirst,h=Q.getNext,m=Q.createEle,v=Q.setOpacity,a=Q.ie,u=Q.Uploader;function r(e,t){e.className+=" "+t}function s(t,e,r){var n=e.input,e=e.file||(n.files?n.files[0]:void 0);if(e)!function(e,t){var r=i.URL||i.webkitURL;if(r)return t(r.createObjectURL(e));i.FileReader?((r=new FileReader).onload=function(e){t(e.target.result)},r.readAsDataURL(e)):e.readAsDataURL&&t(e.readAsDataURL())}(e,function(e){e&&(t.innerHTML=''),r&&r(e)});else if(n){e=n.value;if(e&&!/^\w:\\fakepath/.test(e)||(n.select(),parent.document.body.focus(),document.selection&&(e=document.selection.createRange().text)),e){t.innerHTML='';try{6
'+l+'
X
')).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,o;e&&e.box&&(t=e.total||e.size,r=e.loaded,n=e.state,i=e.boxProgress,a=e.boxDetail,s=u.getStatusText(n),this.html5&&null!=r&&0<=r&&(n==u.PROCESSING?"100.0"==(o=Math.min(100*r/t,100).toFixed(1))&&(o="99.9"):n==u.COMPLETE&&(o="100"),o&&(s+=" "+(o+="%"),i.style.width=o)),o=s,(s=a)&&(s.innerHTML=o||""),a.title=e.message||"")},over:function(e){e&&e.box&&r(e.box,"u-over")}},u.extend(u.UI.Image)}(window); -------------------------------------------------------------------------------- /dist/js/Q.Uploader.UI.File.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2022/01/07 10:47:04 3 | !function(){"use strict";var n=Q.def,v=Q.getFirst,o=Q.getLast,u=Q.getNext,r=Q.createEle,p=Q.formatSize,m=Q.event.add,h=Q.Uploader,f=h.Lang;function i(e,i){e.className+=" "+i}function g(e,i){e&&(e.innerHTML=i||"")}h.UI.File={init:function(){var e=this.ops.view;e&&i(e,"ui-file "+(this.html5?"html5":"html4"))},draw:function(e){var i,s,a,t,d=this,l=d.ops,c=l.view;c&&(i=l.button||{},t=n(f.cancel||i.cancel,"取消"),l=n(f.remove||i.remove,"删除"),i=e.name,s=e.id,(a=r("div","u-item",'
'+i+'
')).taskId=s,e.box=a,c.appendChild(a),t=o(a.childNodes[1]),l=v(t),t=o(t),m(l,"click",function(){d.cancel(s)}),m(t,"click",function(){d.remove(s),c.removeChild(a)}),d.update(e))},update:function(e){var i,s,a,t,d,l,c,n,o;e&&e.box&&(i=e.total||e.size,s=e.loaded,a=e.state,c=e.box.childNodes[1],t=v(c),d=u(t),o=u(d),l=v(o),c=u(o),g(o=u(c),h.getStatusText(a)),o.title=e.message||"",i<0||(o="",this.html5&&null!=s&&0<=s&&(a==h.PROCESSING?"100.0"==(n=Math.min(100*s/i,100).toFixed(1))&&(n="99.9"):a==h.COMPLETE&&(n="100"),n&&g(c,l.style.width=n+="%"),o=''+p(s)+" / ",e=e.avgSpeed||e.speed,g(d,p(e)+"/s")),g(t,o+=''+p(i)+"")))},over:function(e){e&&e.box&&i(e.box,"u-over")}},h.extend(h.UI.File)}(window); -------------------------------------------------------------------------------- /dist/js/Q.Uploader.UI.Image.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2022/01/07 10:47:04 3 | !function(r,l){"use strict";var f=Q.getFirst,p=Q.getNext,v=Q.createEle,m=Q.setOpacity,o=Q.ie,c=Q.Uploader;function i(e,t){e.className+=" "+t}function n(t,e,i){var a=e.input,e=e.file||(a.files?a.files[0]:l);if(e)!function(e,t){var i=r.URL||r.webkitURL;if(i)return t(i.createObjectURL(e));r.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){e=a.value;if(e&&!/^\w:\\fakepath/.test(e)||(a.select(),parent.document.body.focus(),document.selection&&(e=document.selection.createRange().text)),e){t.innerHTML='';try{6
'+c+'
X
')).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,s;e&&e.box&&(t=e.total||e.size,i=e.loaded,a=e.state,r=e.boxProgress,o=e.boxDetail,n=c.getStatusText(a),this.html5&&i!=l&&0<=i&&(a==c.PROCESSING?"100.0"==(s=Math.min(100*i/t,100).toFixed(1))&&(s="99.9"):a==c.COMPLETE&&(s="100"),s&&(n+=" "+(s+="%"),r.style.width=s)),s=n,(n=o)&&(n.innerHTML=s||""),o.title=e.message||"")},over:function(e){e&&e.box&&i(e.box,"u-over")}},c.extend(c.UI.Image)}(window); -------------------------------------------------------------------------------- /dist/js/Q.Uploader.js: -------------------------------------------------------------------------------- 1 | //devin87@qq.com 2 | //build:2022/01/07 15:00:20 3 | !function(e,d){"use strict";var a=Q.def,c=Q.fire,i=Q.extend,u=Q.getFirst,n=Q.getLast,o=JSON.parse,s=Q.createEle,l=Q.parseHTML,p=Q.setOpacity,f=Q.getOffset||Q.offset,h=Q.md5File,t=Q.event,m=t.add,g=t.trigger,v=t.stop,_=!1,y=!1,w=!1,r=0,T=0,k=0,x=-1,S=-3,b={};function I(e,t){t=e.lastIndexOf(t);return-1!=t?e.slice(t):""}function E(e){if(e){for(var t=e.split(","),r={},a=0,i=t.length;a
'),a.appendChild(e),r=u(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(d,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)&&v(e)}),p(i,0),t.resetInput()),t.run("init",d,"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 r=t.getPos||f,a=t.boxInput,i=u(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(t,r,a){var i=this,e=i.fns||{},n=b.on||{};if(!a){var s=c(e[t],i,r),o=c(n[t],i,r);return s!=d?s:o}function u(e){if(s=e,e=n[t+"Async"])return c(e,i,r,l);l(c(n[t],i,r))}var l=function(e){a(s!=d?s:e)},o=e[t+"Async"];if(o)return c(o,i,r,u);u(c(e[t],i,r))},run:function(e,t,r){e=this[e];return e&&c(e,this,t),r&&this.fire(r,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=I(e.value,"\\").slice(1)||e.value,-1),n=this,s=I(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;var a=e.state||0;0<(r=t<(r=!r||r<0?0:r)?t:r)&&0==a&&(e.state=a=1),function(e,t,r){if(t&&!(t<=0)){var a,i=Date.now();if(t<=r)return(a=i-e.startTime)?e.avgSpeed=Math.min(Math.round(1e3*t/a),t):e.speed||(e.avgSpeed=e.speed=t),e.time=a||0,e.endTime=i;(a=i-e.lastTime)<200||(t=e.lastLoaded||0,e.speed=Math.min(Math.round(1e3*(r-t)/a),e.total),e.lastTime=i,e.lastLoaded=r)}}(e,t=2==a?r=e.size:t,r),e.total=t,e.loaded=r,this.run("update",e,"progress")},_process_response:function(e,t){(e.response=t)&&"json"==this.dataType&&(e.json=o(t))},complete:function(e,t,r){var a=this;return(e=!e&&1==a.workerThread?a._lastTask:e)&&(r!==d&&(a._process_response(e,r),!1===a.fire("uploadResponse",e)&&(t=S)),e.message||(r=e.json)&&(e.message=r.errMsg||r.msg||r.result||r.Result||r.message),t!=d&&(e.state=t),1!=e.state&&2!=t||(e.state=2,a.progress(e,e.size,e.size))),a.run("update",e,"update").run("over",e,"over"),-2==t&&a.fire("cancel",e),a.fire("complete",e),a.workerIdle++,a.started&&a.start(),a}},L.extend=function(e,t){i(L.prototype,e,t)},(t=e.XMLHttpRequest)&&(new t).upload&&e.FormData&&(_=!0),(t=document.createElement("input")).type="file",y=!!t.files,i(L,{support:{html5:w=_,multiple:y},READY:0,PROCESSING:1,COMPLETE:2,SKIP:x,CANCEL:-2,ERROR:S,UI:{},Lang:{status_ready:"准备中",status_processing:"上传中",status_complete:"已完成",status_skip:"已跳过",status_cancel:"已取消",status_error:"已失败",upload_error:"服务器或网络错误"},setup:function(e){i(b,e,!0)},getStatusText:function(e){var t=L.Lang;switch(e){case 0:return t.status_ready;case 1:return t.status_processing;case 2:return t.status_complete;case x:return t.status_skip;case-2:return t.status_cancel;case S:return t.status_error}return e}}),Q.Uploader=L}(window); -------------------------------------------------------------------------------- /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); -------------------------------------------------------------------------------- /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>>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}); -------------------------------------------------------------------------------- /images/Q/progress.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devin87/web-uploader/c9a4fcd77abf72b925db8ef6d3f3e0c68715f226/images/Q/progress.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /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 | boxState.title = task.message || ""; 130 | 131 | if (total < 0) return; 132 | 133 | var html_size = ''; 134 | 135 | //更新上传进度(for html5) 136 | if (this.html5 && loaded != undefined && loaded >= 0) { 137 | var percentText; 138 | 139 | if (state == Uploader.PROCESSING) { 140 | var percent = Math.min(loaded * 100 / total, 100); 141 | 142 | percentText = percent.toFixed(1); 143 | if (percentText == "100.0") percentText = "99.9"; 144 | 145 | } else if (state == Uploader.COMPLETE) { 146 | percentText = "100"; 147 | } 148 | 149 | //进度百分比 150 | if (percentText) { 151 | percentText += "%"; 152 | 153 | boxProgress.style.width = percentText; 154 | setHtml(boxDetail, percentText); 155 | } 156 | 157 | //已上传的文件大小 158 | html_size = '' + formatSize(loaded) + ' / '; 159 | 160 | //上传速度; 161 | var speed = task.avgSpeed || task.speed; 162 | setHtml(boxSpeed, formatSize(speed) + "/s"); 163 | } 164 | 165 | //文件总大小 166 | html_size += '' + formatSize(total) + ''; 167 | 168 | setHtml(boxSize, html_size); 169 | }, 170 | 171 | //上传完毕 172 | over: function (task) { 173 | if (!task || !task.box) return; 174 | 175 | addClass(task.box, "u-over"); 176 | } 177 | }; 178 | 179 | //实现默认的UI接口 180 | Uploader.extend(Uploader.UI.File); 181 | 182 | })(window); -------------------------------------------------------------------------------- /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 | boxDetail.title = task.message || ""; 324 | }, 325 | 326 | //上传完毕 327 | over: function (task) { 328 | if (!task || !task.box) return; 329 | 330 | addClass(task.box, "u-over"); 331 | } 332 | }; 333 | 334 | //实现默认的UI接口 335 | Uploader.extend(Uploader.UI.Image); 336 | 337 | })(window); -------------------------------------------------------------------------------- /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); -------------------------------------------------------------------------------- /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); -------------------------------------------------------------------------------- /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); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------