การใช้งาน ajax สร้าง Drag and Drop เพื่ออัพโหลดไฟล์ อย่างง่าย

บทความใหม่ ปีนี้ โดย Ninenik Narkdee
draganddrop ajax fetch

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ draganddrop ajax fetch

ดูแล้ว 1,080 ครั้ง


เนื้อหานี้จะพามาดูแนวทางการสร้างวิธีการลากไฟล์มาวางในพื้นที่ที่กำหนดแล้วให้ทำการ
อัพโหลดไฟล์นั้นๆ หรือที่เรียกว่าการอัพโหลดไฟล์แบบ drag and drop โดยคำสั่งเกี่ยวกับ
การจัดการวิธีการอัพโหลด เราจะใช้ javascript และในส่วนของคำสั่งทำการอัพโหลดไฟล์ฝั่ง
server เราจะใช้เป็นฟังก์ชั่น php อย่างเเกี่ยวกับการอัพโหลดไฟล์อย่างง่าย
 
1. สร้างส่วนของการกำหนดพื้นที่ของตำแหน่งที่เราจะให้สามารถลากไฟล์มาวาง
 
<style>
.drop-place{
    margin: auto;
    width: 75%;
    height: 50px;
    border: 1px solid red;
    text-align: center;
    padding: 50px;
    font-size: 20px;
}
.drop-place.dragging{
    background: #C8EFD4;
}
.drop-place.dropped{
    background: #6BD089;
}
.drop-guide.process{
    color: yellow;
}
.drop-guide.success{
    color: blue;
}
</style>
<div class="drop-place">
    <span class="drop-guide">ลากไฟล์มาวางที่นี่เพื่ออัพโหลด</span>
</div>
 
2. กำหนดส่วนของ javascript ในการทำงานเมื่อมีการลากไฟล์เข้ามาในพื้นที่ เบื้องต้นจะให้เห็น
รูปแบบการทำงานของเหตุการณ์ต่างๆ ที่จะเกิดขึ้นสำหรับการลากไฟล์มาในพื้นที่เพื่ออัพโหลด
 
<script>
document.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOM fully loaded and parsed");
    const el = document.querySelector(".drop-place");    
    const drop_guide = document.querySelector(".drop-guide");    
    // เมื่อลากไฟล์เข้ามาในพื้นที่ ทำงานครั้งเดียวเมื่อลากไฟล์เข้ามา
    el.addEventListener("dragenter", (event) => {
        event.preventDefault();
        console.log("DragEnter");
        drop_guide.textContent = 'ปล่อยไฟล์เพื่ออัพโหลด';
     //   var drag_image = event.originalEvent.dataTransfer.files;
    });
    // เมื่อลากไฟล์เข้ามาอยู่ด้านบนของพื้นที่ ส่วนนี้จะทำงานตลอดที่ไฟล์ที่ลากยังอยู่ในพื้ที่
    el.addEventListener("dragover", (event) => {
        event.preventDefault();
        console.log("DragOver");
        event.target.classList.add('dragging');
    });
    // เมื่อนำไฟล์ที่ลากออกนอกพื้นที่ จะทำงานครั้งเดียวเมื่่อลากไฟล์ออกนอกพื้นที่
    el.addEventListener("dragleave", (event) => {
        event.preventDefault();
        console.log("DragLeave");
        drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
        event.target.classList.remove('dragging');
    });
    // เมื่อวางหรือปล่อยไฟล์ที่ลากลงในพื้นที่ ทำงานครั้งเดียว
    el.addEventListener("drop", (event) => {
        event.preventDefault();
        console.log("Drop");
        drop_guide.textContent = 'กำลังอัพโหลดไฟล์...';
        event.target.classList.remove('dragging');
        event.target.classList.add('dropped');
    });
});
</script>
 
3. โฟกัสในส่วนของการ drop ไฟล์ 
 
    // เมื่อวางหรือปล่อยไฟล์ที่ลากลงในพื้นที่ ทำงานครั้งเดียว
    el.addEventListener("drop", (event) => {
        event.preventDefault();
        console.log("Drop");
        drop_guide.textContent = 'กำลังอัพโหลดไฟล์...';
        event.target.classList.remove('dragging');
        event.target.classList.add('dropped');
        // ตัวแปรเก็บข้อมูลไฟล์ ที่ลากมาวางเพื่ออัพโหลด
        var image = event.dataTransfer.files;
        console.log(image);
    });
 
เมื่อมีการวางไฟล์เพื่อทำการอัพโหลด เราสามารถดูข้อมูล FileList ที่นำมาวาง ผ่านค่า
event.dataTransfer.files ซึ่งจะเป็นข้อมูลไฟล์อาเรย์ นั่นหมายความว่า เราสามารถลาก
มาวางทีละหลายๆ ไฟล์พร้อมกันได้ แต่ในตัวอย่างนี้เราจะอัพโหลดแค่ไฟล์เดียวก่อน
 
เมื่อเราได้ข้อมูลไฟล์รูปที่จะอัพโหลด ต่อไป เราต้องทำการสร้างฟอร์มข้อมูล ในที่นี้ก็คือ 
ข้อมูล FormData object โดยสร้าง ตัวแปร สำหรับเก็บข้อมูลดังนี้
 
var formData = new FormData();
 
ถ้าเรานึกภาพไม่ออกว่าข้อมูล FormData คืออะไร ให้เรานึกถึงรูปแบบฟอร์มส่งข้อมูลทั่วไป
 
<form action="" method="post">
<input type="text">
<button type="submit">Send</button>
</form>
 
ข้างต้นคือรูปแบบฟอร์มที่สร้างด้วย html แต่การสร้างด้วย javascript จะใช้เป็น new FormData();
ซึ่งจะเป็นการสร้างแค่ฟอร์มข้อมูล ยังไม่มี Element หรือฟิลด์ข้อมูลใดๆ เช่นตัวอย่างด้านบน จะมี input
text เป็นองค์ประกอบ ดังนั้น คำสั่งที่จะเพิ่มองค์ประกอบ หรือ Element ของฟอร์มจะใช้เป็น
 
var formData = new FormData();
formData.append('picture', image[0]);
 
เรากำหนดชื่อฟิลด์เป็น picture เนื่องจากข้อมูลจากตัวแปร image ของเราเป็นอาเรย์รูปภาพ 
ตัวแปรข้อมูลที่ส่งไปก็จะเป็น $_FILES['picture'] และเมื่อเรา จะเลือกเพียงไฟล์เดียว จึงกำหนดเป็นค่า
ที่อาเรย์ 0 หรือค่าแรก เป็น image[0] เป็นค่ารุปภาพที่เรากำหนดให้ตัวแปร $_FILES['picture']
 
4. เมื่อเราได้ข้อมูลฟอร์มสำหรับส่งเรียบร้อยแล้ว ต่อไปก็คือการสร้างฟังก์ชั่น ajax สำหรับการส่งข้อมูล
ในที่นี้เราจะใช้คำสั่ง fetch() ซึ่งคำสั่งนี้เป็นคำสั่งที่มีเรื่องของเวลาที่ต้องรอเข้ามาเกี่ยวข้อง เพราะเป็น
การส่งข้อมูลไปยัง server และต้องรอการทำงาน หรือก็คือการทำงานแบบ async (asynchronous)
 
    // ฟังก์ชั่่ง ajax ส่งข้อมูลไปยัง server และต้องรอข้อมูลกลับมาก่อนนำไปใช้งาน
    async function uploadFormData(formData) {
        const response_async =  await fetch('upload.php?upload', {
            method: "POST",  
            body: formData // ส่งข้อมูลฟอร์ม
        });
        // เพื่อให้ข้อมูลที่ส่งกลับมา สามารถนำไปประยุกต์ใช้ได้ เราจึงให้ส่งเป็น json object
        return response_async.json();
    }   
 
