標籤:

框架基礎:ajax設計方案(三)--- 集成ajax上傳技術

來源:框架基礎:ajax設計方案(三)--- 集成ajax上傳技術

作者:仲強(@仲強 )(如需轉載請與作者聯繫)

  • 框架基礎:ajax設計方案(一)---集成核心請求

  • 框架基礎:ajax設計方案(二)---集成輪詢技術

在此之前讓我感慨一下現在的前端開發的氛圍。我遇到好多人,給我的觀念都是,這個東西這個框架有了,那個東西那個框架做了,前端嘛,學幾個框架,這個拼湊一下那個拼湊一下就好了。其實我想問,東西都框架做了,那你會什麼?會吹牛逼?會撕逼?

我在簡歷上寫專註原生js,曾經被幾個人笑過。我一直搞不懂,框架會過時,基礎永遠不會過時。離了框架,我可以活,我也會自己寫框架,我也在前端模塊化的路上自己摸索著,我也在前端如何更簡單,更快速開發的路上摸索著。難道有框架做了,我就坐吃山空等死嗎?

前端太浮躁,我也無所謂成為別人眼中的奇葩。我去做前端,我去做分離,我去寫後端,每天總會花一些時間在我喜歡的東西上。我知道我很傻逼,有的玩不玩,還去看代碼。那我沒辦法,我做奇葩很長時間了,因為我有自己的夢想,我有自己的追求。

請不要在我面前說,你這個東西人家都做了。做了怎麼了?人家做了,是你的嗎?人家做的時候前面不也有千千萬萬個死的淘汰的東西嗎?難道就因為人家做了我就不幹了?請不要目光短淺。鄙人也不隱藏我為什麼做前端,現在列舉我做前端的理由,你和我一樣嗎?

  1. 對接UI,需要涉獵頁面設計,布局和美化。學會去審美,學會站在用戶角度去看自己的東西。當然我不否認,UI妹子也多
  2. 對接js,對前端核心技術的考驗。對於頁面功能實現、新舊技術兼容和捨棄、如何寫出高效可維護代碼的思考
  3. 對接架構,前端性能優化,程序健壯性和容錯性,各種框架資源整合使用,從DNS解析到頁面渲染到你眼前的各種過程的跨域學習
  4. 對接後端,新老技術的變化,永遠不是前端,後端一樣需要去配合著去改變。所以懂後端,設計後端,寫後端,永遠都需要你去懂得
  5. 對接伺服器,反向代理,負載均衡,請求分發容錯整合等等難道不需要懂嗎?難道只敲個js就夠了?
  6. 前端的未來,任何項目的使用和展示,都離不開前端。因為前端第一接觸是用戶,是發展的最核心的資源。前端是站在整個戰線第一線的。
  7. 最重要的。前端剛起步,不是很成熟,可以接觸所有的原聲和底層,優化,開發,調試,甚至改變世界,都是有可能的。後端太成熟,接觸太多都是框架,突出重圍,需要更深的積累和叛逆。
  8. 最重要的,前端相對來說,薪水前期還是很好的。畢竟還要生活,詩和遠方都不及麵包重要,活著,一切才有可能。

之前發布了ajax的通用解決方案,核心的ajax發布請求,以及集成了輪詢。這次去外國網站逛逛,然後發現了ajax level2的上傳文件,所以就有了把ajax的上傳文件集成進去的想法,ajax方案的level2的改進就不介紹了,不清楚的可到前幾篇博客去看看。我們直接切入主題。

概念介紹:

1. js的FormData:js中在新的版本中已經支持了FormData對象,可以初始化一個空的form,或者初始化已經存在的form,瀏覽器測試代碼。

2. 瀏覽器的支持:瀏覽器已支持input=file的時候查看文件,具體包括文件的大小(size)和類型(type),瀏覽器測試如下

3. xmlhttprequest:支持發送(send方法)新的數據類型,包括DOMString、Document、FormData、Blob、File、ArrayBuffer。具體參見ajax設計方案的規範

工具準備:

