關於js非同步上傳文件

来源:http://www.cnblogs.com/yuanlong1012/archive/2016/01/13/5127497.html
-Advertisement-
Play Games

好久沒登錄博客園了,今天來一發分享。 最近項目里有個需求,上傳文件(好吧,這種需求很常見,這也不是第一次遇到了)。當時第一想法就是直接用form表單提交(原諒我以前就是這麼乾的),不過表單里不僅有文件還有別的信息需要交互,跟後端商量後決定文件單獨上傳,獲取到伺服器端返回的文件地址在和表單一起提...


  好久沒登錄博客園了,今天來一發分享。

  最近項目里有個需求,上傳文件(好吧,這種需求很常見,這也不是第一次遇到了)。當時第一想法就是直接用form表單提交(原諒我以前就是這麼乾的),不過表單里不僅有文件還有別的信息需要交互,跟後端商量後決定文件單獨上傳,獲取到伺服器端返回的文件地址在和表單一起提交。這裡就需要非同步上傳文件。

  在網上扒了扒相關的內容,發現還真不少,阮一峰老師的這篇文章(文件上傳的漸進式增強)就介紹的很具體,下麵就談談自己在實戰中遇到的一些問題的感受吧。

  先看看效果,實現了哪些功能

  (好吧,就一個按鈕而已,搞得神神秘秘,嘿嘿)

  

<button type="button" class="btn" @click="upload">點擊上傳文件</button>

  給按鈕綁定了一個點擊事件,下麵看看點擊事件方法里做了什麼

methods: {
        upload: function(){
            myUpload({
                url: window.location.protocol + '//' + window.location.host + '/crm/upload',
                maxSize: 10,
                beforeSend: function(file){

                },
                callback: function(res){
                    var data = JSON.parse(res);
                    pageCont.attachmentUrl = data.url;
                },
                uploading: function(pre){
                    pageCont.uploadCont.display = 'block';
                    pageCont.uploadStyle.width = pre * 2 + 'px';
                    pageCont.pre = pre;
                }
            });
        }
}

  按鈕綁定的點擊事件執行了upload方法,在upload方法里調用了一下myUpload方法,並傳遞了一些配置信息進去,稍後說下這些配置信息。先看看myUpload的具體實現:

  初始化了一個FormData對象和一個XMHttpResquest對象,創建一個type為file的input,並觸發一次該input的click,如下

var fd = new FormData(),
        xhr = new XMLHttpRequest(),
        input;
input = document.createElement('input');
input.setAttribute('id', 'myUploadInput');
input.setAttribute('type', 'file');
input.setAttribute('name', 'file');
document.body.appendChild(input);
input.style.display = 'none';
input.click();

  監聽剛纔創建的input的change事件,並作在裡面做相應處理

input.onchange = function(){
        if(!input.value){return;}
        if(option.maxSize &&  input.files[0].size > option.maxSize * 1024 * 1024){
            dialog({
                title: '提示',
                content: '請上傳小於'+option.maxSize+'M的文件',
                okValue: '確定',
                ok: function () {}
            }).showModal();
            return;
        }
        if(option.beforeSend instanceof Function){
            if(option.beforeSend(input) === false){
                return false;
            }
        }
        fd.append('file', input.files[0]);
        xhr.open('post', option.url);
        xhr.onreadystatechange = function(){
            if(xhr.status == 200){
                if(xhr.readyState == 4){
                    if(option.callback instanceof Function){
                        option.callback(xhr.responseText);
                    }
                }
            }else{
                if(!(dialog.get('uploadfail'))){
                    dialog({
                        id: 'uploadfail',
                        title: '提示',
                        content: '上傳失敗',
                        okValue: '確定',
                        ok: function () {}
                    }).showModal();
                }
            }
        }
        xhr.upload.onprogress = function(event){
            var pre = Math.floor(100 * event.loaded / event.total);
            if(option.uploading instanceof Function){
                option.uploading(pre);
            }
        }
        xhr.send(fd);
    }

  解釋下上面的代碼。input的change事件觸發後,首先判斷了下當前是否選擇了文件

if(!input.value){return;}

  已開始我是沒做這個判斷的,在後來的測試過程中發現,當上傳一次文件後,再次點擊按鈕上傳,打開文件選擇框,然後不選擇文件,而是點擊取消按鈕,change事件也觸發了,導致後面的代碼也會執行,顯然這不合理,故加了這個判斷。

  然後限制了下上傳文件的大小(這樣的事能夠前端處理就不要交給服務端來驗證了),當文件大小超過最大限制,就會彈框提示

if(option.maxSize &&  input.files[0].size > option.maxSize * 1024 * 1024){
            dialog({
                title: '提示',
                content: '請上傳小於'+option.maxSize+'M的文件',
                okValue: '確定',
                ok: function () {}
            }).showModal();
            return;
        }

  然後加了一個文件上傳前的操作,可以在文件上傳前做一些處理,如進度條的顯示,圖片預覽等等

       if(option.beforeSend instanceof Function){
            if(option.beforeSend(input) === false){
                return false;
            }
        }   

 

  接下來將文件append到formData對象里,使用欄位名‘file’,該欄位名是服務端接收文件時使用的欄位名

