Tag Archive upload

curl: ส่งไฟล์

และแล้วก็มาถึงจุดที่รอคอย การส่งไฟล์ด้วย curl เหมือนที่ user upload ไฟล์เข้าเว็บ เหมาะกับการเอาไปเขียนโปรแกรมทรานสเฟอร์ไฟล์จากเว็บหนึ่งไปอีกที่หนึ่ง หรือจะเขียน bot ส่งไฟล์ออกไปเก็บเป็นข้อมูลสำรองก็ได้

[code language=”php” title=”curl_file.php”]<!doctype html>
<html>

<head>
<meta charset="utf-8">
<title>CURL: send file</title>
<link href="../vendor/twbs/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css">
</head>

<body>
<div class="container">
<?php
if (count($_FILES) || count($_POST)) {
$url = ‘http://localhost/snippets/PHP/variables.php’;

if (count($_FILES)) {
$file_name_with_full_path = $_FILES[‘avatar’][‘tmp_name’];

if (function_exists(‘curl_file_create’)) {
/* php 5.5+ */
$_POST[‘avatar’] = curl_file_create($file_name_with_full_path);
} else {
$_POST[‘avatar’] = ‘@’ . realpath($file_name_with_full_path);
}
}

$ch = curl_init();

curl_setopt_array($ch, [
CURLOPT_ENCODING => ‘UTF-8’,
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $_POST,
//CURLOPT_POSTFIELDS => http_build_query($_POST),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_URL => $url,
]);

$result = curl_exec($ch);
curl_close($ch);

echo $result;
}
?>
<form action="curl_file.php" enctype="multipart/form-data" method="post">
<div class="form-group">
<label for="name">Name:</label>
<input class="form-control" id="name" name="name" type="text">
</div>
<div class="form-group">
<label for="avatar">Avatar:</label>
<input accept="image/gif, image/jpeg, image/x-png" class="form-control" id="avatar" name="avatar" type="file">
</div>
<div class="form-group">
<label for="address1">text address:</label>
<input class="form-control" id="address1" name="address[d]" type="text">
</div>
<div class="form-group">
<label for="address2">text address 2:</label>
<input class="form-control" id="address2" name="address[f]" type="text">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</body>

</html>[/code]

แต่เพราะ bug ของ function http_build_query ไม่สามารถ encoded query string ในกรณีที่มีไฟล์ได้อย่างถูกต้อง ทำให้ต้องส่งค่าฟอร์มไปโดยไม่เข้ารหัส ดังนั้น ค่าที่เป็น array จึงส่งค่าออกไปผิด[code language=”php” title=”function http_build_query bug”][address]Array
(
[name] => pitt phunsanit
[address] => Array
)[/code]

จากที่ค้นหาข้อมูลดู ยังไม่มีวิธีที่แก้ปัญหานี้ได้โดยไม่มีผลอาการข้างเคียง คงต้องรอให้ทางทีมงานพัฒนา PHP แก้ปัญหาให้ ตอนนี้ก็พยามหลีกเลี่ยงการส่งข้อมูลแบบเป็นอาร์เรไปก่อน อาจจะใช้ implode รวมข้อมูลก่อนส่งไปก็ได้

PHP: upload ไฟล์ขนาดใหญ่

การอัพโหลดไฟล์ขนาดใหญ่เกินกว่าที่อนุญาตไว้ใน php.ini สามารถแก้ได้หลายวิธี

  • กำหนดใหม่เฉพาะที่ใช้

    เพิ่ม code เข้าไปก่อนที่จะดำเนินการกับไฟล์ขนาดใหญ่เป็นพิเศษ วิธีนี้จะมีผลแค่ในส่วนที่เรากำหนดเท่านั้นเป็นวิธีที่ปลอดภัยที่สุด[code language=”php” title=”ini_set”]<?php
    ini_set(‘max_execution_time’, ‘300’);
    ini_set(‘post_max_size’, ’64M’);
    ini_set(‘upload_max_size’, ’64M’);[/code]

  • ใช้ไฟล์ .htaccess

    ใช้ได้กับเซิฟเวอร์ที่ใช้ apache เท่านั้น กำหนดโดยใส่ไว้ในไฟล์นามสกุล .htaccess[code language=”text” title=”.htaccess”]RewriteEngine On

    # if not root directory
    #RewriteBase /

    # upload larage file
    php_value max_execution_time 300
    php_value max_input_time 300
    php_value post_max_size 64M
    php_value upload_max_filesize 64M[/code]

  • กำหนดใน php.ini

    จะมีผลกับทุกๆส่วนในเซิฟเวอร์ เพราะฉะนั้น ถ้าไม่ใช่ server ของตัวเองหรือเป็น private server แล้วอาจจะไม่สามารถใช้วิธีนี้ เพราะผู้ดูแลระบบมักจะไม่ให้แก้ไขในส่วนนี้ ยกเว้นในบาง server เช่น fatcow ยอมให้แก้ไขค่าตั้งต้นได้ แต่เซิฟเวอร์ไทยไม่เคยเจอที่ไหนยอมให้แก้ไข[code language=”text” title=”php.ini”]max_execution_time = 300
    post_max_size = 64M
    upload_max_filesize = 64M[/code]

