首頁 web前端 js教程 談談基於iframe、FormData、FileReader三種無刷新上傳檔案的方法_javascript技巧

談談基於iframe、FormData、FileReader三種無刷新上傳檔案的方法_javascript技巧

May 16, 2016 pm 03:27 PM
formdata iframe

發送請求有兩種方式,一種是用ajax,另一種是用form提交,預設的form提交如果不做處理的話,會使頁面重定向。以一個簡單的demo做說明:


     html如下圖所示,請求的路徑action為"upload",其它的不做任何處理:

 <form method="POST" action="upload" enctype="multipart/form-data">
  名字 <input type="text" name="user"></input>
  头像 <input type="file" name="file"></input>
  <input type="submit" id="_submit" value="提交"></input>
 </form> 
登入後複製

      服務端(node)response直接回傳: "Recieved form data",示範如下:

       可看到預設情況下,form請求upload的同時重新導向至upload。但是很多情況下是希望form請求像ajax一樣,不會重定向或刷新頁面。像上面的場景,當上傳完成之後,將使用者選擇的頭像顯示在目前頁面。

      解決方法第一種是使用html5的FormData,將form裡面的資料封裝到FormData物件裡,再以POST的方式send出去。如下面程式碼所示,對提交按鈕的點選事件做一個回應,程式碼第6行取得到form的DOM對象,然後第8行建構一個FormData的實例,第18行,將form資料傳送出去。

document.getElementById("_submit").onclick = function(event){
   //取消掉默认的form提交方式
   if(event.preventDefault) event.preventDefault();
   else event.returnValue = false;       //对于IE的取消方式
   var formDOM = document.getElementsByTagName("form")[];
   //将form的DOM对象当作FormData的构造函数
   var formData = new FormData(formDOM);
   var req = new XMLHttpRequest();
   req.open("POST", "upload");
   //请求完成
   req.onload = function(){
    if(this.status === ){
      //对请求成功的处理
    }
   }
   //将form数据发送出去
   req.send(formData);
       //避免内存泄漏
       req = null;
 } 
登入後複製

      成功上傳後,服務將返回圖片的存取地址,補充14行對請求成功的處理:在submit按鈕的上方位置顯示上傳的圖片:            

var img = document.createElement("img");
     img.src = JSON.parse(this.responseText).path;
     formDOM.insertBefore(img, document.getElementById("_submit")); 
登入後複製

      範例: 


      如果使用jQuery,可以把formData當作ajax的data參數,同時設定contentType: false和processData: false,告訴jQuery不要去處理請求頭和傳送的資料。

      看起來這種提交方式跟ajax一樣,但是其實並不是完全一樣,form提交的資料格式有三種,如果要上傳文件則必須為multipart/form-data,所以上面的form提交請求裡的http的頭資訊裡面的Content-Type為multipart/form-data,而普通的ajax提交為application/json。 form提交完整的Content-Type如下:

"content-type":"multipart/form-data; boundary=------WebKitFormBoundaryYOE7pWLqdFYSeBFj"

       除了multipart/form-data之外,也指定了boundary,這個boundary的功能是用來區分不同的欄位。由於FormData物件是不透明的,呼叫JSON.stringify將會傳回一個空的物件{},同時FormData只提供append方法,所以無法得到FormData實際上傳的內容,但是可以透過分析工具或服務收到的資料來查看。在上面如果上傳一個文字文件,那麼服務收到的POST資料的原始格式是這樣的:

------WebKitFormBoundaryYOE7pWLqdFYSeBFj

Content-Disposition: form-data; name="user"

abc

------WebKitFormBoundaryYOE7pWLqdFYSeBFj

Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain

這是一個文字檔案的內容。

------WebKitFormBoundaryYOE7pWLqdFYSeBFj--

     從上述服務收到的資料看出FormData提交的格式,每個欄位以boundary隔開,最後以--結束。而ajax請求,send出去的資料格式是自訂的,一般都是以key=value中間用&連接:

 var req = new XMLHttpRequest();
  var sendData = "user=abc&file=这是一个文本文件的内内容";
  req.open("POST", "upload");
  //发送的数据需要转义,见上面提到的三种格式
  req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  req.send(sendData); 
登入後複製

服务就会收到和send发出去的字符串一模一样的内容,然后再作参数解析,所以就得统一参数的格式:

user=abc&file=这是一个文本文件的内容

从这里可以看出POST本质上并不比GET安全,POST只是没有将数据放在网址传送而已。

考虑到FormData到了IE10才支持,如果要支持较低版本的IE,那么可以借助iframe。