5. เรียกใช้ฟังก์ชั่น ajax สำหรับอัพโหลดหรือส่งข้อมูลไปยัง server เมื่อเราได้ฟังก์ชั่นสำหรับส่งข้อมูล
แล้ว ในการเรียกใช้ฟังก์ชั่น async ก็จะต้องรอข้อมูลส่งกลับมาก่อนถึงจะนำไปประมวลผลหรือทำงานต่อ
ดังนั้นวิธีการใช้งานจะเป็นในรูปแบบดังนี้
 
    uploadFormData(formData) // ส่งข้อมูลไปัยัง server
    .then( response => { // เมื่อส่งข้อมูลกลับมา
        console.log( response ); // JSON data 
        if(response.status=="OK"){
            event.target.classList.remove('dropped');
            drop_guide.classList.remove('process');
            drop_guide.classList.add('success');
            drop_guide.textContent = 'อัพโหลดไฟล์เรียบร้อยแล้ว';
            setTimeout(()=>{
                drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                drop_guide.classList.remove('success');
            },2000);
        }
    });
 
6. ส่วนของไฟล์สำหรับอัพโหลดในฝั่งของ server อย่างง่าย สามารถประยุกต์เพิ่มเติมได้ตามต้องการ
ไฟล์ upload.php
 
<?php
if(isset($_GET['upload'])){

if (!empty($_FILES['picture'])) {
    if (is_uploaded_file($_FILES['picture']['tmp_name'])) {
        $sourcePath = $_FILES['picture']['tmp_name'];
        $targetPath = "uploads/" . $_FILES['picture']['name'];
        if (move_uploaded_file($sourcePath, $targetPath)) {
            $response = array(
                "status"=>"OK"
            );  
        }else{
            $response = array(
                "status"=>"Fail"
            );            
        }
    }else{
        $response = array(
            "status"=>"Fail"
        );
    }
}else{
    $response = array(
        "status"=>"Fail"
    );
}
header("Content-Type: application/json");
echo json_encode($response);		
exit; 
}
?>
ตัวอย่างไฟล์ทดสอบ ajax_draganddrop.php ทั้งหมด
<?php
if(isset($_GET['upload'])){

if (!empty($_FILES['picture'])) {
    if (is_uploaded_file($_FILES['picture']['tmp_name'])) {
        $sourcePath = $_FILES['picture']['tmp_name'];
        $targetPath = "uploads/" . $_FILES['picture']['name'];
        if (move_uploaded_file($sourcePath, $targetPath)) {
            $response = array(
                "status"=>"OK"
            );  
        }else{
            $response = array(
                "status"=>"Fail"
            );            
        }
    }else{
        $response = array(
            "status"=>"Fail"
        );
    }
}else{
    $response = array(
        "status"=>"Fail"
    );
}
header("Content-Type: application/json");
echo json_encode($response);		
exit; 
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

<style>
.drop-place{
    margin: auto;
    width: 75%;
    height: 50px;
    border: 1px solid red;
    text-align: center;
    padding: 50px;
    font-size: 20px;
}
.drop-place.dragging{
    background: #C8EFD4;
}
.drop-place.dropped{
    background: #6BD089;
}
.drop-guide.process{
    color: yellow;
}
.drop-guide.success{
    color: blue;
}
</style>
<div class="drop-place">
    <span class="drop-guide">ลากไฟล์มาวางที่นี่เพื่ออัพโหลด</span>
</div>

<script>
document.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOM fully loaded and parsed");
    const el = document.querySelector(".drop-place");    
    const drop_guide = document.querySelector(".drop-guide");    
    // เมื่อลากไฟล์เข้ามาในพื้นที่ ทำงานครั้งเดียวเมื่อลากไฟล์เข้ามา
    el.addEventListener("dragenter", (event) => {
        event.preventDefault();
    //    console.log("DragEnter");
        drop_guide.textContent = 'ปล่อยไฟล์เพื่ออัพโหลด';
     //   var drag_image = event.originalEvent.dataTransfer.files;
    });
    // เมื่อลากไฟล์เข้ามาอยู่ด้านบนของพื้นที่ ส่วนนี้จะทำงานตลอดที่ไฟล์ที่ลากยังอยู่ในพื้ที่
    el.addEventListener("dragover", (event) => {
        event.preventDefault();
    //    console.log("DragOver");
        event.target.classList.add('dragging');
    });
    // เมื่อนำไฟล์ที่ลากออกนอกพื้นที่ จะทำงานครั้งเดียวเมื่่อลากไฟล์ออกนอกพื้นที่
    el.addEventListener("dragleave", (event) => {
        event.preventDefault();
    //    console.log("DragLeave");
        drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
        event.target.classList.remove('dragging');
    });
    // เมื่อวางหรือปล่อยไฟล์ที่ลากลงในพื้นที่ ทำงานครั้งเดียว
    el.addEventListener("drop", (event) => {
        event.preventDefault();
        console.log("Drop");
        drop_guide.textContent = 'กำลังอัพโหลดไฟล์...';
        drop_guide.classList.add('process');
        event.target.classList.remove('dragging');
        event.target.classList.add('dropped');
        // ตัวแปรเก็บข้อมูลไฟล์ ที่ลากมาวางเพื่ออัพโหลด
        var image = event.dataTransfer.files;
        var formData = new FormData();
        console.log(image);
        formData.append('picture', image[0]);

    uploadFormData(formData) // ส่งข้อมูลไปัยัง server
    .then( response => { // เมื่อส่งข้อมูลกลับมา
        console.log( response ); // JSON data 
        if(response.status=="OK"){
            event.target.classList.remove('dropped');
            drop_guide.classList.remove('process');
            drop_guide.classList.add('success');
            drop_guide.textContent = 'อัพโหลดไฟล์เรียบร้อยแล้ว';
            setTimeout(()=>{
                drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                drop_guide.classList.remove('success');
            },2000);
        }
    });


    });

    // ฟังก์ชั่่ง ajax ส่งข้อมูลไปยัง server และต้องรอข้อมูลกลับมาก่อนนำไปใช้งาน
    async function uploadFormData(formData) {
        const response_async =  await fetch('ajax_draganddrop.php?upload', {
            method: "POST",  
            body: formData // ส่งข้อมูลฟอร์ม
        });
        // เพื่อให้ข้อมูลที่ส่งกลับมา สามารถนำไปประยุกต์ใช้ได้ เราจึงให้ส่งเป็น json object
        return response_async.json();
    }    

});
</script>
    
</body>
</html>


   เพิ่มเติมเนื้อหา ครั้งที่ 1 วันที่ 18-02-2024


การตรวจสอบข้อผิดพลาดกรณีใช้งาน คำสั่ง fetch() ในการส่งข้อมูล

 
ในส่วนนี้จะมาเพิ่มในส่วนของการตรวจสอบเพิ่มเติมกรณีเกิดข้อผิดพลาดระหว่างการ
ส่งข้อมูลด้วยคำสั่ง fetch() เช่น ชื่อไฟล์ไม่ถูกต้อง หรือ ไม่พบไฟล์ หรือกรณี server 
มีปัญหาไม่ตอบสนอง ทั้งหมดนี้ รูปแบบก่อนหน้าจะไม่ได้กำหนดการตรวจสอบไว้

