Ajax:修訂版本之間的差異

出自六年制學程
跳轉到: 導覽搜尋
FormData 類別
檔案上傳程式範例
 
(未顯示同用戶所作出之76次版本)
第 1 行: 第 1 行:
==HTTP 中的 form-data Content-Type==
+
===總覽===
 +
====(一)非同步事件====
 +
#JavaScript 一次僅能做一件事情,遇到非同步的事件時,就會將非同步的事件移動到程式碼的最後方,等到所有的原始碼運行完以後才會執行非同步的事件。
 +
#非同步事件如:
 +
#*setTimeout( ()=>{console.log('非同步事件');} ,秒數); 其中的 setTimeout()
 +
#*fetch(後送目的地,後送選項);
 +
#* Ajax物件.send(後送資料);
 +
====(二)後送圖解====
 +
<img src='http://jendo.org/uploadFiles/學習資源/資訊/表單資料後送總覽.png' width='780'/>
  
==FormData 類別==
+
====(三)簡易 QA====
 +
Q: Promise 跟 Ajax 有什麼關係?
  
==資料後送==
+
Ajax 可以向伺服器傳送及取得資料,並且不需要重新整理瀏覽器畫面,這樣可以大幅提升使用者體驗並且減少伺服器負擔(僅處理資料,畫面由前端處理)。
===etable 舊版===
+
  
 +
Ajax 是一個透過 JavaScript 技術名稱,用於取得遠端資料;而 Promise 則是一個語法,專門用來處理非同步行為,並不是專門用來處理 Ajax 使用,所以兩者是不同的。
  
  
 +
Q: Promise 與 Async、Await 有什麼關係?<br/>Promise 是用來優化非同步的語法,而 Async、Await 可以 <strong>基於 Promise</strong> 讓非同步的語法的結構類似於「同步語法」,更易讀且好管理。
  
  
 +
Q: 請問 Promise 很常用到嗎?是否一定要學呢?<br/>使用頻率高,必學。
  
 +
===HTTP 中的 Content-Type: multipart/form-data===
 +
常見的傳輸格式:
 +
#Content-Type: application/x-www-form-urlencoded 用 變數1=變數值&變數2=變數值 的方式傳資料
 +
#Content-Type: application/json 代表請求內容是 JSON
 +
#Content-Type: image/png 代表請求內容是圖片檔
 +
#Content-Type: multipart/form-data 使用 (RFC7578) 規範,用一個請求傳送複數個資料格式,主要用於表單或實作檔案上傳。可以用 HTML 的 form 標籤指示 enctype='multipart/form-data' 屬性(配合 Submit),或 JavaScript 的 FormData 類別(配合 onclick)。
 +
#*multipart/form-data 也是 HTTP 請求的一種
 +
#*只要符合格式不用瀏覽器也可以發送請求
 +
#*請求只是將一坨二進制數傳至伺服器,檔案內容必須在伺服器端解析
 +
#表單當中使用 GET 方法送出,那麼所有表單的內容都以 url encoded 的方式被傳送。HTML 點擊 Submit 按鈕後會變成「請求目的地?name=變數值&file=變數值」,就算 enctype 指定 multipart/form-data 還是會以「 application/x-www-form-urlencoded」的形式送出。
 +
以下先看瀏覽器發送的一個 HTTP POST multipart/form-data 請求:<pre>POST 目的地 HTTP/1.1
 +
Host: localhost:3000
  
 +
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFYGn56LlBDLnAkfd
 +
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36
  
 +
------WebKitFormBoundaryFYGn56LlBDLnAkfd
 +
Content-Disposition: form-data; name="name"
  
 +
Test
 +
------WebKitFormBoundaryFYGn56LlBDLnAkfd
 +
Content-Disposition: form-data; name="file"; filename="text.txt"
 +
Content-Type: text/plain
  
==檔案上傳==
+
Hello World
 +
------WebKitFormBoundaryFYGn56LlBDLnAkfd--</pre>
  
 +
