Tag Archive upload

Byphunsanit

curl: ส่งไฟล์

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

<!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>

แต่เพราะ bug ของ function http_build_query ไม่สามารถ encoded query string ในกรณีที่มีไฟล์ได้อย่างถูกต้อง ทำให้ต้องส่งค่าฟอร์มไปโดยไม่เข้ารหัส ดังนั้น ค่าที่เป็น array จึงส่งค่าออกไปผิด

[address]Array
(
    [name] => pitt phunsanit
    [address] => Array
)

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

Byphunsanit

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

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

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

    เพิ่ม code เข้าไปก่อนที่จะดำเนินการกับไฟล์ขนาดใหญ่เป็นพิเศษ วิธีนี้จะมีผลแค่ในส่วนที่เรากำหนดเท่านั้นเป็นวิธีที่ปลอดภัยที่สุด

    <?php
    ini_set('max_execution_time', '300');
    ini_set('post_max_size', '64M');
    ini_set('upload_max_size', '64M');
  • ใช้ไฟล์ .htaccess

    ใช้ได้กับเซิฟเวอร์ที่ใช้ apache เท่านั้น กำหนดโดยใส่ไว้ในไฟล์นามสกุล .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
  • กำหนดใน php.ini

    จะมีผลกับทุกๆส่วนในเซิฟเวอร์ เพราะฉะนั้น ถ้าไม่ใช่ server ของตัวเองหรือเป็น private server แล้วอาจจะไม่สามารถใช้วิธีนี้ เพราะผู้ดูแลระบบมักจะไม่ให้แก้ไขในส่วนนี้ ยกเว้นในบาง server เช่น fatcow ยอมให้แก้ไขค่าตั้งต้นได้ แต่เซิฟเวอร์ไทยไม่เคยเจอที่ไหนยอมให้แก้ไข

    max_execution_time = 300
    post_max_size = 64M
    upload_max_filesize = 64M

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

Byphunsanit

Custom Rules Validation

ตัวอย่างการเขียน custom rule validation

<?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');
    }
}

หลักการคือ

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

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

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

ตัวอย่าง jQuery Upload Image Preview Demo

<!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>
Byphunsanit

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

คุณสมบัติที่น่าสนใจอย่างหนึ่งของ HTML 5 คือเราสามารถกำหนดให้อินพุทยอมรับชนิดไฟล์ตามที่เราต้องการได้ ช่องใส่ภาพประจำตัว ก็น่าจะใส่ได้แค่นามสกุล jpeg อย่างน้อยก็ png หรือ gif แต่ก็มียูเซอร์ copy รูปใส่ word upload เข้ามา สมกับ used เซ่อๆ จริงๆ
เราสามารถกำหนดได้โดยใช้ accept Attribute ตามตัวอย่าง

<!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>

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

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

Byphunsanit

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

อ่านก่อน ตัวอย่างการอัพโหลดไฟล์โดยใช้พีเอชพี
การอัพโหลดครั้งละหลายไฟล์จะเขียนให้ใช้ move_uploaded_file หลายๆ บรรทัดก็ได้ครับ แต่คนมาแก้ที่หลังจะบอกว่า “ถึก” (สมมุติ ว่าถ้าต้องแก้เพิ่มอย่างตรวจชนิดไฟล์ หรือจำกัดขนาด ก็ต้องแก้หลายครั้งไปด้วย ถ้ามีซัก 5 ตัวก็สนุกแล้ว)
ก่อนอื่นแก้ส่วนของฟอร์มให้ส่งได้ครั้งละหลายๆ ไฟล์ก่อน

<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>

สังเกตุดู picture โดนเติม s (บอกว่ามีมากกว่า 1 อันนี้ไม่บังคับ เป็นแค่หลักการตั้งชื่อที่ดี) และมี [] เพิ่มขึ้นมา อันนี้เพื่อบอกว่า pictures เป็นอาร์เรย์ มีพรรคพวก ไม่ได้ตัวคนเดียวแล้วนะ
ไฟล์ uploading.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]);
		}
	}
}

เราใช้ foreach ทำงานที่ซ้าๆ ให้ สังเกตุว่า $_FILES[‘pictures’] จะมี [$index] เพิ่มขึ้นมา อันนี้คือดัชนีที่จะบอกว่า เรากำลังทำงานกับ pictures ตัวที่เท่าไหร่
เงื่อนไข $value == UPLOAD_ERR_OK ทำหน้าที่ตรวจดูว่า pictures อัพโหลดไฟล์ขึ้นมาหรือว่างเปล่า
ดูเพิ่มเติม

Byphunsanit

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

ก่อนอื่นเราจะส่งไฟล์เข้าเครื่องเซิร์ฟเวอร์ต้องแก้ฟอร์มโดยเพิ่ม enctype=”multipart/form-data” และส่งไฟล์โดยใช้อินพุทชนิดไฟล์

<form action="uploading.php" method="post" enctype="multipart/form-data">
<br /><input type="file" name="picture" />
</form>

ในการรับค่าพีเอชพีจะใช้ตัวแปร $_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

if (isset($_FILES['picture']))
{ // ตรวจดูก่อนว่าอัพโหลดไฟล์เข้ามาจริงๆ
	move_uploaded_file($_FILES['picture']['name'] ,'uploads/'.$_FILES['picture']['tmp_name']);
}

ลองเขียนตรวจชนิดไฟล์ / จำกัดขนาดดูนะครับ
ดูเพิ่มเติม