<script>
document.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOM fully loaded and parsed");
    const el = document.querySelector(".drop-place");    
    const drop_guide = document.querySelector(".drop-guide");    
    // เมื่อลากไฟล์เข้ามาในพื้นที่ ทำงานครั้งเดียวเมื่อลากไฟล์เข้ามา
    el.addEventListener("dragenter", (event) => {
        event.preventDefault();
    //    console.log("DragEnter");
        drop_guide.textContent = 'ปล่อยไฟล์เพื่ออัพโหลด';
     //   var drag_image = event.originalEvent.dataTransfer.files;
    });
    // เมื่อลากไฟล์เข้ามาอยู่ด้านบนของพื้นที่ ส่วนนี้จะทำงานตลอดที่ไฟล์ที่ลากยังอยู่ในพื้ที่
    el.addEventListener("dragover", (event) => {
        event.preventDefault();
    //    console.log("DragOver");
        event.target.classList.add('dragging');
    });
    // เมื่อนำไฟล์ที่ลากออกนอกพื้นที่ จะทำงานครั้งเดียวเมื่่อลากไฟล์ออกนอกพื้นที่
    el.addEventListener("dragleave", (event) => {
        event.preventDefault();
    //    console.log("DragLeave");
        drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
        event.target.classList.remove('dragging');
    });
    // เมื่อวางหรือปล่อยไฟล์ที่ลากลงในพื้นที่ ทำงานครั้งเดียว
    el.addEventListener("drop", (event) => {
        event.preventDefault();
        console.log("Drop");
        drop_guide.textContent = 'กำลังอัพโหลดไฟล์...';
        drop_guide.classList.add('process');
        event.target.classList.remove('dragging');
        event.target.classList.add('dropped');
        // ตัวแปรเก็บข้อมูลไฟล์ ที่ลากมาวางเพื่ออัพโหลด
        var image = event.dataTransfer.files;
        var formData = new FormData();
        console.log(image);
        formData.append('picture', image[0]);

        uploadFormData(formData) // ส่งข้อมูลไปัยัง server
        .then( response => { // เมื่อส่งข้อมูลกลับมา
            console.log( response ); // JSON data 
            if(response.status=="OK"){
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');
                drop_guide.classList.add('success');
                drop_guide.textContent = 'อัพโหลดไฟล์เรียบร้อยแล้ว';
                setTimeout(()=>{
                    drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                },3000);
            }else{
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');            
                drop_guide.textContent = response.text;
                setTimeout(()=>{
                    drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                },3000);            
            }
        });


    });

    // ฟังก์ชั่่ง ajax ส่งข้อมูลไปยัง server และต้องรอข้อมูลกลับมาก่อนนำไปใช้งาน
    async function uploadFormData(formData) {
        try{
            const response_async =  await fetch('ajax_draganddrop.php?upload', {
                method: "POST",  
                body: formData // ส่งข้อมูลฟอร์ม
            });
            // ต้อง(ไม่)เป็น 404 หรือ 500 คือ ไต้ไม่เป็น ไม่พบไฟล์ หรือ server ไม่ตอบสนอง
            if(![404,500].includes(response_async.status)){
                return response_async.json();
            }else{ // กรณี ไม่พบไฟล์ 
                var errors = {status:'Fail',text:'เกิดข้อผิดพลาดลองใหม่อีกครั้ง'};
                return errors;
            }
        }catch (error) { // ส่วนใหญ่เป็นกรณี 500 หรือ server ไม่ตอบสนอง
            console.error(error);
            var errors = {status:'Fail',text:'การเชื่อมต่อ server ไม่ตอบสนอง'};
            return errors;
        }
    }    

});
</script>


   เพิ่มเติมเนื้อหา ครั้งที่ 2 วันที่ 18-02-2024


การอัพโหลดทีละหลายๆไฟล์ หรือลากทีละหลายไฟล์มาวาง

 
ก่อนหน้าเราให้ตัวอย่างการอัพโหลดโดยเลือกไฟล์เดียว นั่นคือ รูปแบบเดิม ถึงเราจะเลือกไฟล์
หลายไฟล์มาวาง แต่ก็จะทำการส่งเฉพาะไฟล์แรกไฟล์เดียวเท่านั้นไปอัพโหลด โดยส่งข้อมูลไป
 
formData.append('picture', image[0]);
 
เราใช้ชื่อฟิลด์เป็น picture และเลือกรูปแบบกำหนดเจาะจงให้เป็นรูปแรกด้วยค่า 0 เป็น key
ของอาเรย์รูปที่มาวาง แต่เมื่อเราจะทำการส่งไปทีละหลายๆไฟล์ เราก็จะทำการเปลี่ยนชื่อฟิลด์ให้
เป็นแบบอาเรย์ แล้วทำการวนลูปเพิ่มฟิลด์เข้าไปใน formData ดังนี้
 
for (var i = 0; i < image.length; i++) {
    formData.append('picture[]', image[i]);
}
 
จากนั้นก็ปรับแต่งข้อความตามต้องการเช่น การแจ้งว่ากำลังอัดโหลด (จำนวน) ไฟล์..
 
drop_guide.textContent = 'กำลังอัพโหลด '+image.length+' ไฟล์...';
 
หรือเมื่ออัพโหลดเรียบร้อยแล้วก็กำหนดเป็น อัพโหลด (จำนวน)/(ทั้งหมด) ไฟล์ เรียบร้อยแล้ว
 
drop_guide.textContent = 'อัพโหลด '+response.num_success+'/'+
                        response.num_upload+' ไฟล์ เรียบร้อยแล้ว';
 
และอีกส่วนที่ต้องแก้ไขคือส่วนของฝั่ง server เนื่องจากชื่อฟิลด์มีการแก้ไขให้ส่งแบบ array
ดังนั้นการทำงานก็จะปรับเล็กน้อยเป็นดังนี้
 
<?php
if(isset($_GET['upload'])){

if (!empty($_FILES['picture'])) {    
    if(is_array($_FILES['picture']['name'])){
        $num_success = 0; // จำนวนอัพโหลดสำเร็จ
        $num_upload = count($_FILES['picture']['name']); // จำนวนไฟล์ทั้งหมด

        for($i = 0; $i < $num_upload; $i++){
            if (is_uploaded_file($_FILES['picture']['tmp_name'][$i])) {
                $sourcePath = $_FILES['picture']['tmp_name'][$i];
                $targetPath = "uploads/" . $_FILES['picture']['name'][$i];
                if (move_uploaded_file($sourcePath, $targetPath)) {
                    $num_success++; 
                }
            }
        }
        $response = array(
            "status"=>"OK",
            "num_success"=>$num_success,
            "num_upload"=>$num_upload
        );  
    }else{
        $response = array(
            "status"=>"Fail"
        );    
    }
}else{
    $response = array(
        "status"=>"Fail"
    );
}
header("Content-Type: application/json");
echo json_encode($response);		
exit; 
}
?>
 
โค้ดไฟล์ตัวอย่างทั้งหมด ajax_draganddrop.php
 