fd.append('file', input.files[0]);

  然後就是使用XMLHttpRequest對象向服務端發送數據了

        xhr.open('post', option.url);
        xhr.onreadystatechange = function(){
            if(xhr.status == 200){
                if(xhr.readyState == 4){
                    if(option.callback instanceof Function){
                        option.callback(xhr.responseText);
                    }
                }
            }else{
                if(!(dialog.get('uploadfail'))){
                    dialog({
                        id: 'uploadfail',
                        title: '提示',
                        content: '上傳失敗',
                        okValue: '確定',
                        ok: function () {}
                    }).showModal();
                }
            }
        }
        xhr.upload.onprogress = function(event){
            var pre = Math.floor(100 * event.loaded / event.total);
            if(option.uploading instanceof Function){
                option.uploading(pre);
            }
        }
        xhr.send(fd);    

  在向服務端發送數據時,使用了監聽了一下progress事件,主要是為了進行上傳進度的顯示,上述代碼中,

var pre = Math.floor(100 * event.loaded / event.total);

  獲取上傳的百分比,能夠拿到這個值,頁面上就可以展示各種各樣的上傳進度效果了。

 

  差不多介紹完了,下麵補充一下使用中遇到的問題:

  問題一:文件在上傳的過程中,使用JSON.parse()序列化服務端返回的json字元串報錯(傻啊,文件還在上傳,服務端怎麼會返回數據啊)。

事情是這樣的,一開始,我在readystatechange里只監聽了狀態碼是否是200,如果是就說明通了,然後執行回調,在回調里處理服務端返回的數據,但是通了不一定代表服務端已經返回了數據,所以就出現了上面的錯誤,所以後來在判斷了status是否為200後,還判斷了readyState,以確保服務端已處理完畢並返回數據在執行回調

            if(xhr.status == 200){
                    if(option.callback instanceof Function){
                        option.callback(xhr.responseText);
                    }
            }    

  問題二:重覆創建input。每次點擊按鈕上傳文件後,頁面都會多一個type=file的input感覺不是很好(個人癖好吧),所以對最開始的初始化代碼做了下優化,判斷當前頁面是否存在剛纔創建的input,存在就直接使用,不存在就創建,如下

    if(document.getElementById('myUploadInput')){
        input = document.getElementById('myUploadInput');
    }else{
        input = document.createElement('input');
        input.setAttribute('id', 'myUploadInput');
        input.setAttribute('type', 'file');
        input.setAttribute('name', 'file');
        document.body.appendChild(input);
        input.style.display = 'none';
    }

 

好了,就這麼多了。看看效果

 

因個人知識面有限,如有錯誤,還請指正。轉載請註明出處,謝謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一,效果圖。二,代碼。//點擊任何處,彈出提示選項-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UIAlertView * alert=[[UIAlertView alloc] initWithTitl...
  • 本文總結了20種ios濾鏡都是基於GPUImage的,有3種濾鏡是GPUImage庫中包含的,還有17種是Instagram中的經典濾鏡,集成在一個項目中。使用GPUImage可以非常容易創建我們自己的濾鏡效果總會有你想要的效果吧。在文章下麵附源碼下載 相信你也在使用濾鏡吧,今天就讓你見識一下...
  • 雜談在進行android進行開發時,我們的數據一般通過介面來獲收,這裡指的介面泛指web api,webservice,wcf,web應用程式等;它們做為服務端與資料庫進行直接通訊,而APP這塊通過向這些介面發Http請求來獲得數據,這樣的好處大叔認為,可以有效的降低軟體的開發難度,所以數據交互都被...
  • JavaScript數組常用操作、總結
  • jQuery
  • 話說JSON數據平常用的確實挺多的,但是基本上只知道怎麼用,對其一些細節並沒有整理過,今兒趁著下午有點空,坐下來,學習整理下,並分享出來。 對於JSON,首先它只是一種數據格式,並非一種語言,雖然和javascript長的比較像,但並不從屬於javascript。如果你使用過其他編程...
  • 需求:在富文本編輯器中插入圖片,圖片來自用戶可以自己上傳的圖片庫。本來可以用比較噁心的方式,也就是直接用tinyMCE自帶的插入圖片插件來實現。噁心是因為這個圖片插件需要用戶填入圖片的url。想來想去,雖然是內部管理平臺產品1期,但作為一個21世紀的程式猿做這樣的事兒太low了,而且也怕被同事圍毆,...
  • 好久沒更新博客了...自從有了mac之後世界變得簡單了。。。日常麽,除了研究代碼,看別人的代碼,寫自己的代碼。就那樣。。。。吐槽點:window配個nodejs的環境花了九頭牛兩隻老虎的力氣,mac上只要安裝,就可以啪啪啪啪了。。。。瞬間無力吐槽。。。今天突然想到了偶的徒弟上次說貌似不會用retur...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...