'''boundary 的作用與格式:'''
 +
#指示每個資料的界限在哪裡
 +
#boundary 的格式:
 +
#*開頭是兩個 hypen
 +
#*總長度在 70 以內(不包含 hypen 本身)
 +
#*只接受 ASCII 7bit
 +
#*最後一個 boundary 則會再以兩個 hypen 當作結尾
 +
#boundary 之下的處理:
 +
#*Content-Disposition: form-data; name="欄名"<br/>空白行<br/>欄位內容
 +
#*如果是檔案:<br/>Content-Disposition: form-data; name="欄名"; filename="檔名"<br/>Content-Type: text/plain<br/>空白行<br/>欄位內容(同時也是檔案內容)
 +
#*如果是圖片檔或是其他非文字檔(舉 png 圖檔為例):<br/>Content-Disposition: form-data; name="file"; filename="image.png"<br/>Content-Type: image/png<br/>空白行<br/>圖檔二進制內容
  
 +
===FormData 類別===
 +
====(一)初始一個 FormData====
 +
取得整個表單物件:
 +
<pre>var form = document.getElementById(form的ID);</pre>
 +
 +
<pre>var form = document.querySelector('form'); // 取第一個 form</pre>
  
 +
取得整個表單物件的數據:
 +
<pre>var formData = new FormData(form); // 將 form 數據放入 formData</pre>
 +
 +
<pre>var formData = new FormData(form); // 造一個空的 formData</pre>
  
 +
====(二)FormData 操作與取值的方法====
 +
#FormData.append(<field_name>, <field_value>, [<filename>]):新增欄位
 +
#FormData.delete(<field_name>):刪除欄位
 +
#FormData.set(<field_name>, <field_value>, [<filename>]):更新既存的欄位,尚未建立就新增
 +
#FormData.get(<field_name>):取得該欄位的第一個值
 +
#FormData.getAll(<field_name>):回傳陣列,取得該欄位的所有值
 +
#FormData.entries():取得所有欄位的名稱和值
 +
#FormData.keys():取得所有欄位的名稱
 +
#FormData.values():取得所有欄位的值
 +
#:for(let [key, value] of formData.entries()) {
 +
#:&emsp;console.log(key+ ', '+ value);
 +
#:}
 +
#FormData.has(<field_name>):檢驗表單中是否有該欄位,回傳 true 或 false
  
 +
====(三)fetch 後送請求====
 +
#fetch(後送的目的地,後送選項)
 +
#後送選項是一個物件,包含用什麼通道送?後送什麼?<br/>fetch(url,{method:'POST',body:FormData物件})
  
===前台程式===
+
===檔案上傳程式範例===
<pre><!DOCTYPE html>  
+
====(一)無 form 標籤====
<html>  
+
#不翻頁
<head>  
+
#無法顯示後台程式的回應
  <title> Ajax JavaScript File Upload Example </title>  
+
'''前台程式'''
</head>  
+
<pre><!DOCTYPE html>
<body>
+
<html>
  <!-- HTML5 Input Form Elements -->
+
<head>
  <input id="fileupload" type="file" name="formData" />  
+
<title> Ajax JavaScript File Upload Example </title>
  <button id="upload-button" onclick="uploadFile()"> Upload </button>
+
</head>
 +
<body>
 +
<!-- HTML5 Input Form Elements -->
 +
<input id='fileupload' type='file' />
 +
<button onclick='uploadFile()'> Upload </button>
  
  <!-- Ajax JavaScript File Upload Logic -->
+
<!-- Ajax JavaScript File Upload Logic -->
  <script>
+
<script>
  async function uploadFile() {
+
async function uploadFile() {
  let formData = new FormData();  
+
let formData = new FormData();
  formData.append("file", fileupload.files[0]);
+
formData.append('file', fileupload.files[0]);
  await fetch('./upload.php', {
+
await fetch('./upload.php', {method: 'POST',body: formData});
    method: "POST",  
+
alert('The file has been uploaded successfully.');
    body: formData
+
}
  });  
+
</script>
  alert('The file has been uploaded successfully.');
+
</body>
  }
+
  </script>
+
 
+
</body>  
+
 
</html></pre>
 
</html></pre>
 +
說明:
 +
#表單元素沒有 form 標籤、沒有 action 指示,完全靠 javascript 的 fetch 指示出被請求的後台程式,執行時也不會將後台程式的回應顯示取代目前的頁
 +
#兩個表單元素:一個負責讓使用者選上傳檔,另一個負責驅動 javascript 的 uploadFile() 函式
 +
#*前台的 &lt;button&gt; 與 &lt;input type='button'&gt; 等效,前者可以夾圖,後者只能表現按鈕上的字
 +
#*type='file' 不需要 name 屬性,只須要 id 屬性讓 javascript 叫用
 +
#*button 更是連 id 屬性都不用,只要可以 onclick 就行
 +
#uploadFile() 是一個非同步函式,其中的 fetch 動作,須等伺服器處理完才能繼續往下走
 +
#async 和 await 相搭配,後者指出要等伺服器處理完的非同步動作。可以使得本應移到程式碼的最後方,等到所有的原始碼運行完以後才會執行的非同步的事件,如同步事件一樣先被執行:
 +
#*async function 用來定義一個非同步函式,讓這個函式雖為非同步,但其內部以「同步的方式」運行「非同步程式碼」。
 +
#*await 則是可以暫停非同步函式的運行(中止 Promise 的運行),直到非同步動作進入 resolve 或 reject,當接收完回傳值後繼續
 +
#後送到 $_FILES 通道的變數名由 append 指定,與 input 的 name 屬性無關
 +
#後送到 $_FILES 通道的檔案實體數據,由 input 的選擇動作決定,所以 &lt;input type='file'&gt; 要留 id 屬性
 +
#fileupload 和 document.getElementById('fileupload') 等效
 +
#let 宣告的變數生命週期只在區塊 {…} 之內
 +
#formData.append(欄位, 欄值);
 +
#fetch(後送請求目的地,後送選項)<br/>後送選項是一個物件,包含怎麼後送、後送什麼兩個項
 +
#注意時間, alert('The file has been uploaded successfully.'); 會等檔案上傳完才執行
  
===對應的後台程式 upload.php===
+
'''對應的後台程式 upload.php'''
 
<pre><?php
 
<pre><?php
 
 
/* Get the name of the uploaded file */
 
/* Get the name of the uploaded file */
 
$filename = $_FILES['file']['name'];
 
$filename = $_FILES['file']['name'];
  
 
/* Choose where to save the uploaded file */
 
/* Choose where to save the uploaded file */
$location = "./upload/".$filename;
+
$location = './upload/'.$filename;
  
 
/* Save the uploaded file to the local filesystem */
 
/* Save the uploaded file to the local filesystem */
if ( move_uploaded_file($_FILES['file']['tmp_name'], $location) ) {  
+
if(move_uploaded_file($_FILES['file']['tmp_name'],$location)){  
  echo 'Success';  
+
echo 'Success';  
} else {  
+
}else{  
  echo 'Failure';  
+
echo 'Failure';  
 
}
 
}
 +
?></pre>
 +
說明:
 +
#將上傳檔放到 ./upload/ 路徑下
 +
#並以 echo 回應成功或失敗,但此回應不會反映在前台程式執行上,因為前台沒有要處理回應
  
 +
'''整合前後台程式為:'''
 +
<pre><?php
 +
// 前置處理
 +
if(isset($_FILES['file']['name'])){
 +
if(move_uploaded_file($_FILES['file']['tmp_name'],'./上傳資料夾/'.$_FILES['file']['name'])){
 +
echo 'Success';
 +
}else{
 +
echo 'Failure';
 +
}
 +
}else{
 +
$str="<!DOCTYPE html>
 +
<html>
 +
<head>
 +
<title> Ajax JavaScript File Upload Example </title>
 +
</head>
 +
<body>
 +
<input type='file' id='fileupload' />
 +
<button onclick='uploadFile()'> Upload </button>
 +
<!--<input type='button' name='button' value='Upload' id='upload-button' onclick='uploadFile()'/>-->
 +
<script>
 +
async function uploadFile() {
 +
let formData = new FormData();
 +
alert(fileupload);
 +
formData.append('file',fileupload.files[0]);
 +
await fetch('".$_SERVER['PHP_SELF']."',{method:'POST',body:formData});
 +
alert('The file has been uploaded successfully.');
 +
}
 +
</script>
 +
</body>
 +
</html>";
 +
echo $str;
 +
}
 +
?></pre>
 +
 +
====(二)無 form 標籤,用 fetch 後送,將後台回應塞入 Div====
 +
<pre><?php
 +
// 前置處理
 +
if(isset($_FILES['file']['name'])){
 +
if(move_uploaded_file($_FILES['file']['tmp_name'],'./上傳資料夾/'.$_FILES['file']['name'])){
 +
echo 'Success';
 +
}else{
 +
echo 'Failure';
 +
}
 +
}else{
 +
$str="<!DOCTYPE html>
 +
<html>
 +
<head>
 +
<title> Ajax JavaScript File Upload Example </title>
 +
</head>
 +
<body>
 +
<input type='file' id='fileupload' />
 +
<button onclick='uploadFile()'> Upload </button>
 +
<!--<input type='button' name='button' value='Upload' id='upload-button' onclick='uploadFile()'/>-->
 +
<script>
 +
async function uploadFile() {
 +
let formData = new FormData();
 +
formData.append('file',fileupload.files[0]);
 +
await fetch('".$_SERVER['PHP_SELF']."',{method:'POST',body:formData}).then((response)=>{return response.text();}).then((responseText)=>{document.getElementById('list').innerHTML=responseText;});
 +
}
 +
</script>
 +
<div id='list' name='list'></div>
 +
</body>
 +
</html>";
 +
echo $str;
 +
}
 +
?></pre>
 +
說明:
 +
#fetch 之後取得回應,回應是一個物件(object),然後用 then 方法處理這個物件
 +
#(response)=>{return response.text();}:將回應物件代入變數 response ,然後執行 {…} 。執行內容就是:對 response 取方法
 +
text() 再返回。
 +
#其返回物是一段文字,,然後再用 then 方法處理這個返回物
 +
#(responseText)=>{document.getElementById('list').innerHTML=responseText;}:將返回物文字片段塞入 ID 為 list 的標籤中。
 +
 +
====(三)無 form 標籤,不用 fetch 而改用 ajax 後送,將後台回應塞入 Div====
 +
實體檔案在 DS218+/study/uploadAjax.php
 +
<pre><?php
 +
// 前置處理
 +
if(isset($_FILES['file']['name'])){
 +
if(move_uploaded_file($_FILES['file']['tmp_name'],'./上傳資料夾/'.$_FILES['file']['name'])){
 +
echo 'Success';
 +
}else{
 +
echo 'Failure';
 +
}
 +
}else{
 +
$str="<!DOCTYPE html>
 +
<html>
 +
<head>
 +
<title> Ajax JavaScript File Upload Example </title>
 +
</head>
 +
<body>
 +
<input type='file' id='fileupload' />
 +
<button onclick='uploadFile()'> Upload </button>
 +
<!--<input type='button' name='button' value='Upload' id='upload-button' onclick='uploadFile()'/>-->
 +
<script>
 +
var ahr;
 +
if(window.XMLHttpRequest){ahr=new XMLHttpRequest();}
 +
else{ahr = new ActiveXObject('Microsoft.XMLHTTP');}
 +
function uploadFile() {
 +
let formData = new FormData();
 +
formData.append('file',fileupload.files[0]);
 +
ahr.open('POST','".$_SERVER['PHP_SELF']."',true);
 +
ahr.onreadystatechange=function(){
 +
if(ahr.readyState==4 && ahr.status==200){
 +
document.getElementById('list').innerHTML=ahr.responseText;
 +
}
 +
}
 +
ahr.send(formData);
 +
}
 +
</script>
 +
<div id='list' name='list'></div>
 +
</body>
 +
</html>";
 +
echo $str;
 +
}
 +
?></pre>
 +
說明:
 +
#造 ajax 請求物件 ahr
 +
#用 ahr.send(formData)
 +
#使用 FormData 後,會將 request 的 Content-Type 設定為「multipart/form-data」
 +
 +
====(四)用 iframe 配合 target 使 form 後送不翻頁====
 +
<pre><?php
 +
// 前置處理
 +
if(isset($_FILES['file']['name'])){
 +
if(move_uploaded_file($_FILES['file']['tmp_name'],'./上傳資料夾/'.$_FILES['file']['name'])){
 +
echo 'Success';
 +
}else{
 +
echo 'Failure';
 +
}
 +
}else{
 +
$str="<!DOCTYPE html>
 +
<html>
 +
<body>
 +
<form target='list' id='col_form' method='post' enctype='multipart/form-data'>
 +
<input type='file' name='file'/>
 +
<button onclick='uploadFile('col_form','list')'> Upload </button>
 +
</form>
 +
<script>
 +
async function uploadFile(formID,divID){
 +
let formData = new FormData(document.getElementById(formID));
 +
await fetch('".$_SERVER['PHP_SELF']."',{method:'POST',body:formData}).then((response)=>{return response.text();}).then((responseText)=>{document.getElementById(divID).innerHTML=responseText;});
 +
}
 +
</script>
 +
<iframe id='list' name='list'></iframe>
 +
</body>
 +
</html>";
 +
echo $str;
 +
}
 
?></pre>
 
?></pre>

2023年3月23日 (四) 17:23的最新修訂版本

總覽

(一)非同步事件

  1. JavaScript 一次僅能做一件事情,遇到非同步的事件時,就會將非同步的事件移動到程式碼的最後方,等到所有的原始碼運行完以後才會執行非同步的事件。
  2. 非同步事件如:
    • setTimeout( ()=>{console.log('非同步事件');} ,秒數); 其中的 setTimeout()
    • fetch(後送目的地,後送選項);
    • Ajax物件.send(後送資料);

(二)後送圖解

(三)簡易 QA

Q: Promise 跟 Ajax 有什麼關係?

Ajax 可以向伺服器傳送及取得資料,並且不需要重新整理瀏覽器畫面,這樣可以大幅提升使用者體驗並且減少伺服器負擔(僅處理資料,畫面由前端處理)。

Ajax 是一個透過 JavaScript 技術名稱,用於取得遠端資料;而 Promise 則是一個語法,專門用來處理非同步行為,並不是專門用來處理 Ajax 使用,所以兩者是不同的。


Q: Promise 與 Async、Await 有什麼關係?
Promise 是用來優化非同步的語法,而 Async、Await 可以 基於 Promise 讓非同步的語法的結構類似於「同步語法」,更易讀且好管理。


Q: 請問 Promise 很常用到嗎?是否一定要學呢?
使用頻率高,必學。

HTTP 中的 Content-Type: multipart/form-data

常見的傳輸格式:

  1. Content-Type: application/x-www-form-urlencoded 用 變數1=變數值&變數2=變數值 的方式傳資料
  2. Content-Type: application/json 代表請求內容是 JSON
  3. Content-Type: image/png 代表請求內容是圖片檔
  4. Content-Type: multipart/form-data 使用 (RFC7578) 規範,用一個請求傳送複數個資料格式,主要用於表單或實作檔案上傳。可以用 HTML 的 form 標籤指示 enctype='multipart/form-data' 屬性(配合 Submit),或 JavaScript 的 FormData 類別(配合 onclick)。
    • multipart/form-data 也是 HTTP 請求的一種
    • 只要符合格式不用瀏覽器也可以發送請求
    • 請求只是將一坨二進制數傳至伺服器,檔案內容必須在伺服器端解析
  5. 表單當中使用 GET 方法送出,那麼所有表單的內容都以 url encoded 的方式被傳送。HTML 點擊 Submit 按鈕後會變成「請求目的地?name=變數值&file=變數值」,就算 enctype 指定 multipart/form-data 還是會以「 application/x-www-form-urlencoded」的形式送出。
以下先看瀏覽器發送的一個 HTTP POST multipart/form-data 請求:
POST 目的地 HTTP/1.1
Host: localhost:3000

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFYGn56LlBDLnAkfd
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36

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

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

Hello World
------WebKitFormBoundaryFYGn56LlBDLnAkfd--

boundary 的作用與格式:

  1. 指示每個資料的界限在哪裡
  2. boundary 的格式:
    • 開頭是兩個 hypen
    • 總長度在 70 以內(不包含 hypen 本身)
    • 只接受 ASCII 7bit
    • 最後一個 boundary 則會再以兩個 hypen 當作結尾
  3. boundary 之下的處理:
    • Content-Disposition: form-data; name="欄名"
      空白行
      欄位內容
    • 如果是檔案:
      Content-Disposition: form-data; name="欄名"; filename="檔名"
      Content-Type: text/plain
      空白行
      欄位內容(同時也是檔案內容)
    • 如果是圖片檔或是其他非文字檔(舉 png 圖檔為例):
      Content-Disposition: form-data; name="file"; filename="image.png"
      Content-Type: image/png
      空白行
      圖檔二進制內容

FormData 類別

(一)初始一個 FormData

取得整個表單物件:

var form = document.getElementById(form的ID);

var form = document.querySelector('form');	// 取第一個 form

取得整個表單物件的數據:

var formData = new FormData(form);	// 將 form 數據放入 formData

var formData = new FormData(form);	// 造一個空的 formData

(二)FormData 操作與取值的方法

  1. FormData.append(<field_name>, <field_value>, [<filename>]):新增欄位
  2. FormData.delete(<field_name>):刪除欄位
  3. FormData.set(<field_name>, <field_value>, [<filename>]):更新既存的欄位,尚未建立就新增
  4. FormData.get(<field_name>):取得該欄位的第一個值
  5. FormData.getAll(<field_name>):回傳陣列,取得該欄位的所有值
  6. FormData.entries():取得所有欄位的名稱和值
  7. FormData.keys():取得所有欄位的名稱
  8. FormData.values():取得所有欄位的值
    for(let [key, value] of formData.entries()) {
     console.log(key+ ', '+ value);
    }
  9. FormData.has(<field_name>):檢驗表單中是否有該欄位,回傳 true 或 false

(三)fetch 後送請求

  1. fetch(後送的目的地,後送選項)
  2. 後送選項是一個物件,包含用什麼通道送?後送什麼?
    fetch(url,{method:'POST',body:FormData物件})

檔案上傳程式範例

(一)無 form 標籤

  1. 不翻頁
  2. 無法顯示後台程式的回應

前台程式

<!DOCTYPE html>
<html>
<head>
	<title> Ajax JavaScript File Upload Example </title>
</head>
<body>
	<!-- HTML5 Input Form Elements -->
	<input id='fileupload' type='file' />
	<button onclick='uploadFile()'> Upload </button>

	<!-- Ajax JavaScript File Upload Logic -->
	<script>
	async function uploadFile() {
		let formData = new FormData();
		formData.append('file', fileupload.files[0]);
		await fetch('./upload.php', {method: 'POST',body: formData});
		alert('The file has been uploaded successfully.');
	}
	</script>
</body>
</html>

說明:

  1. 表單元素沒有 form 標籤、沒有 action 指示,完全靠 javascript 的 fetch 指示出被請求的後台程式,執行時也不會將後台程式的回應顯示取代目前的頁
  2. 兩個表單元素:一個負責讓使用者選上傳檔,另一個負責驅動 javascript 的 uploadFile() 函式
    • 前台的 <button> 與 <input type='button'> 等效,前者可以夾圖,後者只能表現按鈕上的字
    • type='file' 不需要 name 屬性,只須要 id 屬性讓 javascript 叫用
    • button 更是連 id 屬性都不用,只要可以 onclick 就行
  3. uploadFile() 是一個非同步函式,其中的 fetch 動作,須等伺服器處理完才能繼續往下走
  4. async 和 await 相搭配,後者指出要等伺服器處理完的非同步動作。可以使得本應移到程式碼的最後方,等到所有的原始碼運行完以後才會執行的非同步的事件,如同步事件一樣先被執行:
    • async function 用來定義一個非同步函式,讓這個函式雖為非同步,但其內部以「同步的方式」運行「非同步程式碼」。
    • await 則是可以暫停非同步函式的運行(中止 Promise 的運行),直到非同步動作進入 resolve 或 reject,當接收完回傳值後繼續
  5. 後送到 $_FILES 通道的變數名由 append 指定,與 input 的 name 屬性無關
  6. 後送到 $_FILES 通道的檔案實體數據,由 input 的選擇動作決定,所以 <input type='file'> 要留 id 屬性
  7. fileupload 和 document.getElementById('fileupload') 等效
  8. let 宣告的變數生命週期只在區塊 {…} 之內
  9. formData.append(欄位, 欄值);
  10. fetch(後送請求目的地,後送選項)
    後送選項是一個物件,包含怎麼後送、後送什麼兩個項
  11. 注意時間, alert('The file has been uploaded successfully.'); 會等檔案上傳完才執行

對應的後台程式 upload.php

<?php
/* Get the name of the uploaded file */
$filename = $_FILES['file']['name'];

/* Choose where to save the uploaded file */
$location = './upload/'.$filename;

/* Save the uploaded file to the local filesystem */
if(move_uploaded_file($_FILES['file']['tmp_name'],$location)){ 
	echo 'Success'; 
}else{ 
	echo 'Failure'; 
}
?>

說明:

  1. 將上傳檔放到 ./upload/ 路徑下
  2. 並以 echo 回應成功或失敗,但此回應不會反映在前台程式執行上,因為前台沒有要處理回應

整合前後台程式為:

<?php
// 前置處理
if(isset($_FILES['file']['name'])){
	if(move_uploaded_file($_FILES['file']['tmp_name'],'./上傳資料夾/'.$_FILES['file']['name'])){
		echo 'Success';
	}else{
		echo 'Failure';
	}
}else{
	$str="<!DOCTYPE html> 
<html> 
	<head> 
		<title> Ajax JavaScript File Upload Example </title> 
	</head> 
	<body>
		<input type='file' id='fileupload' />
		<button onclick='uploadFile()'> Upload </button>
	<!--<input type='button' name='button' value='Upload' id='upload-button' onclick='uploadFile()'/>-->
		<script>
		async function uploadFile() {
			let formData = new FormData(); 
			alert(fileupload);
			formData.append('file',fileupload.files[0]);
			await fetch('".$_SERVER['PHP_SELF']."',{method:'POST',body:formData});
			alert('The file has been uploaded successfully.');
		}
		</script>
	</body> 
</html>";
	echo $str;
}
?>

(二)無 form 標籤,用 fetch 後送,將後台回應塞入 Div

<?php
// 前置處理
if(isset($_FILES['file']['name'])){
	if(move_uploaded_file($_FILES['file']['tmp_name'],'./上傳資料夾/'.$_FILES['file']['name'])){
		echo 'Success';
	}else{
		echo 'Failure';
	}
}else{
	$str="<!DOCTYPE html> 
<html> 
	<head> 
		<title> Ajax JavaScript File Upload Example </title> 
	</head> 
	<body>
		<input type='file' id='fileupload' />
		<button onclick='uploadFile()'> Upload </button>
	<!--<input type='button' name='button' value='Upload' id='upload-button' onclick='uploadFile()'/>-->
		<script>
		async function uploadFile() {
			let formData = new FormData(); 
			formData.append('file',fileupload.files[0]);
			await fetch('".$_SERVER['PHP_SELF']."',{method:'POST',body:formData}).then((response)=>{return response.text();}).then((responseText)=>{document.getElementById('list').innerHTML=responseText;});
		}
		</script>
	<div id='list' name='list'></div>
	</body> 
</html>";
	echo $str;
}
?>

說明:

  1. fetch 之後取得回應,回應是一個物件(object),然後用 then 方法處理這個物件
  2. (response)=>{return response.text();}:將回應物件代入變數 response ,然後執行 {…} 。執行內容就是:對 response 取方法

text() 再返回。

  1. 其返回物是一段文字,,然後再用 then 方法處理這個返回物
  2. (responseText)=>{document.getElementById('list').innerHTML=responseText;}:將返回物文字片段塞入 ID 為 list 的標籤中。

(三)無 form 標籤,不用 fetch 而改用 ajax 後送,將後台回應塞入 Div

實體檔案在 DS218+/study/uploadAjax.php

<?php
// 前置處理
if(isset($_FILES['file']['name'])){
	if(move_uploaded_file($_FILES['file']['tmp_name'],'./上傳資料夾/'.$_FILES['file']['name'])){
		echo 'Success';
	}else{
		echo 'Failure';
	}
}else{
	$str="<!DOCTYPE html> 
<html> 
	<head> 
		<title> Ajax JavaScript File Upload Example </title> 
	</head> 
	<body>
		<input type='file' id='fileupload' />
		<button onclick='uploadFile()'> Upload </button>
	<!--<input type='button' name='button' value='Upload' id='upload-button' onclick='uploadFile()'/>-->
		<script>
		var ahr;
		if(window.XMLHttpRequest){ahr=new XMLHttpRequest();}
		else{ahr = new ActiveXObject('Microsoft.XMLHTTP');}
		function uploadFile() {
			let formData = new FormData(); 
			formData.append('file',fileupload.files[0]);
			ahr.open('POST','".$_SERVER['PHP_SELF']."',true);
			ahr.onreadystatechange=function(){
				if(ahr.readyState==4 && ahr.status==200){
					document.getElementById('list').innerHTML=ahr.responseText;
				}
			}
			ahr.send(formData);
		}
		</script>
	<div id='list' name='list'></div>
	</body> 
</html>";
	echo $str;
}
?>

說明:

  1. 造 ajax 請求物件 ahr
  2. 用 ahr.send(formData)
  3. 使用 FormData 後,會將 request 的 Content-Type 設定為「multipart/form-data」

(四)用 iframe 配合 target 使 form 後送不翻頁

<?php
// 前置處理
if(isset($_FILES['file']['name'])){
	if(move_uploaded_file($_FILES['file']['tmp_name'],'./上傳資料夾/'.$_FILES['file']['name'])){
		echo 'Success';
	}else{
		echo 'Failure';
	}
}else{
	$str="<!DOCTYPE html> 
<html> 
<body>
	<form target='list' id='col_form' method='post' enctype='multipart/form-data'>
		<input type='file' name='file'/>
		<button onclick='uploadFile('col_form','list')'> Upload </button>
	</form>
	<script>
	async function uploadFile(formID,divID){
		let formData = new FormData(document.getElementById(formID));
		await fetch('".$_SERVER['PHP_SELF']."',{method:'POST',body:formData}).then((response)=>{return response.text();}).then((responseText)=>{document.getElementById(divID).innerHTML=responseText;});
	}
	</script>
	<iframe id='list' name='list'></iframe>
</body> 
</html>";
	echo $str;
}
?>