<?php
if(isset($_GET['upload'])){

if (!empty($_FILES['picture'])) {    
    if(is_array($_FILES['picture']['name'])){
        $num_success = 0; // จำนวนอัพโหลดสำเร็จ
        $num_upload = count($_FILES['picture']['name']); // จำนวนไฟล์ทั้งหมด

        for($i = 0; $i < $num_upload; $i++){
            if (is_uploaded_file($_FILES['picture']['tmp_name'][$i])) {
                $sourcePath = $_FILES['picture']['tmp_name'][$i];
                $targetPath = "uploads/" . $_FILES['picture']['name'][$i];
                if (move_uploaded_file($sourcePath, $targetPath)) {
                    $num_success++; 
                }
            }
        }
        $response = array(
            "status"=>"OK",
            "num_success"=>$num_success,
            "num_upload"=>$num_upload
        );  
    }else{
        $response = array(
            "status"=>"Fail"
        );    
    }
}else{
    $response = array(
        "status"=>"Fail"
    );
}
header("Content-Type: application/json");
echo json_encode($response);		
exit; 
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

<style>
.drop-place{
    margin: auto;
    width: 75%;
    height: 50px;
    border: 1px solid red;
    text-align: center;
    padding: 50px;
    font-size: 20px;
}
.drop-place.dragging{
    background: #C8EFD4;
}
.drop-place.dropped{
    background: #6BD089;
}
.drop-guide.process{
    color: yellow;
}
.drop-guide.success{
    color: blue;
}
</style>
<div class="drop-place">
    <span class="drop-guide">ลากไฟล์มาวางที่นี่เพื่ออัพโหลด</span>
</div>

<script>
document.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOM fully loaded and parsed");
    const el = document.querySelector(".drop-place");    
    const drop_guide = document.querySelector(".drop-guide");    
    // เมื่อลากไฟล์เข้ามาในพื้นที่ ทำงานครั้งเดียวเมื่อลากไฟล์เข้ามา
    el.addEventListener("dragenter", (event) => {
        event.preventDefault();
    //    console.log("DragEnter");
        drop_guide.textContent = 'ปล่อยไฟล์เพื่ออัพโหลด';
     //   var drag_image = event.originalEvent.dataTransfer.files;
    });
    // เมื่อลากไฟล์เข้ามาอยู่ด้านบนของพื้นที่ ส่วนนี้จะทำงานตลอดที่ไฟล์ที่ลากยังอยู่ในพื้ที่
    el.addEventListener("dragover", (event) => {
        event.preventDefault();
    //    console.log("DragOver");
        event.target.classList.add('dragging');
    });
    // เมื่อนำไฟล์ที่ลากออกนอกพื้นที่ จะทำงานครั้งเดียวเมื่่อลากไฟล์ออกนอกพื้นที่
    el.addEventListener("dragleave", (event) => {
        event.preventDefault();
    //    console.log("DragLeave");
        drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
        event.target.classList.remove('dragging');
    });
    // เมื่อวางหรือปล่อยไฟล์ที่ลากลงในพื้นที่ ทำงานครั้งเดียว
    el.addEventListener("drop", (event) => {
        event.preventDefault();
        console.log("Drop");
        drop_guide.classList.add('process');
        event.target.classList.remove('dragging');
        event.target.classList.add('dropped');
        // ตัวแปรเก็บข้อมูลไฟล์ ที่ลากมาวางเพื่ออัพโหลด
        var image = event.dataTransfer.files;
        drop_guide.textContent = 'กำลังอัพโหลด '+image.length+' ไฟล์...';
        var formData = new FormData();
        console.log(image);
        // วนลูปเพิ่มข้อมูลฟิลด์รูปแบบอาเรย์เข้าไป
        for (var i = 0; i < image.length; i++) {
            formData.append('picture[]', image[i]);
        }
        //formData.append('picture', image[0]);

        uploadFormData(formData) // ส่งข้อมูลไปัยัง server
        .then( response => { // เมื่อส่งข้อมูลกลับมา
            console.log( response ); // JSON data 
            if(response.status=="OK"){
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');
                drop_guide.classList.add('success');
                drop_guide.textContent = 'อัพโหลด '+response.num_success+'/'+
                                        response.num_upload+' ไฟล์ เรียบร้อยแล้ว';
                setTimeout(()=>{
                    drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                },3000);
            }else{
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');            
                drop_guide.textContent = response.text;
                setTimeout(()=>{
                    drop_guide.textContent = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                },3000);            
            }
        });


    });

    // ฟังก์ชั่่ง ajax ส่งข้อมูลไปยัง server และต้องรอข้อมูลกลับมาก่อนนำไปใช้งาน
    async function uploadFormData(formData) {
        try{
            const response_async =  await fetch('ajax_draganddrop.php?upload', {
                method: "POST",  
                body: formData // ส่งข้อมูลฟอร์ม
            });
            // ต้อง(ไม่)เป็น 404 หรือ 500 คือ ไต้ไม่เป็น ไม่พบไฟล์ หรือ server ไม่ตอบสนอง
            if(![404,500].includes(response_async.status)){
                return response_async.json();
            }else{ // กรณี ไม่พบไฟล์ 
                var errors = {status:'Fail',text:'เกิดข้อผิดพลาดลองใหม่อีกครั้ง'};
                return errors;
            }
        }catch (error) { // ส่วนใหญ่เป็นกรณี 500 หรือ server ไม่ตอบสนอง
            console.error(error);
            var errors = {status:'Fail',text:'การเชื่อมต่อ server ไม่ตอบสนอง'};
            return errors;
        }
    }    

});
</script>
    
</body>
</html>


   เพิ่มเติมเนื้อหา ครั้งที่ 3 วันที่ 18-02-2024


การกำหนดให้รองรับทั้งการเลือก browse ไฟล์ และลากไฟล์มาวางเพื่ออัพโหลด

 
เนื้อหาเพิ่มเติมนี้จะทำการเพิ่มเติมความสามารถให้เราเลือกได้ว่า จะลากไฟล์มาวางเพื่อ
อัพโหลดหรือเลือกไฟล์ที่ตำแหน่งที่ต้องการ เพราะบางครั้ง เราก็อาจจะไม่ได้เปิดหน้าไฟล์
ที่จะลากมาวางไว้ ก็ให้สามารถเลือกไฟล์โดยตรงได้ ซึ่งรองรับการเลือกหลายๆ ไฟล์พร้อมกัน
 
วิธีการคือ เราจะมีปุ่มข้อความ สำหรับให้เลือกไฟล์ เมื่อคลิกเลือก ก็จะทำการสร้าง input file
ขึ้นมาเพื่อใช้สำหรับรับไฟล์ที่เลือก โดยเราจะใช้วิธีการซ่อนไว้ เพื่อให้ไม่ต้องเห็น input นั้น
โดยกำหนด div ไว้ซ่อน
 
<style>
.hidden-inputfile{
    width: 1px;
    height: 1px;
    visibility: hidden;
}
</style>
<div class="hidden-inputfile"></div>
 
จากนั้นเมื่อเราทำการคลิกปุ่ม เลือกไฟล์ ก็จะทำการตรวจสอบว่า input file อยู่ก่อนแล้วไหม 
ถ้ายังไม่มีก็ให้ทำการสร้างขึ้นมา จากนั้นให้เกิด event click ขึ้นเพื่อเรืยกหน้าต่างเลือกไฟล์
ขี้นมา หลักจากเลือกไฟล์ ก็นำรายการไฟล์ที่เลือก เข้าไปสู่ขั้นตอนการอัพโหลดไฟล์ ที่ใช้ร่วมกัน
กับแบบของการลากวาง
 
ไฟล์ตัวอย่างทั้งหมด ajax_draganddrop.php 

<?php
if(isset($_GET['upload'])){

if (!empty($_FILES['picture'])) {    
    if(is_array($_FILES['picture']['name'])){
        $num_success = 0; // จำนวนอัพโหลดสำเร็จ
        $num_upload = count($_FILES['picture']['name']); // จำนวนไฟล์ทั้งหมด

        for($i = 0; $i < $num_upload; $i++){
            if (is_uploaded_file($_FILES['picture']['tmp_name'][$i])) {
                $sourcePath = $_FILES['picture']['tmp_name'][$i];
                $targetPath = "uploads/" . $_FILES['picture']['name'][$i];
                if (move_uploaded_file($sourcePath, $targetPath)) {
                    $num_success++; 
                }
            }
        }
        $response = array(
            "status"=>"OK",
            "num_success"=>$num_success,
            "num_upload"=>$num_upload
        );  
    }else{
        $response = array(
            "status"=>"Fail"
        );    
    }
}else{
    $response = array(
        "status"=>"Fail"
    );
}
header("Content-Type: application/json");
echo json_encode($response);		
exit; 
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

<style>
.drop-place{
    margin: auto;
    width: 75%;
    height: 50px;
    border: 1px solid red;
    text-align: center;
    padding: 50px;
    font-size: 20px;
}
.drop-place.dragging{
    background: #C8EFD4;
}
.drop-place.dropped{
    background: #6BD089;
}
.drop-guide.process{
    color: yellow;
}
.drop-guide.success{
    color: blue;
}
.hidden-inputfile{
    width: 1px;
    height: 1px;
    visibility: hidden;
}
</style>
<div class="drop-place">
    <span class="drop-guide"><a href="#" class="choose-image">เลือกไฟล์</a> หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด</span>
</div>
<div class="hidden-inputfile"></div>
<script>
document.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOM fully loaded and parsed");
    const el = document.querySelector(".drop-place");    
    const drop_guide = document.querySelector(".drop-guide");    
    // เมื่อลากไฟล์เข้ามาในพื้นที่ ทำงานครั้งเดียวเมื่อลากไฟล์เข้ามา
    el.addEventListener("dragenter", (event) => {
        event.preventDefault();
    //    console.log("DragEnter");
        drop_guide.innerHTML = 'ปล่อยไฟล์เพื่ออัพโหลด';
     //   var drag_image = event.originalEvent.dataTransfer.files;
    });
    // เมื่อลากไฟล์เข้ามาอยู่ด้านบนของพื้นที่ ส่วนนี้จะทำงานตลอดที่ไฟล์ที่ลากยังอยู่ในพื้ที่
    el.addEventListener("dragover", (event) => {
        event.preventDefault();
    //    console.log("DragOver");
        event.target.classList.add('dragging');
    });
    // เมื่อนำไฟล์ที่ลากออกนอกพื้นที่ จะทำงานครั้งเดียวเมื่่อลากไฟล์ออกนอกพื้นที่
    el.addEventListener("dragleave", (event) => {
        event.preventDefault();
    //    console.log("DragLeave");
        drop_guide.innerHTML = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
        event.target.classList.remove('dragging');
    });
    // เมื่อวางหรือปล่อยไฟล์ที่ลากลงในพื้นที่ ทำงานครั้งเดียว
    el.addEventListener("drop", (event) => {
        event.preventDefault();
        console.log("Drop");
        drop_guide.classList.add('process');
        event.target.classList.remove('dragging');
        event.target.classList.add('dropped');
        // ตัวแปรเก็บข้อมูลไฟล์ ที่ลากมาวางเพื่ออัพโหลด
        var image = event.dataTransfer.files;
        drop_guide.innerHTML = 'กำลังอัพโหลด '+image.length+' ไฟล์...';
        var formData = new FormData();
        console.log(image);
        // วนลูปเพิ่มข้อมูลฟิลด์รูปแบบอาเรย์เข้าไป
        for (var i = 0; i < image.length; i++) {
            formData.append('picture[]', image[i]);
        }
        //formData.append('picture', image[0]);

        uploadFormData(formData) // ส่งข้อมูลไปัยัง server
        .then( response => { // เมื่อส่งข้อมูลกลับมา
            console.log( response ); // JSON data 
            if(response.status=="OK"){
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');
                drop_guide.classList.add('success');
                drop_guide.innerHTML = 'อัพโหลด '+response.num_success+'/'+
                                        response.num_upload+' ไฟล์ เรียบร้อยแล้ว';
                setTimeout(()=>{
                    drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                },3000);
            }else{
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');            
                drop_guide.innerHTML = response.text;
                setTimeout(()=>{
                    drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                },3000);            
            }
        });


    });

    // ฟังก์ชั่่ง ajax ส่งข้อมูลไปยัง server และต้องรอข้อมูลกลับมาก่อนนำไปใช้งาน
    async function uploadFormData(formData) {
        try{
            const response_async =  await fetch('ajax_draganddrop.php?upload', {
                method: "POST",  
                body: formData // ส่งข้อมูลฟอร์ม
            });
            // ต้อง(ไม่)เป็น 404 หรือ 500 คือ ไต้ไม่เป็น ไม่พบไฟล์ หรือ server ไม่ตอบสนอง
            if(![404,500].includes(response_async.status)){
                return response_async.json();
            }else{ // กรณี ไม่พบไฟล์ 
                var errors = {status:'Fail',text:'เกิดข้อผิดพลาดลองใหม่อีกครั้ง'};
                return errors;
            }
        }catch (error) { // ส่วนใหญ่เป็นกรณี 500 หรือ server ไม่ตอบสนอง
            console.error(error);
            var errors = {status:'Fail',text:'การเชื่อมต่อ server ไม่ตอบสนอง'};
            return errors;
        }
    }   

    // กำหนด event ให้กับปุ่ม เลือกไฟล์ สังเกตว่า เราไม่ได้กำหนดให้กับปุ่มโดยตรง
    // แต่กำหนดในกับ drop-place แทน แล้วค่อยไปตรวจสอบว่า ถ้าตัวที่คลิกเป็นปุ่ม
    // choose-image หรือไม่ ถ้าใช่ ให้ทำงาน ทั้งนี้ก็เพราะว่า ถ้าเรากำหนดโดยตรง
    // ปุ่มที่ถูกสร้างมาจาก javascript จะไม่ทำงาน วิธีนี้จึงเป็นการป้องกันปัญหานั้น
    el.addEventListener("click", (event) => {
        var chooseEl = event.target.closest('.choose-image');
        if (chooseEl) { // คลิกโดยปุ่ม เลือกไฟล์หรือไม่
            createUploadFileInput();
        }
        event.preventDefault();
    });

    // ฟังก์ชั่นสำหรับสร้าง input file ให้เลือก พร้อมให้รองรับการอัพโหลดไฟล์ เมื่อเลือกไฟล์
    function createUploadFileInput() {

        var fileInputDOM = document.querySelector('.image_upload');
        var placeForFileInput = document.querySelector('.hidden-inputfile');

        // ตรวจสอบว่า ถ้ายังไม่มี ให้ทำการสร้างขึ้นมาก่อน
        if (!fileInputDOM) {

            // ทำการสร้าง
            var fileInput = document.createElement('input');

            fileInput.type = 'file';
            fileInput.name = 'picture[]'
            fileInput.classList.add('image_upload');
            fileInput.multiple = true;
            fileInput.accept = 'image/*';
            placeForFileInput.appendChild(fileInput);

            // เมื่อมีการเลือกไฟล์ หรือเปลี่ยนแปลงการเลือกไฟล์
            fileInput.addEventListener("change", (event) => {
                event.preventDefault();
                console.log("choose file");
                var image = fileInput.files; // เก็บตัวแปรไฟล์ที่เลือก
                if(image.length>0){ // ถ้ามีการเลือกไฟล์ 
                    drop_guide.innerHTML = 'กำลังอัพโหลด '+image.length+' ไฟล์...';
                    var formData = new FormData();
                    console.log(image);
                    // วนลูปเพิ่มข้อมูลฟิลด์รูปแบบอาเรย์เข้าไป
                    for (var i = 0; i < image.length; i++) {
                        formData.append('picture[]', image[i]);
                    }
                    //formData.append('picture', image[0]);

                    uploadFormData(formData) // ส่งข้อมูลไปัยัง server
                    .then( response => { // เมื่อส่งข้อมูลกลับมา
                        console.log( response ); // JSON data 
                        if(response.status=="OK"){
                            drop_guide.classList.remove('process');
                            drop_guide.classList.add('success');
                            drop_guide.innerHTML = 'อัพโหลด '+response.num_success+'/'+
                                                    response.num_upload+' ไฟล์ เรียบร้อยแล้ว';
                            setTimeout(()=>{
                                drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                                                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                                drop_guide.classList.remove('success');
                            },3000);
                        }else{
                            drop_guide.classList.remove('process');            
                            drop_guide.innerHTML = response.text;
                            setTimeout(()=>{
                                drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                                                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                                drop_guide.classList.remove('success');
                            },3000);            
                        }
                    });


                }

            });   
            fileInput.click(); // ให้ input file ถูกคลิก หลังจากสร้าง        
        }else{
            fileInputDOM.click(); // ให้ input file ถูกคลิก ตัวที่ถูกสร้างแล้ว
        }

    }    
    

});
</script>
    
</body>
</html>


 


   เพิ่มเติมเนื้อหา ครั้งที่ 4 วันที่ 18-02-2024


กำหนดให้แสดงรูปตัวอย่างไฟล์ที่กำลังอัพโหลด

 
เนื้อหาเพิ่มเติมนี้ เราจะเพิ่มในส่วนของฟังก์ชั่น ให้แสดงรูปตัวอย่าง เพื่อให้เห็นชัดเจนว่า
เราได้ทำการเลือกไฟล์ที่จะอัพโหลดถูกต้องหรือไม่ เป็นการช่วยตรวจสอบเพิ่มเติมอีกที
แนวทางการปรับโค้ด แค่เพิ่มส่วนของฟังก์ชั่น และการแสดงเข้ามา รายละเอียดอยู่ในโค้ด
ตัวอย่างทั้งหมดด้านล่างนี้
 
ไฟล์โค้ดตัวอย่าง ajax_draganddrop.php

<?php
if(isset($_GET['upload'])){

if (!empty($_FILES['picture'])) {    
    if(is_array($_FILES['picture']['name'])){
        $num_success = 0; // จำนวนอัพโหลดสำเร็จ
        $num_upload = count($_FILES['picture']['name']); // จำนวนไฟล์ทั้งหมด

        for($i = 0; $i < $num_upload; $i++){
            if (is_uploaded_file($_FILES['picture']['tmp_name'][$i])) {
                $sourcePath = $_FILES['picture']['tmp_name'][$i];
                $targetPath = "uploads/" . $_FILES['picture']['name'][$i];
                if (move_uploaded_file($sourcePath, $targetPath)) {
                    $num_success++; 
                }
            }
        }
        $response = array(
            "status"=>"OK",
            "num_success"=>$num_success,
            "num_upload"=>$num_upload
        );  
    }else{
        $response = array(
            "status"=>"Fail"
        );    
    }
}else{
    $response = array(
        "status"=>"Fail"
    );
}
header("Content-Type: application/json");
echo json_encode($response);		
exit; 
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

<style>
.drop-place{
    margin: auto;
    width: 75%;
    height: 50px;
    border: 1px solid red;
    text-align: center;
    padding: 50px;
    font-size: 20px;
}
.drop-place.dragging{
    background: #C8EFD4;
}
.drop-place.dropped{
    background: #6BD089;
}
.drop-guide.process{
    color: yellow;
}
.drop-guide.success{
    color: blue;
}
.hidden-inputfile{
    width: 1px;
    height: 1px;
    visibility: hidden;
}
#thumbnail,div.removepic{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}
#thumbnail img{
    width:50px;
    height:50px;
    margin:5px;
}
canvas{
    border:1px solid red;
}    
</style>
<div class="drop-place">
    <span class="drop-guide"><a href="#" class="choose-image">เลือกไฟล์</a> หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด</span>
    <div id="thumbnail"></div>
</div>
<div class="hidden-inputfile"></div>
<script>
document.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOM fully loaded and parsed");
    const el = document.querySelector(".drop-place");    
    const drop_guide = document.querySelector(".drop-guide");    
    const thumbnail = document.querySelector("#thumbnail");    
    // เมื่อลากไฟล์เข้ามาในพื้นที่ ทำงานครั้งเดียวเมื่อลากไฟล์เข้ามา
    el.addEventListener("dragenter", (event) => {
        event.preventDefault();
    //    console.log("DragEnter");
        drop_guide.innerHTML = 'ปล่อยไฟล์เพื่ออัพโหลด';
     //   var drag_image = event.originalEvent.dataTransfer.files;
    });
    // เมื่อลากไฟล์เข้ามาอยู่ด้านบนของพื้นที่ ส่วนนี้จะทำงานตลอดที่ไฟล์ที่ลากยังอยู่ในพื้ที่
    el.addEventListener("dragover", (event) => {
        event.preventDefault();
    //    console.log("DragOver");
        event.target.classList.add('dragging');
    });
    // เมื่อนำไฟล์ที่ลากออกนอกพื้นที่ จะทำงานครั้งเดียวเมื่่อลากไฟล์ออกนอกพื้นที่
    el.addEventListener("dragleave", (event) => {
        event.preventDefault();
    //    console.log("DragLeave");
        drop_guide.innerHTML = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
        event.target.classList.remove('dragging');
    });
    // เมื่อวางหรือปล่อยไฟล์ที่ลากลงในพื้นที่ ทำงานครั้งเดียว
    el.addEventListener("drop", (event) => {
        event.preventDefault();
        console.log("Drop");
        drop_guide.classList.add('process');
        event.target.classList.remove('dragging');
        event.target.classList.add('dropped');
        // ตัวแปรเก็บข้อมูลไฟล์ ที่ลากมาวางเพื่ออัพโหลด
        var image = event.dataTransfer.files;
        showThumbnail(image);
        drop_guide.innerHTML = 'กำลังอัพโหลด '+image.length+' ไฟล์...';
        var formData = new FormData();
        console.log(image);
        // วนลูปเพิ่มข้อมูลฟิลด์รูปแบบอาเรย์เข้าไป
        for (var i = 0; i < image.length; i++) {
            formData.append('picture[]', image[i]);
        }
        //formData.append('picture', image[0]);

        uploadFormData(formData) // ส่งข้อมูลไปัยัง server
        .then( response => { // เมื่อส่งข้อมูลกลับมา
            console.log( response ); // JSON data 
            if(response.status=="OK"){
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');
                drop_guide.classList.add('success');
                drop_guide.innerHTML = 'อัพโหลด '+response.num_success+'/'+
                                        response.num_upload+' ไฟล์ เรียบร้อยแล้ว';
                setTimeout(()=>{
                    drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                    thumbnail.innerHTML = ''; // ล้างข้อมูลรูปตัวอย่างที่แสดง
                },3000);
            }else{
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');            
                drop_guide.innerHTML = response.text;
                setTimeout(()=>{
                    drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                    thumbnail.innerHTML = ''; // ล้างข้อมูลรูปตัวอย่างที่แสดง
                },3000);            
            }
        });


    });

    // ฟังก์ชั่่ง ajax ส่งข้อมูลไปยัง server และต้องรอข้อมูลกลับมาก่อนนำไปใช้งาน
    async function uploadFormData(formData) {
        try{
            const response_async =  await fetch('ajax_draganddrop.php?upload', {
                method: "POST",  
                body: formData // ส่งข้อมูลฟอร์ม
            });
            // ต้อง(ไม่)เป็น 404 หรือ 500 คือ ไต้ไม่เป็น ไม่พบไฟล์ หรือ server ไม่ตอบสนอง
            if(![404,500].includes(response_async.status)){
                return response_async.json();
            }else{ // กรณี ไม่พบไฟล์ 
                var errors = {status:'Fail',text:'เกิดข้อผิดพลาดลองใหม่อีกครั้ง'};
                return errors;
            }
        }catch (error) { // ส่วนใหญ่เป็นกรณี 500 หรือ server ไม่ตอบสนอง
            console.error(error);
            var errors = {status:'Fail',text:'การเชื่อมต่อ server ไม่ตอบสนอง'};
            return errors;
        }
    }   

    // กำหนด event ให้กับปุ่ม เลือกไฟล์ สังเกตว่า เราไม่ได้กำหนดให้กับปุ่มโดยตรง
    // แต่กำหนดในกับ drop-place แทน แล้วค่อยไปตรวจสอบว่า ถ้าตัวที่คลิกเป็นปุ่ม
    // choose-image หรือไม่ ถ้าใช่ ให้ทำงาน ทั้งนี้ก็เพราะว่า ถ้าเรากำหนดโดยตรง
    // ปุ่มที่ถูกสร้างมาจาก javascript จะไม่ทำงาน วิธีนี้จึงเป็นการป้องกันปัญหานั้น
    el.addEventListener("click", (event) => {
        var chooseEl = event.target.closest('.choose-image');
        if (chooseEl) { // คลิกโดยปุ่ม เลือกไฟล์หรือไม่
            createUploadFileInput();
        }
        event.preventDefault();
    });

    // ฟังก์ชั่นสำหรับสร้าง input file ให้เลือก พร้อมให้รองรับการอัพโหลดไฟล์ เมื่อเลือกไฟล์
    function createUploadFileInput() {

        var fileInputDOM = document.querySelector('.image_upload');
        var placeForFileInput = document.querySelector('.hidden-inputfile');

        // ตรวจสอบว่า ถ้ายังไม่มี ให้ทำการสร้างขึ้นมาก่อน
        if (!fileInputDOM) {

            // ทำการสร้าง
            var fileInput = document.createElement('input');

            fileInput.type = 'file';
            fileInput.name = 'picture[]'
            fileInput.classList.add('image_upload');
            fileInput.multiple = true;
            fileInput.accept = 'image/*';
            placeForFileInput.appendChild(fileInput);

            // เมื่อมีการเลือกไฟล์ หรือเปลี่ยนแปลงการเลือกไฟล์
            fileInput.addEventListener("change", (event) => {
                event.preventDefault();
                console.log("choose file");
                var image = fileInput.files; // เก็บตัวแปรไฟล์ที่เลือก
                if(image.length>0){ // ถ้ามีการเลือกไฟล์ 
                    showThumbnail(image);
                    drop_guide.innerHTML = 'กำลังอัพโหลด '+image.length+' ไฟล์...';
                    var formData = new FormData();
                    console.log(image);
                    // วนลูปเพิ่มข้อมูลฟิลด์รูปแบบอาเรย์เข้าไป
                    for (var i = 0; i < image.length; i++) {
                        formData.append('picture[]', image[i]);
                    }
                    //formData.append('picture', image[0]);

                    uploadFormData(formData) // ส่งข้อมูลไปัยัง server
                    .then( response => { // เมื่อส่งข้อมูลกลับมา
                        console.log( response ); // JSON data 
                        if(response.status=="OK"){
                            drop_guide.classList.remove('process');
                            drop_guide.classList.add('success');
                            drop_guide.innerHTML = 'อัพโหลด '+response.num_success+'/'+
                                                    response.num_upload+' ไฟล์ เรียบร้อยแล้ว';
                            setTimeout(()=>{
                                drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                                                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                                drop_guide.classList.remove('success');
                                thumbnail.innerHTML = ''; // ล้างข้อมูลรูปตัวอย่างที่แสดง
                            },3000);
                        }else{
                            drop_guide.classList.remove('process');            
                            drop_guide.innerHTML = response.text;
                            setTimeout(()=>{
                                drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                                                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                                drop_guide.classList.remove('success');
                                thumbnail.innerHTML = ''; // ล้างข้อมูลรูปตัวอย่างที่แสดง
                            },3000);            
                        }
                    });


                }

            });   
            fileInput.click(); // ให้ input file ถูกคลิก หลังจากสร้าง        
        }else{
            fileInputDOM.click(); // ให้ input file ถูกคลิก ตัวที่ถูกสร้างแล้ว
        }

    }    

    // ฟังก์ชั่นวนรูปสร้างรูปตัวอย่างที่กำลังอัพโหลด
    function showThumbnail(files){
   
       for(var i=0;i<files.length;i++){
           var file = files[i]
           var fileName = file.name;
           var imageType = /image.*/
           if(!file.type.match(imageType)){
               //     console.log("Not an Image");
               continue;
           }
  
           var image = document.createElement("img");
           var wrapImg = document.createElement("div");
           var thumbnail = document.getElementById("thumbnail");
           wrapImg.setAttribute("data-file", fileName);
           image.file = file;
           wrapImg.appendChild(image);
           thumbnail.appendChild(wrapImg);
  
           var reader = new FileReader();
           reader.onload = (function(aImg){
               return function(e){
                   aImg.src = e.target.result;
               };
           }(image))
  
           var ret = reader.readAsDataURL(file);
           var canvas = document.createElement("canvas");
           ctx = canvas.getContext("2d");
           image.onload= function(){
               ctx.drawImage(image,50,50)
           }
       } // end for loop
  
   } // end showThumbnail    
    

});
</script>
    
</body>
</html>


   เพิ่มเติมเนื้อหา ครั้งที่ 5 วันที่ 20-02-2024


การใช้งาน Axios แทน fetch เพื่อรองรับการแสดง Progress การอัพโหลด

 
เนื่องจากคำสั่ง fetch ไม่รองรับการแสดง progress หรือสถานะความก้าวหน้า
ในการอัพโหลด หรือเข้าใจง่ายๆ คือว่าอัพโหลดได้คิดเป็นกี่เปอร์เช็นต์แล้ว แบบนี้เป็นต้น
จึงมีตัวช่วยที่เราสามารถนำมาประยุกต์ใช้งานกับโค้ดเดิมเราได้ง่าย โดยปรับเพียงเล็กน้อย
เท่านั้น ก็คือใช้ Axios https://axios-http.com/docs/intro วิธีการก็คือเรียกใช้งาน
ไฟล์ axios.min.js โดยเพิ่มโค้ดส่วนนี้เข้าไป
 
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
 
 แล้วเปลี่ยน การเรียกใช้คำสั่ง fetch เป็น axios ดังนี้
 
    const response_async =  await fetch('ajax_draganddrop.php?upload', {
        method: "POST",  
        body: formData // ส่งข้อมูลฟอร์ม
    });    
 
เปลี่ยนเป็น
 
    const response_async =  await axios.post('ajax_draganddrop.php?upload', formData, { 
        onUploadProgress: (progressEvent) => {
            const { loaded, total } = progressEvent;
            let precentage = Math.floor((loaded * 100) / total);
            progressBar.style.width = precentage+"%";
        //    console.log(precentage);
        },
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    });
 
ดูโค้ดตัวอย่างทั้งหมด ajax_draganddrop.php

<?php
if(isset($_GET['upload'])){
 
if (!empty($_FILES['picture'])) {    
    if(is_array($_FILES['picture']['name'])){
        $num_success = 0; // จำนวนอัพโหลดสำเร็จ
        $num_upload = count($_FILES['picture']['name']); // จำนวนไฟล์ทั้งหมด
 
        for($i = 0; $i < $num_upload; $i++){
            if (is_uploaded_file($_FILES['picture']['tmp_name'][$i])) {
                $sourcePath = $_FILES['picture']['tmp_name'][$i];
                $targetPath = "uploads/" . $_FILES['picture']['name'][$i];
                if (move_uploaded_file($sourcePath, $targetPath)) {
                    $num_success++; 
                }
            }
        }
        $response = array(
            "status"=>"OK",
            "num_success"=>$num_success,
            "num_upload"=>$num_upload
        );  
    }else{
        $response = array(
            "status"=>"Fail"
        );    
    }
}else{
    $response = array(
        "status"=>"Fail"
    );
}
header("Content-Type: application/json");
echo json_encode($response);        
exit; 
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

<style>
.drop-place{
    margin: auto;
    width: 75%;
    height: 50px;
    border: 1px solid red;
    text-align: center;
    padding: 50px;
    font-size: 20px;
}
.drop-place.dragging{
    background: #C8EFD4;
}
.drop-place.dropped{
    background: #6BD089;
}
.drop-guide.process{
    color: yellow;
}
.drop-guide.success{
    color: blue;
}
.hidden-inputfile{
    width: 1px;
    height: 1px;
    visibility: hidden;
}
#thumbnail,div.removepic{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}
#thumbnail img{
    width:50px;
    height:50px;
    margin:5px;
}
canvas{
    border:1px solid red;
}    
.upload-progress-bar{
    width: 0px;
    height: 5px;
    background-color: greenyellow;
}
</style>
<div class="drop-place">
    <div class="upload-progress-bar"></div>
    <span class="drop-guide"><a href="#" class="choose-image">เลือกไฟล์</a> หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด</span>
    <div id="thumbnail"></div>
