File雜談——拖拽異步上傳實現

在前一篇文章《File雜談——拖拽上傳前傳》中我製做了一個靜態的拖拽上傳界面,拖拽文件到顯示區域釋放,能夠顯示拖入文件的基本信息。本文將在此基礎上進一步加工,打造一個完整的拖拽上傳示例php

示例說明

點擊區域選擇文件或直接將文件拖入區域,觸發文件上傳功能,文件將異步發送到服務器。待服務端處理完成後返回基本信息,在頁面中顯示。因爲服務器容量問題,本示例未作文件保存處理,只是簡單的將文件基本信息返回,文件上傳的後端具體處理邏輯須要自行補充。json

新的小夥伴FormData

咱們知道,傳統的文件上傳若是要實現異步的效果,咱們會使用iframe去模擬,或使用flash上傳插件。可是今天,咱們又認識了一位新成員——FromData,它能夠經過js建立表單對象,並能夠向該對象中添加表單數據(字符串、數字、文件等)。再結合咱們熟悉的XMLHttpRequest對象,將表單數據異步提交到服務端,這樣咱們的問題就解決了。後端

下面,咱們來看下核心代碼:瀏覽器

function uploadFile(fs) {
    var len = fs.length;
    for (var i = 0; i < len; i++) {
        sendFile(fs[i]);
    }
}
function sendFile(file) {
    var xhr = new XMLHttpRequest(),
        fd = new FormData();
    fd.append('file', file);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
			// 將服務端返回信息輸出到日誌區(考慮多文件狀況)
            consoleDiv.innerHTML += '<br>' + xhr.responseText;
        }
    };
    xhr.open('POST', './upload.php');
    xhr.send(fd);
}
// 文件控件發生變化時,調用uploadFile函數,觸發上傳功能
file.onchange = function() {
    uploadFile(this.files);
};
// 在區域內釋放拖入文件時,調用文件上傳函數
area.ondrop = function(ev) {
    ev.preventDefault();
    var dt = ev.dataTransfer;
    uploadFile(dt.files);
};

代碼很簡單,再也不作過多闡述。可是這裏我想發表一點我的意見:根據示例咱們不難發現有這麼一個問題——若是用戶都使用拖拽上傳功能,而不使用點擊觸發File控件選擇文件上傳,那麼File控件徹底沒有存在的必要。聯繫上文中我提到的File控件的地位受到威脅這一觀點,我大膽的設想,假如將來的某一項標準中給每一個HTMLElement暴露一個選擇文件的功能接口,那麼拖拽和點選功能將能夠集於一個元素之上,到那時File控件的地位恐怕不只僅是受到威脅,頗有可能退出歷史舞臺!出於File控件視覺效果和交互不統一的角度去考慮,我以爲以上推測仍是有可能的,哈哈~~服務器

雖然示例並未在後端作太多工做,我這裏仍是以PHP爲例,說明一下後端該如何工做。單從示例而言,個人代碼是這樣的:app

$file = $_FILES['file'];
echo json_encode($file);

能夠說是很是簡單了。而咱們在實際應用中每每還會涉及更多更復雜的處理邏輯。最起碼的咱們應該要將tmp_name對應的臨時文件移動到咱們指定的上傳目錄吧。固然,這一過程咱們就會對文件類型進行判斷,大小限制,重命名,數據保存,等等。基本代碼:異步

$file = $_FILES['file'];
$path = './upload';
if ($file['size'] > 2000000) {
    echo '{"error": "1000", "message": "上傳文件大小超限,不能超過xxM"}';
}
$path .= '/file_' . time() . '.png';
// 這裏還可能會存在文件數據保存,新舊名稱關聯等邏輯
move_uploaded_file($file['tmp_name'], $path);

一個神奇的方法sendAsBinary

前面咱們聊到的使用FormData來實現文件異步上傳,在高級瀏覽器中都能正常運做,沒有太大問題。接下來咱們另一個在Firefox實現異步上傳的方法。這個方法,咱們又會交到一個新的朋友——FireReader。FileReader是HTML5新增的一個對象,它能夠訪問用戶本地文件,而且能夠以不一樣格式讀取文件內容。ide

FileReader基本使用

首先咱們來看一下如何建立一個FileReader實例對象,以及它擁有哪些實例方法。在js中建立一個FileReader對象很簡單:函數

var reader = new FileReader();

咱們能夠經過reader對象訪問本地文件,那麼reader對象擁有哪些咱們經常使用的屬性、事件和方法呢?請看如下列表:this

事件

  • onload:文件成功讀完時觸發
  • onloadend:文件讀完時觸發,不管成功與否
  • onloadstart:開始讀取文件時觸發
  • onprogress:文件讀取中,經常使用於獲取讀取進度
  • onabort:文件讀取操做中斷
  • onerror:文件讀取出錯

屬性

  • result:讀取到的文件內容,當讀取操做完成後生效
  • readyState:FileReader對象的當前狀態
  • error:出錯時的錯誤信息

方法

  • abort:中斷文件讀取操做
  • readAsBinaryString:將文件內容讀取爲二進制格式
  • readAsDataURL:將文件內容讀取爲DataURL格式,一般所說的base64格式
  • readAsText:將文件內容讀取爲文本

以上就是FileReader對象最經常使用的內容,下面咱們先看一個小例子:

var rd = new FileReader();
rd.onload = function(ev) {
    console.log(ev.target.result);
};
rd.readAsText(file);

以上代碼中的file參數是一個file對象,可使File控件的files屬性中FileList的一個,也能夠是dataTransfer中files屬性中FileList的一個。

FileReader + sendAsBinary實現異步上傳

認識了FireReader,下面咱們來看一下在Firefox中如何使用FileReader和XMLHttpRequest的sendAsBinary方法實現文件異步上傳。核心代碼以下:

function sendByBinary(file) {
    var xhr = new XMLHttpRequest(),
        reader = new FileReader();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
            consoleDiv.innerHTML += '<br>' + xhr.responseText;
        }
    };
    xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
    xhr.open('POST', './upload.php');
    reader.onload = function(ev) {
		// 將二進制內容發送至服務端
        xhr.sendAsBinary(ev.target.result);
    };
	// 將文件內容讀取爲二進制格式
    reader.readAsBinaryString(file);
}

代碼很簡單,跟FormData的方式差很少,咱們接着看服務端將如何獲取POST過去的文件內容(以PHP爲例):

// 方法一,這個方法須要php.ini開啓支持
$content = $GLOBALS['HTTP_RAW_POST_DATA'];

// 方法二,不須要php.ini設置,內存壓力小
$content = file_get_contents('php://input');

因此綜合起來比較保險的方法:

$content = $GLOBALS['HTTP_RAW_POST_DATA'];
if (empty($content)) {
    $content = file_get_contents('php://input');
}
echo $content; // 輸出文件內容

咱們暫且不說sendAsBinary這種方式目前只有Firefox支持,單從服務器拿到文件內容後該如何處理來講,這種方法顯然沒有使用FormData的方式有優點。由於服務端僅僅拿到了文件內容,並無文件類型和大小等信息,對一些限制邏輯和文件存儲的實現很不友好。

做者博客:百碼山莊

相關文章
相關標籤/搜索