利用FormData物件實現AJAX檔案上傳功能及後端實現

Simmzl發表於2017-12-22

mark
包括HTML基礎設定、CSS介面優化、JS利用FormData物件和AJAX進行上傳、後端接收檔案並儲存到指定路徑以及刪除檔案操作。

FE

HTML

基礎的設定:

<form enctype="multipart/form-data">
  <input id="file" type="file" multiple="multiple" name="file" accept="...">
  <input type="submit" value="上傳">
</form>
複製程式碼

Form的enctype屬性

enctype這個屬性管理的是表單的MIME編碼,它一共有三個屬性:

描述
application/x-www-form-urlencoded 在傳送前編碼所有字元(預設)
multipart/form-data 不對字元編碼,用來制定傳輸資料的特殊型別,如mp3、jpg
text/plain 純文字傳輸

因此,傳輸完整的檔案資料需要multipart/form-data屬性。

Input

value

儲存了使用者指定的檔案的名稱。

type="file"

設定input型別為file。

multiple="multiple"

可多選,不設定為單選。

accept="..."

設定可選檔案的MIME_type。在設定之後點選選擇檔案按鈕會預設顯示符合設定的MIME_type的檔案(存在相容性)。具體的檔案型別對應的MIME型別可以搜尋到,這裡列出我用到的型別:

檔案型別 MIME型別
.txt text/plain
.pdf application/pdf
.doc application/msword
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
.xls application/vnd.ms-excel
.ppt application/vnd.ms-powerpoint
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation

效果

mark

太醜,不能忍...

CSS

預設介面會在選擇檔案按鈕後會跟一個顯示檔名的文字區域,選擇檔案按鈕無法編輯。 我修改的方法是將input框隱藏,再設定一個lable標籤使使用者點選lable標籤時觸發選擇檔案按鈕。

<label for="file">選擇檔案</label>
複製程式碼

效果

mark

JS

在使用from提交時,瀏覽器會向伺服器傳送選中的檔案的內容而不僅僅是傳送檔名。

為安全起見,<input type="file">即file-upload 元素不允許 HTML 作者或 JavaScript 程式設計師指定一個預設的檔名。HTML value 屬性被忽略,並且對於此類元素來說,value屬性是隻讀的,這意味著只有使用者可以輸入一個檔名。當使用者選擇或編輯一個檔名時,file-upload 元素觸發 onchange 事件控制程式碼。

利用form提交會導致頁面重新整理,體驗不好,所以使用AJAX進行檔案上傳是個不錯的選擇。但這需要我們自己來組織通過POST請求傳送的內容。

FormData物件

通過FormData物件可以組裝一組用 XMLHttpRequest傳送請求的鍵/值對。它可以更靈活方便的傳送表單資料,因為可以獨立於表單使用。如果你把表單的編碼型別設定為multipart/form-data ,則通過FormData傳輸的資料格式和表單通過submit() 方法傳輸的資料格式相同。 —— MDN web docs

建立FormData物件

let formData = new FormData();
複製程式碼

向例項化物件中新增檔案欄位

let myFile = document.getElementById('file');
// myFile.file[0]為第一個檔案(單選),多個檔案(多選)則要迴圈新增
formData.append('myFile', myFile.files[0]);
複製程式碼
console.log(myFile.files[0]);
複製程式碼

mark

其中size屬性單位是byte(位元組),即b。

新增自定義欄位

formData.append('username', 'simmzl');
複製程式碼

AJAX傳送

let request = new XMLHttpRequest();
request.open("POST", "http://foo.com/foo.php");
request.send(formData);
複製程式碼

不使用FromData物件

不使用FormData物件的情況下,需要通過AJAX序列化和提交表單 : Using nothing but XMLHttpRequest

PHP

接收

全域性陣列 $_FILES,第一個引數是表單的 input name,第二個下標是 "name", "type", "size", "tmp_name" 或 "error"。可以根據這些屬性做相關限制,如限制檔案大小、檔案型別等

描述
name 檔名
type 檔案的MIME型別
size 以位元組Byte為單位的檔案大小
tmp_name PHP接收檔案會存為暫時檔案,如需儲存到指定路徑還要移動這個暫時檔案才可以
error 1-7代表7種不同錯誤類別以及0代表成功

error: 成功:0(UPLOAD_ERR_OK) 失敗:

  1. 超過了配置檔案上傳檔案的大小
  2. 超過了表單設定上傳檔案的大小
  3. 檔案部分被上傳
  4. 沒有檔案被上傳
  5. 沒有找到臨時目錄
  6. 檔案不可寫
  7. 由於PHP的擴充套件程式中斷了檔案上傳

驗證

上傳檔案是通過POST傳送的,is_uploaded_file(file)函式可以判斷指定的檔案是否是通過 HTTP POST 上傳的,返回布林值。

該函式可以用於確保惡意的使用者無法欺騙指令碼去訪問本不能訪問的檔案,例如 /etc/passwd。 這種檢查顯得格外重要,如果上傳的檔案有可能會造成對使用者或本系統的其他使用者顯示其內容的話。

儲存

上傳的檔案暫存在tmp_name中,需要使用move_uploaded_file(file,newlocation)將上傳的檔案移動到指定路徑,返回布林值。

if( move_uploaded_file($tmp_name, $destination) ) {
  echo "檔案上傳成功";
}else{
  echo "檔案移動失敗";
}
複製程式碼

刪除

使用unlink(filepath)刪除檔案,引數是檔案路徑。

擴充功能

當然除了接收、驗證、儲存和刪除這四個基本操作外,還可以新增諸如將檔案路徑存入資料庫、生成檔案列表等功能,視需求而定。

最初發表於blog.simmzl.cn

相關文章