</div>
<div class="hidden-inputfile"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOM fully loaded and parsed");
    const el = document.querySelector(".drop-place");    
    const drop_guide = document.querySelector(".drop-guide");    
    const thumbnail = document.querySelector("#thumbnail");    
    const progressBar = document.querySelector(".upload-progress-bar");
    // เมื่อลากไฟล์เข้ามาในพื้นที่ ทำงานครั้งเดียวเมื่อลากไฟล์เข้ามา
    el.addEventListener("dragenter", (event) => {
        event.preventDefault();
    //    console.log("DragEnter");
        drop_guide.innerHTML = 'ปล่อยไฟล์เพื่ออัพโหลด';
     //   var drag_image = event.originalEvent.dataTransfer.files;
    });
    // เมื่อลากไฟล์เข้ามาอยู่ด้านบนของพื้นที่ ส่วนนี้จะทำงานตลอดที่ไฟล์ที่ลากยังอยู่ในพื้ที่
    el.addEventListener("dragover", (event) => {
        event.preventDefault();
    //    console.log("DragOver");
        event.target.classList.add('dragging');
    });
    // เมื่อนำไฟล์ที่ลากออกนอกพื้นที่ จะทำงานครั้งเดียวเมื่่อลากไฟล์ออกนอกพื้นที่
    el.addEventListener("dragleave", (event) => {
        event.preventDefault();
    //    console.log("DragLeave");
        drop_guide.innerHTML = 'ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
        event.target.classList.remove('dragging');
    });
    // เมื่อวางหรือปล่อยไฟล์ที่ลากลงในพื้นที่ ทำงานครั้งเดียว
    el.addEventListener("drop", (event) => {
        event.preventDefault();
        console.log("Drop");
        drop_guide.classList.add('process');
        event.target.classList.remove('dragging');
        event.target.classList.add('dropped');
        // ตัวแปรเก็บข้อมูลไฟล์ ที่ลากมาวางเพื่ออัพโหลด
        var image = event.dataTransfer.files;
        showThumbnail(image);
        drop_guide.innerHTML = 'กำลังอัพโหลด '+image.length+' ไฟล์...';
        var formData = new FormData();
        console.log(image);
        // วนลูปเพิ่มข้อมูลฟิลด์รูปแบบอาเรย์เข้าไป
        for (var i = 0; i < image.length; i++) {
            formData.append('picture[]', image[i]);
        }
        //formData.append('picture', image[0]);

        uploadFormData(formData) // ส่งข้อมูลไปัยัง server
        .then( response => { // เมื่อส่งข้อมูลกลับมา
            console.log( response ); // JSON data 
            if(response.status=="OK"){
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');
                drop_guide.classList.add('success');
                drop_guide.innerHTML = 'อัพโหลด '+response.num_success+'/'+
                                        response.num_upload+' ไฟล์ เรียบร้อยแล้ว';
                setTimeout(()=>{
                    drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                    thumbnail.innerHTML = ''; // ล้างข้อมูลรูปตัวอย่างที่แสดง
                    progressBar.style.width = "0px";
                },3000);
            }else{
                event.target.classList.remove('dropped');
                drop_guide.classList.remove('process');            
                drop_guide.innerHTML = response.text;
                setTimeout(()=>{
                    drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                    drop_guide.classList.remove('success');
                    thumbnail.innerHTML = ''; // ล้างข้อมูลรูปตัวอย่างที่แสดง
                    progressBar.style.width = "0px";
                },3000);            
            }
        });


    });

    // ฟังก์ชั่่ง ajax ส่งข้อมูลไปยัง server และต้องรอข้อมูลกลับมาก่อนนำไปใช้งาน
    async function uploadFormData(formData) {
        try{

            const response_async =  await axios.post('ajax_draganddrop.php?upload', formData, { 
                onUploadProgress: (progressEvent) => {
                    const { loaded, total } = progressEvent;
                    let precentage = Math.floor((loaded * 100) / total);
                    progressBar.style.width = precentage+"%";
                //    console.log(precentage);
                },
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            });

            console.log(response_async);
            // ต้อง(ไม่)เป็น 404 หรือ 500 คือ ไต้ไม่เป็น ไม่พบไฟล์ หรือ server ไม่ตอบสนอง
            if(![404,500].includes(response_async.status)){
                // กรณีใช้ axios ตรงนี้้จะต้องเรียกผ่าน data ซึ่งเป็น json ไม่ต้องแปลง
                return response_async.data; 
            }else{ // กรณี ไม่พบไฟล์ 
                var errors = {status:'Fail',text:'เกิดข้อผิดพลาดลองใหม่อีกครั้ง'};
                return errors;
            }
        }catch (error) { // ส่วนใหญ่เป็นกรณี 500 หรือ server ไม่ตอบสนอง
            console.error(error);
            var errors = {status:'Fail',text:'การเชื่อมต่อ server ไม่ตอบสนอง'};
            return errors;
        }
    }   

    // กำหนด event ให้กับปุ่ม เลือกไฟล์ สังเกตว่า เราไม่ได้กำหนดให้กับปุ่มโดยตรง
    // แต่กำหนดในกับ drop-place แทน แล้วค่อยไปตรวจสอบว่า ถ้าตัวที่คลิกเป็นปุ่ม
    // choose-image หรือไม่ ถ้าใช่ ให้ทำงาน ทั้งนี้ก็เพราะว่า ถ้าเรากำหนดโดยตรง
    // ปุ่มที่ถูกสร้างมาจาก javascript จะไม่ทำงาน วิธีนี้จึงเป็นการป้องกันปัญหานั้น
    el.addEventListener("click", (event) => {
        var chooseEl = event.target.closest('.choose-image');
        if (chooseEl) { // คลิกโดยปุ่ม เลือกไฟล์หรือไม่
            createUploadFileInput();
        }
        event.preventDefault();
    });

    // ฟังก์ชั่นสำหรับสร้าง input file ให้เลือก พร้อมให้รองรับการอัพโหลดไฟล์ เมื่อเลือกไฟล์
    function createUploadFileInput() {

        var fileInputDOM = document.querySelector('.image_upload');
        var placeForFileInput = document.querySelector('.hidden-inputfile');

        // ตรวจสอบว่า ถ้ายังไม่มี ให้ทำการสร้างขึ้นมาก่อน
        if (!fileInputDOM) {

            // ทำการสร้าง
            var fileInput = document.createElement('input');

            fileInput.type = 'file';
            fileInput.name = 'picture[]'
            fileInput.classList.add('image_upload');
            fileInput.multiple = true;
            fileInput.accept = 'image/*';
            placeForFileInput.appendChild(fileInput);

            // เมื่อมีการเลือกไฟล์ หรือเปลี่ยนแปลงการเลือกไฟล์
            fileInput.addEventListener("change", (event) => {
                event.preventDefault();
                console.log("choose file");
                var image = fileInput.files; // เก็บตัวแปรไฟล์ที่เลือก
                if(image.length>0){ // ถ้ามีการเลือกไฟล์ 
                    showThumbnail(image);
                    drop_guide.innerHTML = 'กำลังอัพโหลด '+image.length+' ไฟล์...';
                    var formData = new FormData();
                    console.log(image);
                    // วนลูปเพิ่มข้อมูลฟิลด์รูปแบบอาเรย์เข้าไป
                    for (var i = 0; i < image.length; i++) {
                        formData.append('picture[]', image[i]);
                    }
                    //formData.append('picture', image[0]);

                    uploadFormData(formData) // ส่งข้อมูลไปัยัง server
                    .then( response => { // เมื่อส่งข้อมูลกลับมา
                        console.log( response ); // JSON data 
                        if(response.status=="OK"){
                            drop_guide.classList.remove('process');
                            drop_guide.classList.add('success');
                            drop_guide.innerHTML = 'อัพโหลด '+response.num_success+'/'+
                                                    response.num_upload+' ไฟล์ เรียบร้อยแล้ว';
                            setTimeout(()=>{
                                drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                                                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                                drop_guide.classList.remove('success');
                                thumbnail.innerHTML = ''; // ล้างข้อมูลรูปตัวอย่างที่แสดง
                                progressBar.style.width = "0px";
                            },3000);
                        }else{
                            drop_guide.classList.remove('process');            
                            drop_guide.innerHTML = response.text;
                            setTimeout(()=>{
                                drop_guide.innerHTML = '<a href="#" class="choose-image">เลือกไฟล์</a>'+
                                                    ' หรือ ลากไฟล์มาวางที่นี่เพื่ออัพโหลด';
                                drop_guide.classList.remove('success');
                                thumbnail.innerHTML = ''; // ล้างข้อมูลรูปตัวอย่างที่แสดง
                                progressBar.style.width = "0px";
                            },3000);            
                        }
                    });


                }

            });   
            fileInput.click(); // ให้ input file ถึงคลิก หลังจากสร้าง        
        }else{
            fileInputDOM.click(); // ให้ input file ถึงคลิก ตัวที่ถูกสร้างแล้ว
        }

    }    

    // ฟังก์ชั่นวนรูปสร้างรูปตัวอย่างที่กำลังอัพโหลด
    function showThumbnail(files){
   
       for(var i=0;i<files.length;i++){
           var file = files[i]
           var fileName = file.name;
           var imageType = /image.*/
           if(!file.type.match(imageType)){
               //     console.log("Not an Image");
               continue;
           }
  
           var image = document.createElement("img");
           var wrapImg = document.createElement("div");
           var thumbnail = document.getElementById("thumbnail");
           wrapImg.setAttribute("data-file", fileName);
           image.file = file;
           wrapImg.appendChild(image);
           thumbnail.appendChild(wrapImg);
  
           var reader = new FileReader();
           reader.onload = (function(aImg){
               return function(e){
                   aImg.src = e.target.result;
               };
           }(image))
  
           var ret = reader.readAsDataURL(file);
           var canvas = document.createElement("canvas");
           ctx = canvas.getContext("2d");
           image.onload= function(){
               ctx.drawImage(image,50,50)
           }
       } // end for loop
  
   } // end showThumbnail    
    

});
</script>
    
</body>
</html>


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ







เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