最近M端項目中涉及到圖片上傳功能,現把項目中遇到的一些問題及解決辦法分享如下,與各位共同探討:
一、相關需求:
1. 客戶端上限10M
2. 服務器端上限2M
3. 文件過濾
4. 顯示上傳進度
5. 異步上傳
6. 多文件上傳
二、需求分析:
1. 實現2,可使用canvas在前端實現壓縮(base64);
2. 實1、3,可採用file.size及/image/.test(file.type)過濾;
3. 實現4,使用XHR2實現上傳,添加進度時間監控,xhr.upload.addEventListener(“progress”, uploadProgress, false);
4. 實現5,使用XHR2實現上傳;
5. 多文件上傳,單文件循環上傳即可,但是兼容進度時,需單文件各自監控;
三、需求實現:
實現一:二進制方式上傳
需求1、3、4、5、6皆可實現,但是服務器端上傳2M,使用canvas方式壓縮後生成的是base64,若使用此方式上傳,必須把base64轉換成二進制流,GitHub上也有相關文章把base64轉換成二進制流的,使用xhr.sendAsBinary()發送二進制流,參考此文,測了一部分常見機型,可以實現,具體是否可以在項目中使用還有待論證。二進制上傳實現部分代碼僅供參考:
01 |
var uploadFile
= function (fileid,
file) { |
02 |
var xhr
= new XMLHttpRequest(), |
04 |
url
= "/picture/upload" , |
06 |
uploadProgress
= function (evt)
{ |
07 |
if (evt.lengthComputable)
{ |
08 |
var percent
= (evt.loaded * 100 / evt.total).toFixed(1); |
11 |
console.log( 'unable
to compute' ); |
14 |
uploadFailed
= function (evt)
{ |
17 |
uploadCanceled
= function (evt)
{ |
21 |
xhr.upload.addEventListener( "progress" ,
uploadProgress, false ); |
22 |
xhr.addEventListener( "error" ,
uploadFailed, false ); |
23 |
xhr.addEventListener( "abort" ,
uploadCanceled, false ); |
24 |
xhr.open( "POST" ,
url , true ); |
25 |
xhr.onreadystatechange
= function (e)
{ |
26 |
if (xhr.readyState
== 4) { |
27 |
if (xhr.status
=== 200) { |
28 |
var data
= xhr.response; |
40 |
fd.append(fileid,
file); |
實現二:base64上傳
需求1、2、3、5、6皆可實現,實現此種方式即基本的Get上傳,但是無法實時監控上傳文件進度,需求4無法實現。
實現三:二進制+base64
即上述兩種方案的綜合。也可參考此文移動端Web上傳圖片實踐中的實例。
四、問題總結:
M端瀏覽器各異,支持情況各異,現總結如下:
a) 部分酷派機型瀏覽器(微信、UC、QQ、百度),中興自帶瀏覽器不支持input[type=file];
解決方式:放棄
b) Adroid機型,不同瀏覽器對input[type=file]支持不同,有的沒有圖庫選項,有的沒有相機選項。主要表現爲小米、酷派部分機型的微信自帶瀏覽器。
解決方式:input[type=file]添加accept=’image/*’屬性,可實現某些adriod機型不出現文檔選項。
c) 上傳文件時,出現圖片自動旋轉的問題
解決方式:實現開源插件CanvasResize中exif.js來糾正,實現此插件可解決壓縮、糾正圖片旋轉,但Adroid上UC瀏覽器中會出現下圖問題:(國外人寫的插件哪會管國內瀏覽器死活!)
最後採用的騰訊的一款壓縮方案,解決了UC瀏覽器的問題。
d) 使用壓縮插件時需注意,PNG圖片壓縮時往往會偏大,可把壓縮成image/jpeg格式;
var cvs = document.createElement(‘canvas’);
var ctx = cvs.getContext(“2d”).drawImage(source_img_obj, 0, 0);
var newImageData = cvs.toDataURL(‘image/jpeg’, quality/100);
f) 因瀏覽器對input[type=file]顯示風格各異,項目使用label的for指向input[type=file]的id,並設置input{display:none};在Adroid部分瀏覽器上點擊無反應;
解決方式:設置input{position: absolute; top: -999em;}來隱藏。
#20150923 update start
使用label標籤for屬性觸發input[type=file]需要注意的一些問題:
1. label的for指向的是input[text/radio/checkbox/date]等元素trigger的是focus事件,而指向input[file]元素trigger的是click 事件,因此可以打開一個瀏覽器窗口,這就是我們使用label的for指向input[file],使用zepto的tap事件綁定時,不會觸發的原因。
2. 給input[type=file]設置display: none or visibility: hidden將不會工作,因爲表單提交時input的值不會被髮送到服務端。除上面解決的top:-999em;也可以使用以下CSS來設置隱藏:
你可能會有疑問,這裏爲什麼不把寬高設置爲0而爲0.1px。設置爲0在一些瀏覽器tab頁可能不會被解析。同時設置position:absolute屬性來使其脫離標準流,不會影響其他元素的佈局。
如是想,使用label標籤的for屬性,我們就可以使用CSS3各種屬性來DIY按鈕各種樣式及效果了,不是嗎?
同時可以設置當input[file]focus時,label的outline樣式:
1 |
.input[type=file]:focus
+ label { |
2 |
outline : 1px dotted #000 ; |
3 |
outline :
-webkit-focus-ring-color auto 5px ; |
Firefox中對:hover,:active支持良好,當時會忽略input[type=”file”]:focus設置,但是Firefox對input[file]支持focus、blur事件,我們可以通過JS來增加和刪除類has-focus來實現:
1 |
input.addEventListener( 'focus' , function (){
input.classList.add( 'has-focus' );
}); |
2 |
input.addEventListener( 'blur' , function (){
input.classList.remove( 'has-focus' );
}); |
3 |
.inputfile:focus
+ label, |
4 |
.inputfile.has-focus
+ label { |
5 |
outline:
1px dotted #000; |
6 |
outline:
-webkit-focus-ring-color auto 5px; |
3. input[file]多選(multiple),現在多選在移動端和PC端支持都不太好,PC端IE9及以下瀏覽器不支持該屬性;移動端andriod系統某些自帶瀏覽器、UC瀏覽器支持情況各異。
#20150923 update end
g) 在部分Adroid支持input[type=file]的瀏覽器中,當使用/image/.test(file.type)時,選擇圖片文件會返回false。使用JSON.stringify(file)分析後發現,是file對象中的name字段中沒有包含後綴,同時type字段爲空,使用this.value獲取路徑中也沒有包含後綴。因此過濾出現問題。
如下結果:
1 |
{ "webkitRelativePath" : "" , "lastModified" :1433304214000, "lastModifiedDate" : "2015-06-03T04:03:34.000Z" , |
2 |
"name" : "fanmian" , "type" : "" , "size" :2273852} |
正常結果如下:
1 |
{ "webkitRelativePath" : "" , "lastModified" :1433304214000, "lastModifiedDate" : "2015-06-03T04:03:34.000Z" , |
2 |
"name" : "fanmian.png" , "type" : "image/png" , "size" :2273852} |
解決方式:放開/image/.test(file.type)過濾,在壓縮時,拋出錯誤過濾。
h) html5上傳文件,Firefox支持重複選擇同一文件,其它瀏覽器不支持
解決方式:每次選擇文件後給input[type=file]賦值空。
2015-09-09補充
input[type=file]控件比較特殊:
對於ios,已實現file,ios7版本之前,可以喚起照片集裏的圖片文件;ios7後,實現了拍照和錄像的功能。不過在7.0.3裏有bug,程序會閃退;
對於andriod,如果使用的是瀏覽器,file類型的文件選擇,會喚起瀏覽器實現的文件選擇,不過文件的選擇,不同的手機,具體實現不同,web無法控制。如果在android app裏使用webkit的方式,需要android的webkit實現私有api接口,才能實現file選擇上傳。
input[type=file]控件在M端瀏覽器支持情況(部分機型) by qianqian and xiaocui of my team —2015.12.12 add
編號 |
手機設備 |
測試瀏覽器 |
是否支持拍照 |
是否支持從相冊選擇 |
1 |
iphone4S |
微信瀏覽器 |
支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
2 |
iphone5S |
微信瀏覽器 |
支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
3 |
iphone6 |
微信瀏覽器 |
支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
4 |
iphone6 plus |
微信瀏覽器 |
支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
5 |
小米手機2S |
微信瀏覽器 |
不支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
6 |
小米手機M4 |
微信瀏覽器 |
不支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
7 |
三星S6 ED |
微信瀏覽器 |
不支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
8 |
三星SM-G5308W |
微信瀏覽器 |
不支持 |
不支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
9 |
華爲榮耀7 |
微信瀏覽器 |
不支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
10 |
華爲P7 |
微信瀏覽器 |
不支持 |
支持 |
QQ瀏覽器 |
不支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
11 |
華爲PE-TL10 |
微信瀏覽器 |
不支持 |
支持 |
QQ瀏覽器 |
支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
支持 |
支持 |
12 |
酷派8675-A |
微信瀏覽器 |
不支持 |
支持 |
QQ瀏覽器 |
不支持 |
支持 |
UC瀏覽器 |
支持 |
支持 |
手機自帶瀏覽器 |
不支持 |
不支持 |
五、參考鏈接:
Html5 File Upload with Progress
移動端Web上傳圖片實踐
圖片壓縮成base64,採用二進流上傳
Styling & Customizing File Inputs the Smart Way
注:本文爲轉載文章,原文信息如下: