PHP ระดับสูง > การอัพโหลด
ฟอร์มอัพโหลด
การทำงานหนึ่งของ PHP ที่มีประโยชน์มากคือ สนับสนุนการอัพโหลด HTTP ตามปกติใช้ฟอร์ม HTML ตามตัวอย่างในภาพ 3.1.1
ภาพ 3.1.1 ฟอร์ม HTML สำหรับโหลดไฟล์
ฟอร์มมีช่องให้ผู้ใช้สามารถป้อนชื่อไฟล์หรือคลิกปุ่ม Browse ค้นหาไฟล์ หลังจากชื่อไฟล์ได้รับการป้อนแล้วผู้ใช้คลิกปุ่ม upload จากนั้นไฟล์จะได้รับการอัพโหลดไปยังแม่ข่ายที่สคริปต์ PHP กำลังคอยอยู่
ในการอัพโหลดไฟล์ ต้องใช้ไวยากรณ์ HTML เฉพาะวัตถุประสงค์ HTML สำหรับฟอร์มนี้ได้รับการแสดงในรายการคำสั่ง 18.1
รายการคำสั่ง 3.1.1 คำสั่งของไฟล์ simple_upload.html
<form action="simple_upload.php" method="post" enctype="multipart/form-data">
<table width="400" border="0">
<tr>
<td width="100">อัพโหลดโดย</td>
<td><input name="username" type="text" size="30" maxlength="60"/>
<input name="MAX_FILE_SIZE" type="hidden" value="50000"/> </td>
</tr>
<tr>
<td>ไฟล์ข้อความ</td>
<td><input name="textfile" type="file" id="textfile"></td>
</tr>
<tr>
<td>ภาพ</td>
<td><input name="imagefile" type="file" id="imagefile"/></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" name="Submit" value="upload"/></td>
</tr>
</table>
</form> |
หมายเหตุ ฟอร์มนี้ใช้ POST การอัพโหลดไฟล์ทำงานได้กับเมธอด PUT ที่สนับสนุนโดย Netscape Composer และ Amaya ซึ่งไม่ทำงานกับ GET
ส่วนการทำงานพิเศษในฟอร์ม คือ
- ใน <form> tag ต้องตั้งค่าคุณลักษณะ enctype="multipart/form-data" เพื่อให้แม่ข่ายทราบว่าไฟล์กำลังมากับสารสนเทศฟอร์มปกติ
- กำหนดขนาดไฟล์อัพโหลดด้วยฟิลด์ MAX_FILE_SIZE เป็นฟิลด์ซ่อน
<input name="MAX_FILE_SIZE" type="hidden" value="50000"/>
- ต้องมี input ของประเภทไฟล์ ดังนี้
<input name="userfile1" type="file" id="textfile">
<input name="userfile1" type="file" id="imagefile">
ชื่อสามารถกำหนดได้แต่ต้องคำนึงว่าชื่อนี้จะใช้ในการเข้าถึงไฟล์จากสคริปต์ PHP ที่เขียนคำสั่ง PHP ติดต่อกับไฟล์
คำสั่งด้านแม่ข่าย
การเข้าถึงไฟล์อัพโหลดสามารถอ่านได้ผ่าน superglobal array ชื่อ $_FILES ที่เก็บ 1 element ด้วยคีย์ชื่อเดียวกับฟิลด์ <input> จากไฟล์ HTML และเก็บค่าเป็นสารสนเทศของการอัพโหลด
ตามตัวอย่างข้างบน ฟอร์ม HTML มีฟิลด์ชื่อ userfile จะส่งผ่านไปยัง PHP ด้วย 4 ตัวแปร
- $userfile เก็บไฟล์ชั่วคราวบนแม่ข่ายเว็บ
- $userfile_name เก็บชื่อไฟล์บนระบบของผู้ใช้
- $userfile_size เก็บขนาดของไฟล์เป็นไบต์
- $userfile_type เก็บประเภท MIME ของไฟล์เช่น text/plain, image/jpg
ค่าที่เข้าถึงได้ $_FILES array เป็นดังนี้
$_FILES['userfile']['tmp_name'] ="z:/web_uploads/phpc7.tmp"
$_FILES['userfile']['name'] = "widelogo.jpg"
$_FILES['userfile']['size'] = "24000"
$_FILES['userfile']['type'] = "image/jpg"
$_FILES['userfile']['error'] = "0"
กระบวนการอัพโหลดของ PHP ไม่ได้วางไฟล์ที่ระบบไฟล์ทันที ถ้าขนาดไฟล์อัพโหลดเล็กกว่าค่ามากที่สุด แม่ข่ายเว็บจะวางไว้ตำแหน่งชั่วคราวตามค่าเริ่มต้นไดเรคทอรีชั่วคราวที่ระบุไว้ของ upload_tmp_dir ในไฟล์ php.ini เมื่อสคริปต์ประมวลผลเสร็จสิ้น ไฟล์นั้นจะถูกลบ
ขั้นตอนการประมวลผลไฟล์อัพโหลด มีดังนี้
- ดูรหัสความผิดพลาดที่เกิดจากการอัพโหลด
- ถ้ารหัสความผิดพลาดชี้ว่าการอัพโหลดเรียบร้อย ให้ทำการตรวจสอบหรือสแกนไวรัส
- จากนั้นให้ย้ายไฟล์ไปยังปลายทางที่ต้องการจัดเก็บ
รหัสความผิดพลาดใน $_FILES array แสดงในตาราง 3.1.2
ตาราง 3.1.2 ค่าของ $_FILES['error']
รหัส |
ค่า |
คำอธิบาย |
UPLOAD_ERR_OK |
1 |
การอัพโหลดเสร็จสมบูรณ์ |
UPLOAD_ERR_INI_SIZE |
2 |
ขนาดไฟล์ใหญ่กว่าค่าของ upload_max_filesize ในไฟล์ php.ini |
UPLOAD_ERR_FORM_SIZE |
3 |
ขนาดไฟล์ใหญ่กว่าค่าที่ระบุในฟิลด์ MAX_FILE_SIZE ของฟอร์ม |
UPLOAD_ERR_PARTIAL |
4 |
ไฟล์อัพโหลดไม่เสร็จสมบูรณ์ (ส่วนใหญ่เกิดจากการใช้เวลามากเกินและถูกตัดออก) |
UPLOAD_ERR_NO_FILE |
5 |
ไม่มีไฟล์อัพโหลดกับคำขอ |
UPLOAD_ERR_NO_TMP_DIR |
6 |
ไม่มีการระบุไดเรคทอรีชั่วคราวในไฟล์ php.ini (รหัสเพิ่มใน php 5.0.3) |
ถ้าไม่มีความผิดพลาด สามารถทำการสแกนไวรัส ตรวจสอบประเภทไฟล์ เช่น ตรวจสอบว่าเป็นประเภทไฟล์ภาพหรือไม่ เป็นต้น
เมื่อทราบว่าไฟล์อยู่ที่ไหนและเรียกว่าอะไร จากนั้นคัดลอกไปยังพื้นที่จัดเก็บในระบบไฟล์ ได้ ฟังก์ชันในการคัดลอกสามารถใช้ copy(), rename() รวมถึง move_uploaded_file() ที่ทำให้มั่นใจว่าการอัพโหลดไปยังแม่ข่ายเว็บ เมื่อสิ้นสุดการประมวลผลสคริปต์ ไฟล์ชั่วคราวจะถูกลบ
สคริปต์ตัวอย่าง simple_upload.php ทำการอัพโหลดไฟล์ข้อความและไฟล์ภาพ ทำการตรวจสอบและแยกการจัดเก็บตามประเภท
ส่วนใหญ่ของสคริปต์นี้คือตรวจสอบความผิดพลาด การอัพโหลดไฟล์เกี่ยวข้องกับความเสี่ยงด้านความปลอดภัย และต้องแบ่งเบาเท่าที่เป็นไปได้ ต้องมีการตรวจไฟล์ที่อัพโหลดเพื่อทำให้มั่นใจว่าปลอดภัย
<?php
function messageError($in_error)
{
switch ($in_error)
{
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
echo "ขนาดไฟล์ใหญ่กว่าข้อกำหนด";
break;
case UPLOAD_ERR_PARTIAL:
echo "การโหลดไฟล์ไม่สำเร็จ";
break;
case UPLOAD_ERR_NO_FILE:
echo "ไม่ได้รับไฟล์";
break;
case UPLOAD_ERR_NO_TMP_DIR:
echo "ไม่มีได้กำหนดไดเรคทอรีอัพโหลด";
break;
default:
echo "ไม่ทราบสาเหตุความผิดพลาด";
break;
}
}
function canUpload($in_err)
{
if ($in_err == UPLOAD_ERR_OK)
return TRUE;
else
return FALSE;
}
function uploadType($filename)
{
$ext = strtolower(pathinfo($filename,PATHINFO_EXTENSION));
switch ($ext)
{
// ตรวจสอบประเภทไฟล์
case 'txt':
return 1;
break;
case 'doc':
return 2;
break;
case 'rtf':
return 2;
break;
case 'jpg': case 'jpeg':
return 3;
break;
case 'gif':
return 3;
break;
case 'png':
return 3;
break;
case 'bmp':
return 3;
break;
default:
echo "ประเภทไฟล์ไม่ถูกต้อง";
return FALSE;
}
}
if (canUpload($_FILES['textfile']['error']))
{
$get_type = uploadType($_FILES['textfile']['name']);
if ($get_type == 1 || $get_type == 2)
moveFile("../data/", $_FILES['textfile']['name'], $_FILES['textfile']['tmp_name']);
}
else
messageError($_FILES['textfile']['error']);
// คำสั่งอัพโหลดไฟล์ภาพ ...
}
?>
ส่วนหลักสคริปต์ คือ
ตรวจสอบความผิดพลาดการอัพโหลดด้วยฟังก์ชัน canUpload() ที่ใช้พารามิเตอร์ คือ $_Files['error'] ถ้าพารามิเตอร์เท่ากับ UPLOAD_ERR_OK ส่งค่าเป็น TRUE กรณีอื่น เป็น FALSE
ตรวจสอบประเภทไฟล์ด้วยฟังก์ชัน uploadType() และประเภทมีผลต่อการจัดเก็บ โดยไฟล์ข้อความเก็บที่ ../data/ และไฟล์ภาพเก็บที่ ../images/
ย้ายไฟล์ไปเก็บตามประเภทไฟล์ที่อ่านได้ด้วยฟังก์ชัน moveFile() ที่ต้องพารามิเตอร์ ไดเรคทอรีปลายทาง ชื่อไฟล์จาก $_Files['name'] และชื่อไฟล์ชั่วคราวจาก $_Files['tmp_name']
การจำกัดขนาดไฟล์
การจำกัดขนาดไฟล์อัพโหลดมีวิธีการที่ดีด้วยการกำหนดฟิลด์ด้วยชื่อ MAX_FILE_SIZE และค่าตามที่ต้องการ ตามปกติเป็นฟิลด์ซ่อนบนฟอร์มส่งค่าจากลูกข่าย ตามตัวอย่างบนฟอร์ม simple_upload.html คือ
<input name="MAX_FILE_SIZE" type="hidden" value="50000"/>
แม่ข่ายจะอ่านจากฟิลด์สำหรับประมวลความผิดพลาด UPLOAD_ERR_FORM_SIZE และต้องกำหนดค่าไว้ก่อน <input type=file>
การอัพโหลดหลายไฟล์
ตามตัวอย่าง simple_upload เป็นการอัพโหลด 2 ไฟล์ที่ใช้ชื่อฟิลด์ต่างกัน การตั้งชื่อที่ดีที่สนับสนุนการทำงานของโปรแกรม เช่น กำหนดชื่อฟิลด์เป็น userfile1, userfile2 เป็นต้น การกำหนดเช่นนี้ ทำให้สามารถเขียนเป็นรอบได้สะดวก ตามตัวอย่าง multi_upload.php ดัดแปลงจาก simple_upload ให้ใช้กับ for loop
<?php
$num_file = 5;
for ($i = 1; $i <= $num_file; $i++)
{
$userfile = "userfile".$i;
if ($_FILES[$userfile] != NULL)
{
if (canUpload($_FILES[$userfile]['error']))
{
$get_type = uploadType($_FILES[$userfile]['name']);
if ($get_type == 1 || $get_type == 2)
moveFile("../data/", $_FILES[$userfile]['name'], $_FILES[$userfile]['tmp_name']);
elseif ($get_type == 3)
moveFile("../images/", $_FILES[$userfile]['name'],
$_FILES[$userfile]['tmp_name']);
}
else
{
messageError($_FILES['textfile']['error']);
}
}
}
?>
ปัญหาร่วม
มีหลายสิ่งที่ต้องสนใจเมื่อทำการอัพโหลด
- ตัวอย่างก่อนสมมติว่าผู้ใช้ได้รับการรับรองมาแล้ว ตามปกติควรให้ผู้ที่ระบบรองได้ให้อัพโหลดบนเว็บ เช่น การลงทะเบียนสมาชิก
- ถ้ายอมให้ผู้ใช้เชื่อถือไม่ได้หรือไม่ได้รับรองให้อัพโหลดไฟล์ ความคิดที่ดีคือ ระวังข้อมูลเหล่านี้จากสคริปต์ประสงค์ร้ายได้รับการอัพโหลดและเรียกใช้ นอกจากประเภทและข้อมูลของไฟล์แล้ว ชื่อไฟล์ที่อัพโหลดควรเปลี่ยนให้ ปลอดภัย
- ถ้ากำลังใช้ Windows Server ให้ใช้ \\ แทนที่ \ ในพาร์ทของไฟล์
- ถ้ามีปัญหาในการทำงานให้ตรวจไฟล์ php.ini ด้วยการตั้งค่าคำสั่ง upload_tmp_dir ให้ชี้ไปยังไดเรคทอรีที่เข้าถึง รวมถึงการปรับคำสั่ง memory_limit ถ้าต้องการอัพโหลดไฟล์ขนาดใหญ่คำสั่งนี้จะหาขนาดไฟล์ใหญ่ที่สุดที่สามารถอัพโหลด
- ถ้า PHP กำลังเรียกใช้ในโหมด safe จะมีข่าวสารความผิดพลาดเกี่ยวกับการเข้าถึงไฟล์ชั่วคราว เรื่องนี้สามารถแก้ไขได้ทั้งการไม่เรียกใช้ในโหมด safe หรือการเขียนสคริปต์ที่ไม่ใช้ PHP ที่คัดลอกไฟล์ไปยังตำแหน่งเข้าถึงได้ จากนั้นประมวลผลสคริปต์นี้จากสคริปต์ PHP
|