WebUploader是由Baidu WebFE(FEX)團隊開發的一個簡單的以HTML5爲主,FLASH爲輔的現代文件上傳組件。javascript
具體接口參考 webuploader接口文檔地址css
引入資源html
<!--引入CSS--> <link rel="stylesheet" type="text/css" href="webuploader/webuploader.css"> <!--引入JS--> <script type="text/javascript" src="webuploader/webuploader.js"></script>
htmljava
<div id="uploader" class="wu-example"> <div class="queueList"> <div id="dndArea" class="placeholder"> <div id="filePicker"></div> <p>或將照片拖到這裏,單次最多可選300張</p> </div> </div> <div class="statusBar" style="display:none;"> <div class="progress"> <span class="text">0%</span> <span class="percentage"></span> </div> <div class="info"></div> <div class="btns"> <div id="filePicker2"></div> <div class="uploadBtn">開始上傳</div> </div> </div> </div>
css樣式python
#container {
color: #838383;
font-size: 12px;
}
#uploader .queueList {
margin: 20px;
border: 3px dashed #e6e6e6;
}
#uploader .queueList.filled {
padding: 17px;
margin: 0;
border: 3px dashed transparent;
}
#uploader .queueList.webuploader-dnd-over {
border: 3px dashed #999999;
}
#uploader p {margin: 0;}
.element-invisible {
position: absolute !important;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px,1px,1px,1px);
}
#uploader .placeholder {
min-height: 350px;
padding-top: 178px;
text-align: center;
background: url(../../../img/webuploader.png) center 93px no-repeat;
color: #cccccc;
font-size: 18px;
position: relative;
}
#uploader .placeholder .webuploader-pick {
font-size: 18px;
background: #00b7ee;
border-radius: 3px;
line-height: 44px;
padding: 0 30px;
*width: 120px;
color: #fff;
display: inline-block;
margin: 0 auto 20px auto;
cursor: pointer;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
#uploader .placeholder .webuploader-pick-hover {
background: #00a2d4;
}
#uploader .placeholder .flashTip {
color: #666666;
font-size: 12px;
position: absolute;
width: 100%;
text-align: center;
bottom: 20px;
}
#uploader .placeholder .flashTip a {
color: #0785d1;
text-decoration: none;
}
#uploader .placeholder .flashTip a:hover {
text-decoration: underline;
}
#uploader .filelist {
list-style: none;
margin: 0;
padding: 0;
}
#uploader .filelist:after {
content: '';
display: block;
width: 0;
height: 0;
overflow: hidden;
clear: both;
}
#uploader .filelist li {
width: 110px;
height: 110px;
background: url(../../img/bg.png) no-repeat;
text-align: center;
margin: 0 8px 20px 0;
position: relative;
display: inline;
float: left;
overflow: hidden;
font-size: 12px;
}
#uploader .filelist li p.log {
position: relative;
top: -45px;
}
#uploader .filelist li p.title {
position: absolute;
top: 0;
left: 0;
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow : ellipsis;
top: 5px;
text-indent: 5px;
text-align: left;
}
#uploader .filelist li p.progress {
position: absolute;
width: 100%;
bottom: 0;
left: 0;
height: 8px;
overflow: hidden;
z-index: 50;
margin: 0;
border-radius: 0;
background: none;
-webkit-box-shadow: 0 0 0;
}
#uploader .filelist li p.progress span {
display: none;
overflow: hidden;
width: 0;
height: 100%;
background: #1483d8 url(../../img/progress.png) repeat-x;
-webit-transition: width 200ms linear;
-moz-transition: width 200ms linear;
-o-transition: width 200ms linear;
-ms-transition: width 200ms linear;
transition: width 200ms linear;
-webkit-animation: progressmove 2s linear infinite;
-moz-animation: progressmove 2s linear infinite;
-o-animation: progressmove 2s linear infinite;
-ms-animation: progressmove 2s linear infinite;
animation: progressmove 2s linear infinite;
-webkit-transform: translateZ(0);
}
@-webkit-keyframes progressmove {
0% {
background-position: 0 0;
}
100% {
background-position: 17px 0;
}
}
@-moz-keyframes progressmove {
0% {
background-position: 0 0;
}
100% {
background-position: 17px 0;
}
}
@keyframes progressmove {
0% {
background-position: 0 0;
}
100% {
background-position: 17px 0;
}
}
#uploader .filelist li p.imgWrap {
position: relative;
z-index: 2;
line-height: 110px;
vertical-align: middle;
overflow: hidden;
width: 110px;
height: 110px;
-webkit-transform-origin: 50% 50%;
-moz-transform-origin: 50% 50%;
-o-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
transform-origin: 50% 50%;
-webit-transition: 200ms ease-out;
-moz-transition: 200ms ease-out;
-o-transition: 200ms ease-out;
-ms-transition: 200ms ease-out;
transition: 200ms ease-out;
}
#uploader .filelist li img {
width: 100%;
}
#uploader .filelist li p.error {
background: #f43838;
color: #fff;
position: absolute;
bottom: 0;
left: 0;
height: 28px;
line-height: 28px;
width: 100%;
z-index: 100;
}
#uploader .filelist li .success {
display: block;
position: absolute;
left: 0;
bottom: 0;
height: 40px;
width: 100%;
z-index: 200;
background: url(../../img/success.png) no-repeat right bottom;
}
#uploader .filelist div.file-panel {
position: absolute;
height: 0;
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#80000000', endColorstr='#80000000')\0;
background: rgba( 0, 0, 0, 0.5 );
width: 100%;
top: 0;
left: 0;
overflow: hidden;
z-index: 300;
}
#uploader .filelist div.file-panel span {
width: 24px;
height: 24px;
display: inline;
float: right;
text-indent: -9999px;
overflow: hidden;
background: url(../../img/icons.png) no-repeat;
margin: 5px 1px 1px;
cursor: pointer;
}
#uploader .filelist div.file-panel span.rotateLeft {
background-position: 0 -24px;
}
#uploader .filelist div.file-panel span.rotateLeft:hover {
background-position: 0 0;
}
#uploader .filelist div.file-panel span.rotateRight {
background-position: -24px -24px;
}
#uploader .filelist div.file-panel span.rotateRight:hover {
background-position: -24px 0;
}
#uploader .filelist div.file-panel span.cancel {
background-position: -48px -24px;
}
#uploader .filelist div.file-panel span.cancel:hover {
background-position: -48px 0;
}
#uploader .statusBar {
height: 63px;
border-top: 1px solid #dadada;
padding: 0 20px;
line-height: 63px;
vertical-align: middle;
position: relative;
}
#uploader .statusBar .progress {
border: 1px solid #1483d8;
width: 198px;
background: #fff;
height: 18px;
position: relative;
display: inline-block;
text-align: center;
line-height: 20px;
color: #6dbfff;
position: relative;
margin: 0 10px 0 0;
}
#uploader .statusBar .progress span.percentage {
width: 0;
height: 100%;
left: 0;
top: 0;
background: #1483d8;
position: absolute;
}
#uploader .statusBar .progress span.text {
position: relative;
z-index: 10;
}
#uploader .statusBar .info {
display: inline-block;
font-size: 14px;
color: #666666;
}
#uploader .statusBar .btns {
position: absolute;
top: 10px;
right: 20px;
line-height: 40px;
}
#filePicker2 {
display: inline-block;
float: left;
}
#uploader .statusBar .btns .webuploader-pick,
#uploader .statusBar .btns .uploadBtn,
#uploader .statusBar .btns .uploadBtn.state-uploading,
#uploader .statusBar .btns .uploadBtn.state-paused {
background: #ffffff;
border: 1px solid #cfcfcf;
color: #565656;
padding: 0 18px;
display: inline-block;
border-radius: 3px;
margin-left: 10px;
cursor: pointer;
font-size: 14px;
float: left;
}
#uploader .statusBar .btns .webuploader-pick-hover,
#uploader .statusBar .btns .uploadBtn:hover,
#uploader .statusBar .btns .uploadBtn.state-uploading:hover,
#uploader .statusBar .btns .uploadBtn.state-paused:hover {
background: #f0f0f0;
}
#uploader .statusBar .btns .uploadBtn {
background: #00b7ee;
color: #fff;
border-color: transparent;
}
#uploader .statusBar .btns .uploadBtn:hover {
background: #00a2d4;
}
#uploader .statusBar .btns .uploadBtn.disabled {
pointer-events: none;
opacity: 0.6;
}
用於保存swf文件 的html 文件jquery
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>403 Forbidden</title> </head><body> <h1>Forbidden</h1> <p>You don't have permission to access /theme/hplus/js/plugins/webuploader/ on this server.</p> <hr> <address>Apache Server at www.zi-han.net Port 80</address> </body></html>
上js代碼git
var BASE_URL = 'js/plugins/webuploader/index.html'; //保存swf文件 jQuery(function() { function e(e) { var a = o('<li id="' + e.id + '"><p class="title">' + e.name + '</p><p class="imgWrap"></p><p class="progress"><span></span></p></li>'), s = o('<div class="file-panel"><span class="cancel">刪除</span><span class="rotateRight">向右旋轉</span><span class="rotateLeft">向左旋轉</span></div>').appendTo(a), i = a.find("p.progress span"), t = a.find("p.imgWrap"), r = o('<p class="error"></p>'), d = function(e) { switch (e) { case "exceed_size": text = "文件大小超出"; break; case "interrupt": text = "上傳暫停"; break; default: text = "上傳失敗,請重試" } r.text(text).appendTo(a) }; "invalid" === e.getStatus() ? d(e.statusText) : (t.text("預覽中"), n.makeThumb(e, function(e, a) { if (e) return void t.text("不能預覽"); var s = o('<img src="' + a + '">'); t.empty().append(s) }, v, b), w[e.id] = [e.size, 0], e.rotation = 0), e.on("statuschange", function(t, n) { "progress" === n ? i.hide().width(0) : "queued" === n && (a.off("mouseenter mouseleave"), s.remove()), "error" === t || "invalid" === t ? (console.log(e.statusText), d(e.statusText), w[e.id][1] = 1) : "interrupt" === t ? d("interrupt") : "queued" === t ? w[e.id][1] = 0 : "progress" === t ? (r.remove(), i.css("display", "block")) : "complete" === t && a.append('<span class="success"></span>'), a.removeClass("state-" + n).addClass("state-" + t) }), a.on("mouseenter", function() { s.stop().animate({ height: 30 }) }), a.on("mouseleave", function() { s.stop().animate({ height: 0 }) }), s.on("click", "span", function() { var a, s = o(this).index(); switch (s) { case 0: return void n.removeFile(e); case 1: e.rotation += 90; break; case 2: e.rotation -= 90 } x ? (a = "rotate(" + e.rotation + "deg)", t.css({ "-webkit-transform": a, "-mos-transform": a, "-o-transform": a, transform: a })) : t.css("filter", "progid:DXImageTransform.Microsoft.BasicImage(rotation=" + ~~ (e.rotation / 90 % 4 + 4) % 4 + ")") }), a.appendTo(l) } function a(e) { var a = o("#" + e.id); delete w[e.id], s(), a.off().find(".file-panel").off().end().remove() } function s() { var e, a = 0, s = 0, t = f.children(); o.each(w, function(e, i) { s += i[0], a += i[0] * i[1] }), e = s ? a / s: 0, t.eq(0).text(Math.round(100 * e) + "%"), t.eq(1).css("width", Math.round(100 * e) + "%"), i() } function i() { var e, a = ""; "ready" === k ? a = "選中" + m + "張圖片,共" + WebUploader.formatSize(h) + "。": "confirm" === k ? (e = n.getStats(), e.uploadFailNum && (a = "已成功上傳" + e.successNum + "張照片至XX相冊," + e.uploadFailNum + '張照片上傳失敗,<a class="retry" href="#">從新上傳</a>失敗圖片或<a class="ignore" href="#">忽略</a>')) : (e = n.getStats(), a = "共" + m + "張(" + WebUploader.formatSize(h) + "),已上傳" + e.successNum + "張", e.uploadFailNum && (a += ",失敗" + e.uploadFailNum + "張")), p.html(a) } function t(e) { var a; if (e !== k) { switch (c.removeClass("state-" + k), c.addClass("state-" + e), k = e) { case "pedding": u.removeClass("element-invisible"), l.parent().removeClass("filled"), l.hide(), d.addClass("element-invisible"), n.refresh(); break; case "ready": u.addClass("element-invisible"), o("#filePicker2").removeClass("element-invisible"), l.parent().addClass("filled"), l.show(), d.removeClass("element-invisible"), n.refresh(); break; case "uploading": o("#filePicker2").addClass("element-invisible"), f.show(), c.text("暫停上傳"); break; case "paused": f.show(), c.text("繼續上傳"); break; case "confirm": if (f.hide(), c.text("開始上傳").addClass("disabled"), a = n.getStats(), a.successNum && !a.uploadFailNum) return void t("finish"); break; case "finish": a = n.getStats(), a.successNum ? alert("上傳成功") : (k = "done", location.reload()) } i() } } var n, o = jQuery, r = o("#uploader"), l = o('<ul class="filelist"></ul>').appendTo(r.find(".queueList")), d = r.find(".statusBar"), p = d.find(".info"), c = r.find(".uploadBtn"), u = r.find(".placeholder"), f = d.find(".progress").hide(), m = 0, h = 0, g = window.devicePixelRatio || 1, v = 110 * g, b = 110 * g, k = "pedding", w = {}, x = function() { var e = document.createElement("p").style, a = "transition" in e || "WebkitTransition" in e || "MozTransition" in e || "msTransition" in e || "OTransition" in e; return e = null, a } (); if (!WebUploader.Uploader.support()) throw alert("不支持您的瀏覽器!若是你使用的是IE瀏覽器,請嘗試升級 flash 播放器"), new Error("不支持您的瀏覽器"); n = WebUploader.create({ pick: { id: "#filePicker", label: "點擊選擇圖片" }, dnd: "#uploader .queueList", paste: document.body, accept: { title: "Images", extensions: "gif,jpg,jpeg,bmp,png", mimeTypes: "image/*" }, swf: BASE_URL + "/Uploader.swf", disableGlobalDnd: !0, chunked: !0, server: "webuploader_photo.html", duplicate: true, fileNumLimit: 9, fileSizeLimit: 5242880, fileSingleSizeLimit: 1048576, headers: { "X-CSRFToken": $.cookie('csrftoken') }, }), n.addButton({ id: "#filePicker2", label: "繼續添加" }), n.onUploadProgress = function(e, a) { var i = o("#" + e.id), t = i.find(".progress span"); t.css("width", 100 * a + "%"), w[e.id][1] = a, s() }, n.on('uploadSuccess', function (file, response) { var res=JSON.parse(response._raw); //這裏能夠獲得後臺返回的數據 // $('#' + file.id).addClass('upload-state-done'); var input = document.createElement("input"); console.log(res.data) var data = res.data input.type="hidden"; input.name=data.id; input.value=data.path; $("#"+data.id).append(input); console.log($("#"+data.id)) }); n.onFileQueued = function(a) { m++, h += a.size, 1 === m && (u.addClass("element-invisible"), d.show()), e(a), t("ready"), s() }, n.onFileDequeued = function(e) { m--, h -= e.size, m || t("pedding"), a(e), s() }, n.on("all", function(e) { switch (e) { case "uploadFinished": t("confirm"); break; case "startUpload": t("uploading"); break; case "stopUpload": t("paused") } }), n.onError=function(e){ // alert("Error: "+e) switch (e) { case 'Q_EXCEED_NUM_LIMIT': alert("錯誤:最多上傳九張圖片!"); break; case 'Q_EXCEED_SIZE_LIMIT': alert.msg("錯誤:文件總大小超出限制!"); break; case 'F_EXCEED_SIZE': alert.msg("錯誤:文件大小超出限制!"); break; case 'Q_TYPE_DENIED': alert.msg("錯誤:禁止上傳該類型文件!"); break; case 'F_DUPLICATE': alert.msg("錯誤:請勿重複上傳該文件!"); break; default: alert.msg('錯誤代碼:' + type); break; } }, c.on("click", function() { return o(this).hasClass("disabled") ? !1 : void("ready" === k ? n.upload() : "paused" === k ? n.upload() : "uploading" === k && n.stop()) }), p.on("click", ".retry", function() { n.retry() }), p.on("click", ".ignore", function() { alert.msg("已忽略") }), c.addClass("state-" + k), s() });
服務端接收上傳文件github
def webuploader_photo(request): ret = {"status": False, "data": {"path": "", "name": ""}, "summary": ""} target_path = "media/upload/goods/" try: # 獲取文件對象 post_obj = request.POST file_obj = request.FILES.get("file") raw_name = file_obj.name raw_id = post_obj.get("id") postfix = raw_name.split(".")[-1] if file_obj: file_name = str(uuid.uuid4()) + "." + postfix if not os.path.exists(os.path.dirname(target_path)): os.makedirs(target_path) file_path = os.path.join(target_path, file_name) # os.path.join()在Linux/macOS下會以斜槓(/)分隔路徑,而在Windows下則會以反斜槓(\)分隔路徑, # 故統一路徑將'\'替換成'/' file_path = file_path.replace('\\', "/") with open(file_path, "wb") as f: for chunk in file_obj.chunks(): f.write(chunk) ret["status"] = True ret["data"]['path'] = file_path ret["data"]['name'] = raw_name ret["data"]["id"] = raw_id except Exception as e: ret["summary"] = str(e) return HttpResponse(json.dumps(ret))
webuploader真的是一個很是強大的文件上傳組件web
htmlajax
<div class="page-container"> <div id="uploader" class="uploader" > <div class="wrapper" class="placeholder"> <div class="file-list"></div> <p>或將照片拖到這裏,單次最多可選9張</p> </div> <div class="actions-area"> <div class="actions"> <div class="filePicker action">選擇圖片</div> <div class="uploadFile action upload-btn disabled">上傳圖片</div> </div> </div> </div> </div> <script type="text/javascript"> var BASE_URL = '/static/plugins/webuploader/index.html'; //webuploader 相關配置 var uploader = new WebUploaderSupport({ server: "webuploader_photo.html", paste: document.body, swf: BASE_URL + "/Uploader.swf", // swf文件所處路徑 support: { uploader: "#uploader", //上傳區域容器選擇器 fileSize: 9, //文件總個數, -1時無限制 serverFiles: HandlerFile(), {#[{"src":"","name":"2.PNG","attrs":{"data-server-file":true,"data-delete-url":""}}]#} }, duplicate: true, fileNumLimit: 9, fileSizeLimit: 5242880, fileSingleSizeLimit: 1048576, headers: { "X-CSRFToken": $.cookie('csrftoken') }, }); </script>
css
.uploader {
position: relative;
padding: 15px 15px;
margin: 15px 0;
background-color: #fafafa;
box-shadow: inset 0 3px 6px rgba(0, 0, 0, .05);
border-color: #e5e5e5 #eee #eee;
border-style: solid;
border-width: 1px 0;
min-width: 250px;
}
.uploader:after {
display: block;
content: "";
overflow: hidden;
clear: both;
}
.uploader .message {
font-size: 16px;
margin-bottom: 20px;
}
.uploader .wrapper {
text-align: center;
border: 3px dashed #ccc;
/* background: url(../images/image.png) center 93px no-repeat;*/
color: #cccccc;
font-size: 18px;
position: relative;
}
.file-list {
list-style: none;
margin: 0;
padding: 0;
}
.file-list:after {
display: block;
content: "";
overflow: hidden;
clear: both;
}
.file-list .file-item {
float: left;
width: 150px;
height: 150px;
margin: 5px;
border: 1px solid;
overflow: hidden;
position: relative;
}
.file-list .file-item img {
width: 100%;
height: 100%;
}
.file-list .file-item .file-delete, .file-list .file-item .file-retry {
display: none;
position: absolute;
width: 100px;
height: 30px;
left: 50%;
margin-left: -50px;
margin-top: -15px;
top: 50%;
z-index: 1;
}
/*重試時顯示重試按鈕*/
.file-list .file-item.retry:hover .file-retry {
display: block;
}
/*刪除重試按鈕樣式*/
.file-list .file-item button {
outline: none !important;
border: 0;
padding: 5px 12px;
opacity: 0.9;
color: #fff!important;
text-align: center;
border-radius: 3px;
display: inline-block;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/*刪除按鈕*/
.file-list .file-item .file-delete button {
background: #f14641;
}
.file-list .file-item .file-delete button:hover {
background: #f1281a;
}
.file-list .file-item .file-retry button {
background: #daa7ff;
}
.file-list .file-item .file-retry button:hover {
background: #bf8cff;
}
/*重試時進度條隱藏*/
.file-list .file-item.retry .progress {
display: none!important;
}
.file-list .file-item:hover .file-delete {
display: block;
}
/*不能預覽時的提示*/
.file-list .file-item .preview-tips {
position: absolute;
width: 100px;
height: 20px;
font-size: 16px;
left: 50%;
margin-left: -50px;
color: #949390;
margin-top: -10px;
top: 50%;
overflow: hidden;
z-index: 1;
}
/*鼠標通過當前item時隱藏提示不能預覽的內容*/
.file-list .file-item:hover .preview-tips {
z-index: -1;
}
/*鼠標通過當前item時若是是不能編輯則不隱藏文字*/
.file-list .file-item.not-edit:hover .preview .preview-tips {
z-index: 1;
}
.file-list .file-item.not-edit:hover .file-delete {
display: none;
}
.file-list .file-item.not-edit:hover .file-retry {
display: none;
}
.file-item .file-info {
position: absolute;
left: 4px;
bottom: 4px;
right: 4px;
height: 20px;
line-height: 20px;
text-indent: 5px;
background: rgba(0, 0, 0, 0.6);
color: white;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 12px;
z-index: 1;
}
.file-item .state {
position: absolute;
left: 4px;
top: 4px;
right: 4px;
height: 20px;
line-height: 20px;
text-indent: 5px;
background: rgba(0, 0, 0, 0.6);
color: white;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 12px;
z-index: 1;
}
.file-item .state.ready {
background: rgba(169, 64, 64, 0.6);
}
.file-item .state.success {
background: rgba(68, 247, 22, 0.6);
}
.file-item .state.error {
background: red;
}
/*進度條*/
.file-item .progress {
display: none;
position: absolute;
width: 120px;
height: 14px;
left: 50%;
margin-left: -60px;
margin-top: -7px;
top: 50%;
}
.actions-area {
height: 55px;
position: relative;
}
.actions {
position: absolute;
top: 15px;
right: 0px;
}
.actions .action {
display: inline-block;
float: left;
height: 40px;
}
.upload-btn {
outline: none !important;
cursor: pointer;
border: 0;
padding: 10px 15px;
background: #76d67d!important;
color: #fff!important;
text-align: center;
border-radius: 3px;
overflow: hidden;
margin-left: 14px;
}
.upload-btn:hover {
background: #4bb953 !important;
}
.upload-btn.disabled {
background: #848080!important;
}
.upload-btn.disabled:hover {
background: #404040!important;
}
/*上傳時使某些內容不顯示*/
.uploading {
z-index: -1!important;
}
.file-list .file-item.retry .file-delete {
display: block;
margin-top: 0;
}
.file-list .file-item.retry button {
padding: 2px 5px;
}
.file-list .file-item.retry .file-retry {
display: block;
margin-top: -30px;
}
.actions-area {
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
}
.actions-area button.disabled{
cursor: not-allowed;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
opacity: .65;
}
.actions-area button {
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
}
/*上傳失敗須要重試時未生成縮略圖的提示文字隱藏*/
.file-list .file-item.retry .preview-tips {
display: none;
}
.progress {
height: 20px;
margin-bottom: 20px;
overflow: hidden;
background-color: #f5f5f5;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
}
.progress-bar {
float: left;
width: 0;
height: 100%;
font-size: 12px;
line-height: 20px;
color: #fff;
text-align: center;
background-color: #337ab7;
-webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
-webkit-transition: width .6s ease;
-o-transition: width .6s ease;
transition: width .6s ease;
}
function WebUploaderSupport(options) { var that = this; var fileStatus = { inited: "inited", //初始狀態 queued: "queued", //已經進入隊列, 等待上傳 progress: "progress", //上傳中 complete: "complete", //上傳完成 error: "error", //上傳出錯,可重試 interrupt: "interrupt", //上傳中斷,可續傳 invalid: "invalid", //文件不合格,不能重試上傳。會自動從隊列中移除 cancelled: "cancelled" //文件被移除 }; WebUploaderSupport.fileStatus = fileStatus; var $fns = { log: function (content) { if(support.log && console) { console.log(content); } }, logInfo: function () { var support = that.support; if(!support) { this.log("WebUploader does not support the browser you are using."); } else { if(this.getUploader() == null) { this.log("WebUploader has not inited, please use it after inited."); } } }, getUploader: function () { var uploader = that.uploader; return uploader; }, getFiles: function () { var result = null; var uploader = this.getUploader(); if(uploader) { result = uploader.getFiles(); } return result; }, getFileSize: function (status) { var result = 0; var uploader = this.getUploader(); if(uploader) { if(status != null) { result = uploader.getFiles(status).length; } else { result = uploader.getFiles().length; } } return result; }, getInitedFileSize: function () { //獲取inited狀態的文件個數 return this.getFileSize('inited'); }, retry: function (file) { var uploader = this.getUploader(); if(uploader) { if(that.edit) { if(file != null) { uploader.retry(file); } else { uploader.retry(); } } else { this.log("can't retry, because not in edit mode"); } } this.logInfo(); }, upload: function () { var uploader = this.getUploader(); if(uploader) { if(that.edit) { uploader.upload(); } else { this.log("can't upload, because not in edit mode"); } } this.logInfo(); }, removeFileWithItem: function (file) { var uploader = that.uploader; if(file) { support.removeFileItem(support.getItem(file)); if(uploader) { uploader.removeFile(file.id, true); } } } }; that.$fns = $fns; options = options || {}; var support = { $fns: {}, //公共函數 $elements: {}, //區域jquery元素 edit: true, uploader: ".uploader", //上傳區域容器選擇器 dndWrapper: ".wrapper", //拖拽區域選擇器 chooseFileBtn: ".filePicker", //選擇文件的按鈕選擇器 uploadFileBtn: ".uploadFile", //上傳文件的按鈕選擇器 fileList: ".file-list", //顯示文件列表的區域選擇器 fileListHeight: 150, //初始默認高度 log: false, //是否打印信息 multiple: true, //默認多選 thumbnailWidth: 150, thumbnailHeight: 150, fileSize: -1, //文件總個數, -1時無限制 instance: null, //uploader實例 ratio: (function () { return window.devicePixelRatio || 1; //優化retina, 在retina下這個值是2 })(), getActualThumbnailWidth: function () { var that = this; var ratio = that.ratio; return that.thumbnailWidth * ratio; }, getActualThumbnailHeight: function () { var that = this; var ratio = that.ratio; return that.thumbnailHeight * ratio; }, /** * 獲取不能預覽的文件的樣式,可覆蓋(可根據名稱解析後返回img對象顯示) * @param name 文件名 * @param thumbnailHeight * @param thumbnailWidth * @returns {jQuery} */ getThumbErrorPreview: function (name, thumbnailHeight, thumbnailWidth) { var $element = $('<div class="preview"></div>').css({ height: thumbnailHeight, width: thumbnailWidth }).append($('<div class="preview-tips">不能預覽</div>')); return $element; //顯示圖片的方式 //return $('<img src="../images/preview/1.jpg">'); }, showPreview: function ($item, file) { //顯示文件中的預覽效果 var $preview = $('<img />'); $item.append($preview); var uploader = this.instance; // 縮略圖大小 var thumbnailWidth = this.getActualThumbnailWidth(), thumbnailHeight = this.getActualThumbnailHeight(); this.setItemStyle($item); //設置item寬高 var self = this; uploader.makeThumb(file, function (error, src) { if (error) { $preview.replaceWith(self.getThumbErrorPreview(file.name, self.thumbnailHeight, self.thumbnailWidth)); return; } $preview.attr('src', src); }, thumbnailWidth, thumbnailHeight); }, getItem: function (file) { //獲取$item return $("#" + file.id); }, setItemStyle: function ($item) { //設置縮略圖所在容器的寬高,默認是均150px,用於加載文件預覽時設置 if($item) { var that = this; var thumbnailWidth = that.thumbnailWidth, thumbnailHeight = that.thumbnailHeight; $item.css({width: thumbnailWidth, height: thumbnailHeight}); //設置$item寬高 } }, loadUploadFileBtnStyle: function () { //用於加載上傳按鈕的樣式 var $fns = this.$fns; var $uploadFileBtn = this.$elements.$uploadFileBtn; if($fns && $uploadFileBtn) { var initedSize = $fns.getInitedFileSize(); if (initedSize === 0) { //inited 文件個數 $uploadFileBtn.addClass("disabled"); //上傳按鈕禁用 } else { $uploadFileBtn.removeClass("disabled"); //移除上傳按鈕的禁用樣式 } } }, retryFile: function ($item, file) { var $fns = this.$fns; $fns.retry(file); }, renderItem: function (isFile, data) { var name = data.name || ""; var html = '<div class="file-item thumbnail">' + '<div class="file-info">' + name + '</div>' + '<div class="file-operations">' + '<div class="file-delete">' + '<button type="button">' + '刪除</button></div>' + '<div class="file-retry">' + '<button type="button">' + '重試</button></div>' + '</div>' + '<div class="progress">' + '<div class="progress-bar"></div>' + '</div>' + '</div>'; var $item = $(html); if (!isFile) { //服務端顯示數據時 var $preview; //根據文件後綴進行展現預覽結果 if(/(.jpg|.png|.gif|.bmp|.jpeg)$/.test(name.toLocaleLowerCase())) { $preview = $('<img src="'+ data.src + '"/>'); //自定義添加 var $input =$('<input type="text" value="'+ data.src +'" class="hidden" >') ; $item.append($input); } else { $preview = this.getThumbErrorPreview(data.name, this.thumbnailHeight, this.thumbnailWidth); } $item.append($preview); } else { $item.attr("id", data.id); $item.append('<div class="state ready">等待上傳...</div>'); this.showPreview($item, data); //顯示預覽效果 } return $item[0]; }, fileQueued: function (file) { //文件被添加進隊列 var self = this; var $item = $(this.renderItem(true, file)); var $fileList = this.$elements.$fileList; $fileList.append($item); //顯示在文件列表中 self.loadUploadFileBtnStyle(); //加載上傳按鈕樣式 $item.on("click", 'button', function () { var $this = $(this); if($this.parents(".file-retry")[0]) { self.retryFile($item, file); } else if ($this.parents(".file-delete")[0]) { self.deleteFile($item, file, self.deleteServerFileCallback, self.$fns.removeFileWithItem); } }); self.loadChooseFileBtnStyle(this.$elements.$chooseFileBtn, this.$elements.$uploadFileBtn); }, fileDequeued: function (file) { this.loadUploadFileBtnStyle(); }, uploadProgress: function (file, percentage) { //文件上傳過程當中建立進度條 var $item = this.getItem(file); $item.find('.file-delete, .preview-tips').addClass("uploading"); //隱藏刪除按鈕、提示文字 $item.removeClass("retry"); //移除重試class $item.find('.progress').show(); //顯示進度條 var $percent = $item.find('.progress .progress-bar'); $percent.css('width', percentage * 100 + '%'); }, uploadComplete: function (file) { //完成上傳時,不管成功或者失敗 var $item = this.getItem(file); $item.find('.progress').fadeOut(); $item.find('.file-delete, .preview-tips').removeClass("uploading"); //顯示刪除按鈕、提示文字 var $uploadFileBtn = this.$elements.$uploadFileBtn; var $chooseFileBtn = this.$elements.$chooseFileBtn; this.loadChooseFileBtnStyle($chooseFileBtn, $uploadFileBtn); this.loadUploadFileBtnStyle(); }, uploadSuccess: function (file, response) { // 文件上傳完成後 var res=JSON.parse(response._raw);//這裏能夠獲得後臺返回的數據 var $item = this.getItem(file), $state = $item.find('.state'); $item.find('.progress').hide(); if (res.status) { //上傳成功時 this.uploadSuccessCallbck($item, res); //用於標識爲服務端文件 if (!$state.hasClass('success')) { $state.attr("class", "state success"); $state.text('上傳成功'); } $item.removeClass("retry"); } else { if (!$state.hasClass('error')) { $state.attr("class", "state error"); $state.text('上傳失敗'); } $item.addClass("retry"); } }, uploadSuccessCallbck: function ($item, data) { //上傳文件成功時的回調,用於標識爲服務端文件 console.log("$item",$item) if($item && data) { var input = document.createElement("input"); console.log(data.data) var data = data.data input.type="hidden"; input.name=data.id; input.value="/"+data.path; $item .append(input); } }, /*** * 當文件被加入隊列以前觸發,若返回false則此文件不會被添加進入隊列 * @param file * @returns {boolean} 爲true時能夠添加到webuploader中並進行顯示 */ beforeFileQueued: function(file) { var fileSize = this.fileSize; //獲取當前總個數 if(fileSize < 1) { //無限制個數 return true; } var currentFileSize = this.getCurrentFileSize(); //當前總個數 var flag = false; var edit = this.edit; if(edit) { //可編輯模式時 if(currentFileSize < fileSize) { flag = true; } } //執行beforeFileQueuedCallback回調函數 this.beforeFileQueuedCallback(edit, flag, file, fileSize, currentFileSize); return flag; }, /** * 當文件被加入隊列返回結果以前觸發 * @param edit 是否可編輯 * @param result 是否會添加並顯示 * @param file --- file對象 * @param fileSize --- 總文件個數 * @param currentFileSize --- 當前文件個數 */ beforeFileQueuedCallback: function (edit, result, file, fileSize, currentFileSize) { }, /** * 當validate不經過時觸發 * @param type * Q_EXCEED_SIZE_LIMIT 在設置了Q_EXCEED_SIZE_LIMIT且嘗試給uploader添加的文件總大小超出這個值時 * Q_TYPE_DENIED 當文件類型不知足時 * Q_EXCEED_NUM_LIMIT 在設置了fileNumLimit且嘗試給uploader添加的文件數量超出這個值時 */ errorTypeHanlder: function (type, file) { }, uploadError: function (file) { //文件上傳失敗後 console.log("uploadError") var $item = this.getItem(file), $state = $item.find('.state'); if (!$state.hasClass('error')) { $state.attr("class", "state error"); $state.text('上傳失敗'); } $item.addClass("retry"); this.loadUploadFileBtnStyle(); this.uploadErrorAfter(file); }, /** * 上傳失敗後執行 * @param file */ uploadErrorAfter: function (file) { }, uploadFinished: function () {}, //文件上傳完後觸發 serverFileAttrName: "data-server-file", //服務端文件的屬性名稱 getIsServerFile: function ($item) { //判斷文件是不是服務端文件 var val = $item && $item.attr(this.serverFileAttrName); if(val && val === "true") { return true; } return false; }, getServerFileSize: function () { //獲取服務端文件的個數 var $fileList = this.$elements.$fileList; var size = 0; var serverFileAttrName = this.serverFileAttrName; if($fileList) { size = $fileList.find(".file-item["+serverFileAttrName+"='true']").size(); } return size; }, getItemSize: function () { //獲取當前item的文件個數 var $fileList = this.$elements.$fileList; var size = 0; if($fileList) { size = $fileList.find(".file-item").size(); } return size; }, getCurrentFileSize: function () { //獲取當前uploader實例中文件的個數 var fileStatus = WebUploaderSupport.fileStatus; var $fns = this.$fns; var initedSize = $fns.getFileSize(fileStatus.inited); //初始狀態個數 var errorSize = $fns.getFileSize(fileStatus.error); //上傳失敗個數 var size = initedSize + errorSize + this.getServerFileSize();//最終加上服務端文件個數 var itemSize = this.getItemSize(); var result = itemSize > size ? itemSize : size; return result; }, removeFileItem: function($item) { //移除$item if($item && $item[0]) { $item.off().remove(); } }, deleteFile: function ($item, file, deleteServerFileCallback, removeFileWithItem) { //刪除文件的處理邏輯,包含服務端 if(this.getIsServerFile($item)) { //服務端時 if(this.edit) { this.deleteServerFile($item, deleteServerFileCallback); } else { this.$fns.log("can't delete server file"); } } else { if(removeFileWithItem && file) { removeFileWithItem(file); } var $chooseFileBtn = this.$elements.$chooseFileBtn, $uploadFileBtn = this.$elements.$uploadFileBtn; this.loadChooseFileBtnStyle($chooseFileBtn, $uploadFileBtn); } }, deleteServerFileAttrName: "data-delete-url", /** * 刪除服務端文件(依賴於getIsServerFile的判斷結果)的業務操做,可根據實際覆蓋重寫(support配置中直接重寫該函數便可) * @param $item * @param deleteServerFileCallback */ deleteServerFile: function ($item, deleteServerFileCallback) { var self = this; //獲取刪除的url var url = $item && $item.attr(self.deleteServerFileAttrName); if(url) { $.ajax({ dataType: "json", type: "post", url: url, success: function (json) { if(deleteServerFileCallback && typeof deleteServerFileCallback === "function") { deleteServerFileCallback(self, $item, json); //經過callback執行業務操做 var $chooseFileBtn = self.$elements.$chooseFileBtn, $uploadFileBtn = self.$elements.$uploadFileBtn; self.loadChooseFileBtnStyle($chooseFileBtn, $uploadFileBtn); } } }); } }, /** * deleteServerFile 響應成功時的回調處理,可根據實際覆蓋重寫 * @param self 當前對象 * @param $item * @param data */ deleteServerFileCallback: function (self, $item, data) { if(data.status) { self.removeFileItem($item); } else { alert(data.content); } }, serverFiles: [], //加載服務端的數據,當前爲 [{name:string, src: string, attrs: {}}] init: function (data, $fileList, $chooseFileBtn, $uploadFileBtn) { //初始化服務端數據,及加載樣式 var self = this; var edit = self.edit; var $files = null; var thumbnailHeight = self.thumbnailHeight; $fileList.css({"min-height": thumbnailHeight + 20}); //設置該區域最小高度爲thumbnailHeight + 20px //加載服務端數據 if(data && data.length > 0) { for(var i in data) { var item = data[i]; var $item = $(this.renderItem(false, item)); if(!edit) { $item.addClass("not-edit"); } self.setItemStyle($item); //以縮略圖大小設置$item寬高 if($item && item) { var attrs = item.attrs; for(var key in attrs) { //設置$item屬性值 $item.attr(key, attrs[key]); } } if(i === "0") { $files = $item; } else { $files = $files.add($item); } } } if($files) { //加載服務端數據 $fileList.append($files); $files.on('click', '.file-delete button', function () { var $item = $(this).parents(".file-item"); self.deleteFile($item, null, self.deleteServerFileCallback); }); } self.loadChooseFileBtnStyle($chooseFileBtn, $uploadFileBtn); }, editChange: function (edit) { //用於根據edit改變時進行設置webuploader模式 var that = this; that.edit = edit; var $chooseFileBtn = that.$elements.$chooseFileBtn, $uploadFileBtn = that.$elements.$uploadFileBtn; var $fileList = that.$elements.$fileList; if(edit) { $fileList.children().removeClass("not-edit"); } else { $fileList.children().addClass("not-edit"); } that.loadChooseFileBtnStyle($chooseFileBtn, $uploadFileBtn); }, getChooseFileLabel: function ($chooseFileBtn) { //獲取當前上傳文件按鈕對應的label,該label用於觸發選擇文件 var $label = null; if($chooseFileBtn) { if($chooseFileBtn.hasClass("webuploader-container")) { $label = $chooseFileBtn.find(".webuploader-element-invisible").next("label"); } else { $label = $(".webuploader-container").not(this.chooseFileBtn).find(".webuploader-element-invisible").next("label"); } } return $label; }, loadChooseFileBtnStyle: function ($chooseFileBtn, $uploadFileBtn) { //根據文件個數進行展現選擇文件的按鈕(用於上傳完成時,刪除文件時,添加到隊列時, 初次加載服務端數據時) var that = this; var $fns = that.$fns; var fileSize = that.fileSize; var $actions = $chooseFileBtn.parents(".actions"); var $actionsArea = $actions.parent(".actions-area"); var $label = that.getChooseFileLabel($chooseFileBtn); if (that.edit) { //可編輯時 $actionsArea.css("height", ""); $actions.show(); var uploader = $fns.getUploader(); if(uploader) { uploader.refresh(); //解決label按鈕點擊無反應 } if (fileSize > 0) { var currentSize = that.getCurrentFileSize(); if (fileSize === currentSize) { $label && $label.hide(); $chooseFileBtn.hide(); $uploadFileBtn.addClass("right"); } else { $label && $label.show(); $chooseFileBtn.show(); $uploadFileBtn.removeClass("right"); } } else { $label && $label.show(); $chooseFileBtn.show(); $uploadFileBtn.removeClass("right"); } } else { //不可編輯時 $actions.hide(); $actionsArea.css("height", 10); if ($label) { $label.hide(); } } } }; support = $.extend(support, options.support); support.$fns = $fns; //設置support方法 that.supports = support; var multiple = support.multiple; delete options.support; //刪除額外的support屬性 var $uploader = $(support.uploader), $chooseFileBtn = $uploader.find(support.chooseFileBtn), //選擇文件的按鈕選擇器 $fileList = $uploader.find(support.fileList), //顯示文件列表的區域 $uploadFileBtn = $uploader.find(support.uploadFileBtn), //上傳文件的按鈕 $dndWrapper = $uploader.find(support.dndWrapper); //支持拖拽到此區域 var $elements = { $uploader: $uploader, $chooseFileBtn: $chooseFileBtn, $fileList: $fileList, $uploadFileBtn: $uploadFileBtn, $dndWrapper: $dndWrapper }; support.$elements = $elements; var defaultOption = { accept: { title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/!*' }, pick: { id: $chooseFileBtn, multiple: multiple }, disableGlobalDnd: true, dnd: $dndWrapper, //支持拖拽到此區域 resize: false, compress: false, //不壓縮圖片,原圖 swf: 'Uploader.swf' // swf文件路徑 }; var currentOptions = $.extend(true, {}, defaultOption, options); //當期webuploader的配置, options中的優先級最高 if(document.all || window.ActiveXObject || "ActiveXObject" in window) { if(currentOptions.paste != null) { currentOptions.paste = null; $fns.log("ie is not support paste"); } } that.edit = support.edit; that.support = WebUploader.Uploader.support(); //獲取是否支持webuploader上傳 jQuery(function() { var $ = jQuery; $fileList.css({"min-height": support.fileListHeight + 20}); var uploader; try { if(!that.support) { support.init(support.serverFiles, $fileList, $chooseFileBtn, $uploadFileBtn); $fns.log("WebUploader does not support the browser you are using."); return; } else { uploader = WebUploader.create(currentOptions); //實例化webuploader support.instance = uploader; support.init(support.serverFiles, $fileList, $chooseFileBtn, $uploadFileBtn); } } catch (e) { if(console) { console.log(e); } } if(uploader) { that.uploader = uploader; if($uploadFileBtn && $uploadFileBtn[0]) { $uploadFileBtn.click(function () { $fns.upload(); }); } //文件被添加進隊列時 uploader.on('fileQueued', function (file) { support.fileQueued && support.fileQueued.apply(support, arguments); }); //移除文件時 uploader.on('fileDequeued', function (file) { support.fileDequeued && support.fileDequeued.apply(support, arguments); }); uploader.on('uploadProgress', function (file, percentage) { support.uploadProgress && support.uploadProgress.apply(support, arguments); }); //完成上傳時,不管成功或者失敗 uploader.on('uploadComplete', function (file) { support.uploadComplete && support.uploadComplete.apply(support, arguments); }); // 文件上傳完成後,添加相應的樣式(響應成功) uploader.on('uploadSuccess', function (file, data) { support.uploadSuccess && support.uploadSuccess.apply(support, arguments); }); // 文件上傳失敗,顯示上傳出錯(上傳失敗出現錯誤狀態碼時) uploader.on('uploadError', function (file) { support.uploadError && support.uploadError.apply(support, arguments); }); // 當文件被加入隊列以前觸 uploader.on('beforeFileQueued', function (file) { return support.beforeFileQueued && support.beforeFileQueued.apply(support, arguments); }); //當前uploader實例文件上傳完成後觸發 uploader.on("uploadFinished", function () { support.uploadFinished && support.uploadFinished.apply(support, arguments); }); uploader.on('error', function () { support.errorTypeHanlder && support.errorTypeHanlder.apply(support, arguments); }); } }); } //上傳該uploader實例的文件 WebUploaderSupport.prototype.upload = function () { this.$fns.upload(); } //判斷是否正在上傳中 WebUploaderSupport.prototype.isInProgress = function () { var flag = false; var uploader = this.uploader; if(uploader) { flag = uploader.isInProgress(); } this.$fns.logInfo(); return flag; } WebUploaderSupport.prototype.retry = function () { this.$fns.retry(); } WebUploaderSupport.prototype.getSupports = function () { var supports = this.supports; return supports; } //更換模式 WebUploaderSupport.prototype.editChange = function (edit) { if(typeof edit != "boolean") { throw new Error("the param type must be boolean"); } var supports = this.supports; this.edit = edit; supports.editChange(edit); }
參考github上的一個朋友對webuploader 的封裝:https://github.com/joker-pper/WebUploaderSupport