文中一开始就说,默认的form提交会使页面重定向,而重定向的规则在target中指定,可以和a标签一样指定为"_blank",在新窗口中打开;还可以指定为一个iframe,在该iframe中打开。所以可以弄一个隐藏的iframe,将form的target指向这个iframe,当form请求完成时,返回的数据就会由这个iframe显示,正如上面在新页面显示的:"Recieved form data"。请求完成后,iframe加载完成,触发load事件,在load事件的处理函数里,获取该iframe的内容,从而拿到服务返回的数据了!拿到后再把iframe删掉。

在提交按钮的响应函数里,首先创建一个iframe,设置iframe为不可见,然后再添加到文档里:

var iframe = document.createElement("iframe");
  iframe.width = 0;
  iframe.height = 0;
  iframe.border = 0;
  iframe.name = "form-iframe";
  iframe.id = "form-iframe";
  iframe.setAttribute("style", "width:0;height:0;border:none");
  //放到document
  this.form.appendChild(iframe); 
登入後複製

改变form的target为iframe的name值:

 this.form.target = "form-iframe"; 
登入後複製

然后再响应iframe的load事件:

 iframe.onload = function(){
   var img = document.createElement("img");
   //获取iframe的内容,即服务返回的数据
   var responseData = this.contentDocument.body.textContent || this.contentWindow.document.body.textContent;
   img.src = JSON.parse(responseData).path;
   f.insertBefore(img, document.getElementById("_submit"));
   //删掉iframe
   setTimeout(function(){
    var _frame = document.getElementById("form-iframe");
    _frame.parentNode.removeChild(_frame);
   }, 100);
   //如果提示submit函数不存在,请注意form里面是否有id/value为submit的控件
   this.form.submit();
  } 
登入後複製

第二种办法到这里就基本可以了,但是如果看163邮箱或者QQ邮箱上传文件的方式,会发现和上面的两种方法都不太一样。用httpfox抓取请求的数据,会发现上传的内容的格式并不是上面说的用boundary隔开,而是直接把文件的内容POST出去了,而文件名、文件大小等相关信息放在了文件的头部。如163邮箱:

POST Data:

this is a text

Headers:

Mail-Upload-name: content.txt
Mail-Upload-size: 15

可以推测它们应该是直接读取了input文件的内容,然后直接POST出去了。要实现这样的功能,可以借助FileReader,读取input文件的内容,再保留二进制的格式发送出去:

 var req = new XMLHttpRequest();
   req.open("POST", "upload");
   //设置和邮箱一样的Content-Type
   req.setRequestHeader("Content-Type", "application/octet-stream");
   var fr = new FileReader();
   fr.onload = function(){
    req.sendAsBinary(this.result);
   }
   req.onload = function(){
     //一样,省略
   }
    //读取input文件内容,放到fileReader的result字段里
   fr.readAsBinaryString(this.form["file"].files[0]); 
登入後複製

代码第13行执行读文件,读取完毕后触发第6行的load响应函数,第7行以二进制文本形式发送出去。由于sendAsBinary的支持性不是很好,可以自行实现一个:

 if(typeof XMLHttpRequest.prototype.sendAsBinary === 'undefined'){
  XMLHttpRequest.prototype.sendAsBinary = function(text){
  var data = new ArrayBuffer(text.length);
  var uia = new UintArray(data, );
  for (var i = ; i < text.length; i++){ 
   uia[i] = (text.charCodeAt(i) & xff);
  }
  this.send(uia);
  }
 } 
登入後複製

代码的关键在于第6行,将字符串转成8位无符号整型,还原二进制文件的内容。在执行了fr.readAsBinaryString之后,二进制文件的内容将会以utf-8的编码以字符串形式存放到result,上面的第6行代码将每个unicode编码转成整型(&0xff或者parseInt),存放到一个8位无符号整型数组里面,第8行把这个数组发送出去。如果直接send,而不是sendAsBinary,服务收到的数据将无法正常还原成原本的文件。

上面的实现需要考虑文件太大,需分段上传的问题。

关于FileReader的支持性,IE10以上支持,IE9有另外一套File API。

文章讨论了3种办法实现无刷新上传文件,分别是使用iframe、FormData和FileReader,支持性最好是的iframe,但是从体验的效果来看FormData和FileReader更好,因为这两者不用生成一个无用的DOM再删除,其中FormData最简单,而FileReader更加灵活。

面给大家介绍iframe无刷新上传文件

form.html
<form enctype="multipart/form-data" method="post" target="upload" action="upload.php" > 
<input type="file" name="uploadfile" />
<input type="submit" /> 
</form> 
<iframe name="upload" style="display:none"></iframe> 
登入後複製

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1317
25
PHP教程
1268
29
C# 教程
1242
24
我該如何裁剪HTML中的IFrame? 我該如何裁剪HTML中的IFrame? Aug 29, 2023 pm 04:33 PM