การทดสอบดูว่าสามารถกำหนดค่าใหม่ได้จริงๆ รึเปล่าทำได้โดยเรียกใช้ function phpinfo() ค่าที่เปลี่ยนจะอยู่ใน column Local Value แถว max_execution_time, post_max_size, และ upload_max_filesize

Custom Rules Validation

ตัวอย่างการเขียน custom rule validation[code language=”php” title=”\application\controllers\FormValidationCustomRules.php”]<?php
class FormValidationCustomRules extends CI_Controller
{

public function __construct()
{
parent::__construct();
// Your own constructor code
}

public function uploadJPEG()
{
$this->form_validation->set_rules(‘cover’, ‘JPEG Upload’, ‘trim|xss_clean’);

if ($this->form_validation->run() == true) {
$fileDir = ‘assets/contents/’;
$fileName = date(‘U’) . ‘.jpeg’;

$config = [
‘allowed_types’ => ‘jfi|jfif|jif|jpe|jpeg|jpg’,
‘file_ext_tolower’ => true,
‘file_name’ => $fileName,
‘max_size’ => ‘10000’,
‘overwrite’ => false,
‘upload_path’ => $fileDir,
];

$this->load->library(‘upload’, $config);

if ($this->upload->do_upload(‘cover’, false)) {
$this->uploadFile = $fileDir . $fileName;
return true;
} else {
$this->form_validation->set_message(‘uploadJPEG’, $data[‘error’] = $this->upload->display_errors());

if ($_FILES[‘cover’][‘error’] != 4) {
return false;
}
}
}

return false;
}

public function index()
{
$this->load->library(‘form_validation’);

$formValidation = [
[
‘field’ => ‘cover’,
‘label’ => ‘JPEG Upload’,
‘rules’ => ‘callback_uploadJPEG’,
],
];
$this->form_validation->set_rules($formValidation);

if ($this->form_validation->run() == true) {
echo ‘upload file to ‘, $this->uploadFile, ‘<br><img alt="’, $this->uploadFile, ‘" class="img-thumbnail" src="../’, $this->uploadFile, ‘">’;
}

$this->load->view(‘FormValidation/CustomRules’);
}
}
[/code]

หลักการคือ

  • สร้าง function ขึ้นมา แล้วเรียกใช้โดยมี callback_ นำหน้า
  • ส่ง message กลับมาโดยใช้ $this->form_validation->set_message(‘function name’,’ข้อความ’)

พรีวิวรูปที่อัพโหลด

ถ้าเวลาเรา upload รูปขึ้น server คงจะดีถ้าเราเห็นว่าเราเลือกภาพถูกรึเปล่า หรือขนาดมันใหญ่เกินไปมั๋ย เราทำได้โดยการแสดงภาพ Preview ขนาด ความกว้าง ความสูง และทำการตรวจสอบก่อนจะได้ไม่เสียเวลา