1. 前端代碼

2. nginx伺服器(分離)

3. IIS伺服器(部署後台)

4. 後台代碼(webAPI)

什麼不多說,先貼代碼:

前端代碼:

/* * ajax上傳文件 * url 文件上傳地址 * fileSelector input=file 選擇器(支持多文件上傳,只要後台介面支持) * size 文件限制大小 * fileType 文件限制類型 mime類型 * success 上傳成功處理 * error 上傳失敗處理 * timeout 超時處理 * * return: status: 0 請選擇文件 * 1 超出文件限制大小 * 2 非允許文件格式 * */ upload:function(url,fileSelector,size,fileType,success,error,timeout){ var formdata = new FormData(),fileNode = document.querySelector(fileSelector),fileCount = fileNode.files.length,data={},result ={}; //以下為上傳文件限制檢查 if ( fileCount > 0 ){ tool.each(Array.prototype.slice.call(fileNode.files),function(value){ //檢查文件大小 if (value.size > size){ result["status"] = 1; result["errMsg"] = "超出文件限制大小"; }else{ //檢查文件格式.因為支持formdata,自然支持數組的indexof(h5) if (fileType.indexOf(value.type)=== -1 ){ result["status"] = 2; result["errMsg"] = "非允許文件格式"; }else{ formdata.append(value.name,value); }; }; }); }else{ result["status"] = 0; result["errMsg"] = "請選擇文件"; }; if (result.status !== undefined) return result; //如果有錯誤信息直接拋出去,結束運行 var ajaxParam ={ type:"post", url:url, data:formdata, isFormData:true, success:success, error:error, timeout:timeout }; ajax.common(ajaxParam); },};

後端介面代碼(C#的webAPI),代碼比較簡陋,能完成測試就好

[Route("upload3")] public async Task<HttpResponseMessage> PostFormData() { // Check if the request contains multipart/form-data. // 檢查該請求是否含有multipart/form-data if (!Request.Content.IsMimeMultipartContent()) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } string root = HttpContext.Current.Server.MapPath("~/uploadfile"); var provider = new ReNameMultipartFormDataStreamProvider(root); try { // Read the form data. // 讀取表單數據 var task = await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t => { if (t.IsFaulted || t.IsCanceled) { Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception); } string fileName = string.Empty; foreach (MultipartFileData file in provider.FileData) { fileName = file.LocalFileName; } //返回上傳後的文件全路徑 return new HttpResponseMessage() { Content = new StringContent(fileName) }; }); return Request.CreateResponse(HttpStatusCode.OK); } catch (System.Exception e) { return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e); } } /// /// 重命名上傳的文件 /// public class ReNameMultipartFormDataStreamProvider : MultipartFormDataStreamProvider { public ReNameMultipartFormDataStreamProvider(string root) : base(root) { } public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers) { //截取文件擴展名 string exp = Path.GetExtension(headers.ContentDisposition.FileName.TrimStart(").TrimEnd(")); string name = base.GetLocalFileName(headers); return name + exp; } }

瀏覽器測試結果(幾乎所有主流瀏覽器都支持,除了IE10以下)

測試代碼如下:

var temp = ajax.upload("/api/ajaxUpload/upload3/", "#file1", 1024 * 1024 * 1, ["image/png","image/bmp"], function (data) { if (data == "true") { alert("上傳成功!"); } if (data == "error") { alert(data); }});console.log(temp);

格式限制測試結果如下:

1. 選取超過限制大小的文件

2. 選取非允許格式的文件

3. 未選擇上傳文件

文件上傳成功測試

IE10、11

safari

火狐

谷歌

opera

edge

360瀏覽器

下面扯一下遇到的問題

  • 問題一 IE8-9兼容問題

在兼容性網站(Can I use... Support tables for HTML5, CSS3, etc)查詢兼容性,ajax leve2支持IE10+版本,所以如果在IE8-9上使用純前端代碼進行上傳文件的話,只有傳統的form標籤

html代碼如下:

<form id="formUpload" action="/api/ajaxUpload/upload2/" method="post" enctype="multipart/form-data" target="framFile"> <input name="isIE8" type="text" value="1" readonly style="display: none"/> <input id="iefile" type="file" name="age"/> <input type="submit" value="submit"></form><iframe id="framFile" name="framFile" src="postMsg.html"></iframe>

缺點:

1. 每次form提交的時候都會刷新頁面,如果想做非同步無刷新,用iframe做提交頁面

2. IE8-9無法在前端對文件進行大小和類型檢查(使用IE的文件組件不安全,因為可以修改系統上所有文件,容易被攻擊,而且瀏覽器都是默認關閉的)

3. 上傳文件介面不能有返回值,否則在IE8下會將介面返回值作為文件下載下來,且無法取得返回值(用了N種方法),但是在其他瀏覽器中ajax的成功事件會去做判斷,測試圖片如下

一些建議:如果真的要做IE8-9,現在普遍的方案是將flash插件和ajax Level2的上傳進行組合,支持H5的用ajax上傳,不支持初始化flash上傳插件。

PS:對於那些偏執的,一定要在IE8-9用純前端代碼支持上傳的,還有一種折中的方案,和這種思想類似,但是我做了優化,思路如下:

需要2個介面:上傳文件介面,IE8-9下上傳結果查詢介面

a. 首先使用form的無刷新上傳(ifarme)

b. 後台接收到formdata數據判斷是否是IE8-9的上傳,是的話將該用戶上傳文件是否成功的狀態改變(不管存庫或者其他地方),否則直接返回上傳結果

c. 前端在form的submit之後,發起得到一次結果的輪詢,如果得到結果,則直接結束輪詢,結果查詢介面也將該用戶的上傳文件狀態清空

  • 問題二 一般ajax請求和formdata請求,後台取值問題。

傳統http請求,可以直接在介面參數中取得數據,但是使用formdata進行ajax請求的話,後台介面需要從formdata對象中取數據,包括文件啥的。因為這個我寫後台介面的時候就懵逼了好長一段時間,然後左查查右查查,終於明白取值方式也不一樣了。

  • 問題三 關於formdata上傳文件,具體能上傳多大文件的限制問題

上傳文件的限制取決於web容器可接受上傳文件的大小,tomcat、IIS等web容器都有自己的設置方法,具體可搜索引擎,你懂的

  • 問題四 前端對大文件的傳輸解決方案,具體可參考該文章

在新的版本中,就是支持H5的版本中,有了File對象可以切割文件,因為在取到input=file中取到的文件都是File類型,File對象有個方法slice,可以切割文件,然後分配一個xhr上傳。主要是後台的切割文件重組問題不是很清楚,所以我暫時也沒有集成大文件上傳方法。

代碼已集成github:GerryIsWarrior/ajax點顆星星是我最大的鼓勵,有什麼問題可以博客、郵箱、github上留言

這一次上傳版本,代碼做過變動,變動如下:

  1. 增加FormData數據傳輸方法(postFormData),如果判斷到瀏覽器不支持FormData,則自動使用默認原始的數據傳輸
  2. 新增各種類型判斷方法,判斷類型
  3. 更新each方法,判斷如果傳入參數obj為數組而且瀏覽器支持h5的新特性直接用數組的forEach方法

我的全棧書籤,這次更新整理了國內頂級互聯網和著名的一些互聯網公司的招聘網站,希望大家找到好工作,^_^

github地址:GerryIsWarrior/MyBookmarks

—————————————————————————————————————————

在學習過程如果有任何疑問,請來極樂網(dreawer.com)提問,或者掃描下方二維碼,關注極樂官方微信,在平台下方留言。

推薦閱讀:

業界主流的RPC框架有哪些?Dubbo與Hadoop RPC的區別?
基於 Ruby 實現的移動 App 自動化測試框架 (AppiumBooster)
2016年 CSS 庫、框架和工具新生榜 TOP 50
MVC到底是設計模式還是一種框架?

TAG:框架 | Ajax |