內嵌框架在HTML中稱為iframe。標籤指定內容中的一個矩形區域,瀏覽器可以在其中顯示帶有捲軸和邊框的不同文件。若要在目前HTML文檔中嵌入另一個文檔,請使用內嵌框架。可以使用HTMLiframe名稱屬性指定元素的參考。在JavaScript中,對元素的參考也是使用name屬性進行的。 iframe本質上用於在目前顯示的網頁中顯示網頁。包含iframe的文件的URL使用「src」屬性指定。語法以下是HTML的語法<iframesrc="URL"title="d

iframe為什麼加載慢 iframe為什麼加載慢 Aug 24, 2023 pm 05:51 PM

iframe載入慢的原因主要包括網路延遲、資源載入時間長、載入順序、快取機制以及安全性策略等。詳細介紹:1、網路延遲,當瀏覽器載入一個包含iframe的網頁時,需要發送請求到伺服器取得iframe中的內容,若網路延遲較高,那麼取得內容的時間就會增加,從而導致iframe載入慢;2.資源載入時間長,資源的大小較大或伺服器回應時間較長時,載入速度會更明顯變慢;3、載入順序等等。

iframe中的data-id是什麼意思 iframe中的data-id是什麼意思 Aug 28, 2023 pm 02:25 PM

iframe中的data-id是指在HTML標籤中使用的自訂屬性,用於儲存特定元素的識別碼。透過使用data-id屬性,可以為iframe元素新增一個唯一的標識符,以便在JavaScript中對其進行操作和存取。 data-id屬性的命名可以根據特定的需求進行自定義,但通常會遵循一些命名規範,以確保其唯一性和易讀性。 data-id屬性也可以用來識別和操作特定的iframe。

微軟:每次造訪時 Outlook 錯誤都會下載「TokenFactoryIframe」文件 微軟:每次造訪時 Outlook 錯誤都會下載「TokenFactoryIframe」文件 Apr 19, 2023 am 08:25 AM

當用戶透過Safari瀏覽器存取電子郵件服務時,微軟的Outlook正在macOS上下載一個名為「TokenFactoryIframe」的神秘檔案。發現Outlook在每次造訪時下載的「TokenFactoryIframe」檔案的使用者現在已廣泛報告此問題。 Outlook每隔幾秒鐘或至少每次造訪Apple平台上的Outlook時都會下載此神祕檔案。根據我們的調查結果,這似乎是由發佈到Outlook的伺服器端更新錯誤所引起的問題,與Safari或macOS無關。微軟在一份

什麼技術可以取代iframe 什麼技術可以取代iframe Aug 24, 2023 pm 01:53 PM

可以取代iframe的技術有Ajax、JavaScript庫或框架、Web元件技術、前端路由和伺服器端渲染等。詳細介紹:1、Ajax是一種用來建立動態網頁的技術。它可以透過在後台與伺服器進行資料交換,實現頁面的非同步更新,而無需刷新整個頁面,使用Ajax可以更靈活地載入和顯示內容,不再需要使用iframe來嵌入其他頁面;2、JavaScript庫或框架,如React等等。

iframe有哪些載入事件 iframe有哪些載入事件 Aug 28, 2023 pm 01:55 PM

iframe的載入事件有onload事件、onreadystatechange事件、onbeforeunload事件、onerror事件、onabort事件等。詳細說明:1、onload事件,指定載入iframe完成後要執行的JavaScript程式碼;2、onreadystatechange事件,指定當iframe狀態變更時要執行的JavaScript程式碼等等。

iframe中的危險在哪裡 iframe中的危險在哪裡 Sep 08, 2023 pm 03:14 PM

iframe中的危險主要有:1、安全漏洞,惡意的網頁可以透過iframe載入其他網頁,並進行一些攻擊行為;2、同源策略突破,透過在iframe中載入其他網域下的網頁,能突破同源策略,實現跨域通信,這可能會被惡意攻擊;3、程式碼執行問題,在iframe中載入的網頁可以執行JS程式碼,這可能導致一些安全性問題;4、SEO問題,搜尋引擎可能無法正確解析和索引透過iframe載入的內容等等。

Python中iframe是什麼意思 Python中iframe是什麼意思 Aug 25, 2023 pm 03:24 PM

Python中iframe是一種HTML標籤,用於在網頁中嵌入另一個網頁或文件。在Python中,可以使用各種函式庫和框架來處理和操作iframe,其中最常用的是BeautifulSoup函式庫,可以輕鬆地從一個網頁中提取iframe的內容,並對其進行操作和處理。掌握如何處理和操作iframe對於Web開發和資料抓取都是非常有用的。

See all articles