ตัวอย่าง jQuery Upload Image Preview Demo
[code language=”html”]
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery Upload Image Preview</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<label>เฉพาะรูปภาพ
<input type="file" name="pic" accept="image/*">
</label>
</form>
<img id="picPreview">
<dl>
<dt>image dimensions width</dt>
<dd id="picDimensionsWidth"></dd>
<dt>image dimensions height</dt>
<dd id="picDimensionsHeight"></dd>
<dt>lastModified</dt>
<dd id="picLastModified"></dd>
<dt>lastModifiedDate</dt>
<dd id="picLastModifiedDate"></dd>
<dt>name</dt>
<dd id="picName"></dd>
<dt>original file upload path</dt>
<dd id="picPath"></dd>
<dt>size</dt>
<dd id="picSize"></dd>
<dt>type</dt>
<dd id="picType"></dd>
</dl>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
$(‘[name="pic"]’).change(function(){

/* original file upload path */
$(‘#picPath’).text($(this).val());

var file = this.files[0];
/* get picture details */
$(‘#picLastModified’).text(file.lastModified);
$(‘#picLastModifiedDate’).text(file.lastModifiedDate);
$(‘#picName’).text(file.name);
$(‘#picSize’).text(file.size);
$(‘#picType’).text(file.type);

/* set image preview */
if( ! file.type.match(/image.*/))
{
return true;
}
var reader = new FileReader();
reader.onload = function (event)
{
$(‘#picPreview’).attr(‘src’, event.target.result);

/* get image dimensions */
var image = new Image();
image.src = reader.result;
image.onload = function()
{
$(‘#picDimensionsWidth’).text(image.width);
$(‘#picDimensionsHeight’).text(image.height);
};

}
reader.readAsDataURL(file);

});
</script>
</body>
</html>
[/code]

เลือกชนิดไฟล์อัพโหลด

คุณสมบัติที่น่าสนใจอย่างหนึ่งของ HTML 5 คือเราสามารถกำหนดให้อินพุทยอมรับชนิดไฟล์ตามที่เราต้องการได้ ช่องใส่ภาพประจำตัว ก็น่าจะใส่ได้แค่นามสกุล jpeg อย่างน้อยก็ png หรือ gif แต่ก็มียูเซอร์ copy รูปใส่ word upload เข้ามา สมกับ used เซ่อๆ จริงๆ
เราสามารถกำหนดได้โดยใช้ accept Attribute ตามตัวอย่าง
[code language=”html”]
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>HTML5 Form Input Accept</title>
<style>
label
{
display: block;
}
</style>
</head>
<body>
<form action="" enctype="multipart/form-data">
<label>เฉพาะไฟล์เสียง
<input type="file" name="files[]" accept="audio/*">
</label>
<label>เฉพาะรูปภาพ
<input type="file" name="files[]" accept="image/*">
</label>
<label>เฉพาะวีดีโอ
<input type="file" name="files[]" accept="video/*">
</label>
<label>pdf
<input type="file" name="files[]" accept="application/pdf">
</label>
<label>.gif หรือ .png
<input type="file" name="files[]" accept="image/gif, image/png">
</label>
<label>.jpg
<input type="file" name="files[]" accept="image/pjpeg, image/jpeg">
</label>
<input type="submit">
</form>
</body>
</html>
[/code]

จะสังเกตุว่าเวลาเรา browse file มันจะแสดงเฉพาะนามสกุลที่เรากำหนด แต่ยังเปลี่ยนตัวเลือกไปเป็น All Files (*.*) ได้อยู่ดี มันแค่ช่วยให้เลือกไฟล์ได้ง่ายขึ้น ยังรับประกันไม่ได้อยู่ดีว่าจะได้นามสกุลไฟล์ที่ถูกต้อง หรือไฟล์ที่ส่งมานามสกุลถูกตาม Extension แต่ก็ไม่ได้รับรองว่าไฟล์มันเป็นจริงตามนามสกุลที่ส่งมา จุดที่อันตรายก็เลยต้องมาตรวจชนิดไฟล์ในผั่ง server อยู่เหมือนเดิม

ค่าที่ใส่ใน accept คือ MIME Types ดูเพิ่มเติมได้ที่ The Complete List of MIME Types

ตัวอย่างการอัพโหลดไฟล์โดยใช้พีเอชพี : หลายไฟล์

อ่านก่อน ตัวอย่างการอัพโหลดไฟล์โดยใช้พีเอชพี
การอัพโหลดครั้งละหลายไฟล์จะเขียนให้ใช้ move_uploaded_file หลายๆ บรรทัดก็ได้ครับ แต่คนมาแก้ที่หลังจะบอกว่า “ถึก” (สมมุติ ว่าถ้าต้องแก้เพิ่มอย่างตรวจชนิดไฟล์ หรือจำกัดขนาด ก็ต้องแก้หลายครั้งไปด้วย ถ้ามีซัก 5 ตัวก็สนุกแล้ว)
ก่อนอื่นแก้ส่วนของฟอร์มให้ส่งได้ครั้งละหลายๆ ไฟล์ก่อน
[sourcecode language=”html”]
<form action="uploading.php" method="post" enctype="multipart/form-data">
<br /><input type="file" name="pictures[]" />
<br /><input type="file" name="pictures[]" />
<br /><input type="file" name="pictures[]" />
<br /><input type="file" name="pictures[]" />
<br /><input type="file" name="pictures[]" />
<input type="submit" />
</form>
[/sourcecode]
สังเกตุดู picture โดนเติม s (บอกว่ามีมากกว่า 1 อันนี้ไม่บังคับ เป็นแค่หลักการตั้งชื่อที่ดี) และมี [] เพิ่มขึ้นมา อันนี้เพื่อบอกว่า pictures เป็นอาร์เรย์ มีพรรคพวก ไม่ได้ตัวคนเดียวแล้วนะ
ไฟล์ uploading.php
[sourcecode language=”php”]
if (isset($_FILES[‘pictures’]))
{
foreach ($_FILES[‘pictures’][‘error’] as $index => $value)
{
if ($value == UPLOAD_ERR_OK)
{
move_uploaded_file($_FILES[‘pictures’][‘tmp_name’][$index] ,’uploads/’.$_FILES[‘pictures’][‘name’][$index]);
}
}
}
[/sourcecode]
เราใช้ foreach ทำงานที่ซ้าๆ ให้ สังเกตุว่า $_FILES[‘pictures’] จะมี [$index] เพิ่มขึ้นมา อันนี้คือดัชนีที่จะบอกว่า เรากำลังทำงานกับ pictures ตัวที่เท่าไหร่
เงื่อนไข $value == UPLOAD_ERR_OK ทำหน้าที่ตรวจดูว่า pictures อัพโหลดไฟล์ขึ้นมาหรือว่างเปล่า
ดูเพิ่มเติม

ตัวอย่างการอัพโหลดไฟล์โดยใช้พีเอชพี

ก่อนอื่นเราจะส่งไฟล์เข้าเครื่องเซิร์ฟเวอร์ต้องแก้ฟอร์มโดยเพิ่ม enctype=”multipart/form-data” และส่งไฟล์โดยใช้อินพุทชนิดไฟล์
[sourcecode language=”html”]<form action="uploading.php" method="post" enctype="multipart/form-data">
<br /><input type="file" name="picture" />
</form>
[/sourcecode]
ในการรับค่าพีเอชพีจะใช้ตัวแปร $_FILES ถ้าอินพุทชื่อ picture ก็จะใช้ $_FILES[‘picture’] โดยจะมีสมาชิกหลายตัว

สมชิกตัวแปร $_FILES
$_FILES[‘pictures’][‘tmp_name’] จะบอกว่าไฟล์ของเราตอนนี้เก็บในชื่ออะไร (เพื่อความปลอดภัยพีเอชพี จะเก็บเป็นไฟล์ชั่วคราวไว้ก่อน ป้องกันการอัพโหลดไฟล์ไวรัสเข้าเซิร์ฟเวอร์ ถ้าไม่ทำอะไรกับไฟล์นี่ตามเวลาที่กำหนดจะโดนลบออกไปเอง)

$_FILES[‘picture’][‘name’] จะบอกว่าต้นฉบับชื่ออะไรนามสกุล
$_FILES[‘picture’][‘type’] ประเภทของไฟล์ตามมาตรฐาน MIME ไว้ดูว่าจริงๆ ไฟล์นี้เป็นไฟล์อะไร (ปลอกภัยกว่าตรวจชนิดจากนามสกุล)
$_FILES[‘picture’][‘size’] ขนาดไฟล์
$_FILES[‘picture’][‘error’] error ดูว่าอัพโหลดผิดพลาดรึเปล่า

จากนั้นก็เขียนไฟล์ที่ได้มาโดยใช้ฟังก์ชัน move_uploaded_file(ไฟล์ชั่วคราว ,พาร์ท/ชื่อไฟล์/นามสกุลที่ต้องการ);ไฟล์ uploading.php[sourcecode language=”php”]if (isset($_FILES[‘picture’]))
{ // ตรวจดูก่อนว่าอัพโหลดไฟล์เข้ามาจริงๆ
move_uploaded_file($_FILES[‘picture’][‘name’] ,’uploads/’.$_FILES[‘picture’][‘tmp_name’]);
}[/sourcecode]ลองเขียนตรวจชนิดไฟล์ / จำกัดขนาดดูนะครับ
ดูเพิ่มเติม