Статьи

HTML5 Drag and Drop для загрузки нескольких файлов

Наша новая статья расскажет вам о загрузке файлов HTML5. Да, я объяснял основы загрузки файлов html5 в прошлом (в одной из наших предыдущих статей), но сегодня я хотел бы привести вам другой пример, более лучший. Теперь вы можете просто перетащить изображения (несколько изображений), чтобы начать загрузку. Кроме того, скрипт отображает общий прогресс (в процентах, плюс оставленные файлы) и ответ сервера (без фактической загрузки). Я собираюсь отобразить прогресс в элементе Canvas в реальном времени. И наконец — наш скрипт может загружать файлы не только на свой сервер, но и на другой (можно сказать, что это межсайтовый загрузчик).

Вот наши демонстрационные и загружаемые пакеты:

Live Demo

скачать в упаковке


Хорошо, скачайте исходники и начнем!


Шаг 1. HTML

Первый — HTML-разметка:

index.html

<div class="container">
    <div class="contr"><h2>Drag and Drop your images to 'Drop Area' (up to 5 files at a time, size - under 256kb)</h2></div>
    <div class="upload_form_cont">
        <div id="dropArea">Drop Area</div>

        <div class="info">
            <div>Files left: <span id="count">0</span></div>
            <div>Destination url: <input id="url" value="http://www.script-tutorials.com/demos/257/upload.php"/></div>
            <h2>Result:</h2>
            <div id="result"></div>
            <canvas width="500" height="20"></canvas>
        </div>
    </div>
</div>
<script src="js/script.js"></script>

Как видите, он состоит из нескольких основных элементов: «Область перетаскивания» слева и «Информационный блок» справа. Когда мы перетаскиваем файлы изображений на нашу dropArea, в блоке Info мы получаем ответ от сервера. Обратите внимание на элемент «URL назначения». Прямо сейчас это связано с нашим сервером. Но вы можете изменить его на свой URL в любое время.

Шаг 2. CSS

CSS / main.css

Теперь пришло время настроить наш макет:

CSS / main.css

.container {
    overflow:hidden;
    width:960px;
    margin:20px auto;
}
.contr {
    background-color: #212121;
    color: #FFFFFF;
    padding: 10px 0;
    text-align: center;

    border-radius:10px 10px 0 0;
    -moz-border-radius:10px 10px 0 0;
    -webkit-border-radius:10px 10px 0 0;
}
.upload_form_cont {
    background: -moz-linear-gradient(#ffffff, #f2f2f2);
    background: -ms-linear-gradient(#ffffff, #f2f2f2);
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f2f2f2));
    background: -webkit-linear-gradient(#ffffff, #f2f2f2);
    background: -o-linear-gradient(#ffffff, #f2f2f2);
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2');
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2')";
    background: linear-gradient(#ffffff, #f2f2f2);

    color: #000;
    overflow: hidden;
}
.info {
    background-color: #EEEEEE;
    border: 1px solid #DDDDDD;
    float: left;
    font-weight: bold;
    height: 530px;
    margin: 20px;
    position: relative;
    width: 560px;
}
.info > div {
    font-size: 14px;
    font-weight: bold;
    padding: 10px 15px 5px;
}
.info > h2 {
    padding: 0 15px;
}
.info > canvas {
    margin-left: 15px;
    margin-bottom: 10px;
}
.info #url {
    width: 400px;
}
#dropArea {
    background-color: #DDDDDD;
    border: 3px dashed #000000;
    float: left;
    font-size: 48px;
    font-weight: bold;
    height: 530px;
    line-height: 530px;
    margin: 20px;
    position: relative;
    text-align: center;
    width: 300px;
}
#dropArea.hover {
    background-color: #CCCCCC;
}
#dropArea.uploading {
    background: #EEEEEE url(loading.gif) center 30% no-repeat;
}
#result .s, #result .f {
    font-size: 12px;
    margin-bottom: 10px;
    padding: 10px;

    border-radius:10px;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
}
#result .s {
    background-color: #77fc9f;
}
#result .f {
    background-color: #fcc577;
}

Шаг 3. HTML5 JS

JS / script.js

// variables
var dropArea = document.getElementById('dropArea');
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
var count = document.getElementById('count');
var destinationUrl = document.getElementById('url');
var result = document.getElementById('result');
var list = [];
var totalSize = 0;
var totalProgress = 0;

// main initialization
(function(){

    // init handlers
    function initHandlers() {
        dropArea.addEventListener('drop', handleDrop, false);
        dropArea.addEventListener('dragover', handleDragOver, false);
    }

    // draw progress
    function drawProgress(progress) {
        context.clearRect(0, 0, canvas.width, canvas.height); // clear context

        context.beginPath();
        context.strokeStyle = '#4B9500';
        context.fillStyle = '#4B9500';
        context.fillRect(0, 0, progress * 500, 20);
        context.closePath();

        // draw progress (as text)
        context.font = '16px Verdana';
        context.fillStyle = '#000';
        context.fillText('Progress: ' + Math.floor(progress*100) + '%', 50, 15);
    }

    // drag over
    function handleDragOver(event) {
        event.stopPropagation();
        event.preventDefault();

        dropArea.className = 'hover';
    }

    // drag drop
    function handleDrop(event) {
        event.stopPropagation();
        event.preventDefault();

        processFiles(event.dataTransfer.files);
    }

    // process bunch of files
    function processFiles(filelist) {
        if (!filelist || !filelist.length || list.length) return;

        totalSize = 0;
        totalProgress = 0;
        result.textContent = '';

        for (var i = 0; i < filelist.length && i < 5; i++) {
            list.push(filelist[i]);
            totalSize += filelist[i].size;
        }
        uploadNext();
    }

    // on complete - start next file
    function handleComplete(size) {
        totalProgress += size;
        drawProgress(totalProgress / totalSize);
        uploadNext();
    }

    // update progress
    function handleProgress(event) {
        var progress = totalProgress + event.loaded;
        drawProgress(progress / totalSize);
    }

    // upload file
    function uploadFile(file, status) {

        // prepare XMLHttpRequest
        var xhr = new XMLHttpRequest();
        xhr.open('POST', destinationUrl.value);
        xhr.onload = function() {
            result.innerHTML += this.responseText;
            handleComplete(file.size);
        };
        xhr.onerror = function() {
            result.textContent = this.responseText;
            handleComplete(file.size);
        };
        xhr.upload.onprogress = function(event) {
            handleProgress(event);
        }
        xhr.upload.onloadstart = function(event) {
        }

        // prepare FormData
        var formData = new FormData();
        formData.append('myfile', file);
        xhr.send(formData);
    }

    // upload next file
    function uploadNext() {
        if (list.length) {
            count.textContent = list.length - 1;
            dropArea.className = 'uploading';

            var nextFile = list.shift();
            if (nextFile.size >= 262144) { // 256kb
                result.innerHTML += '<div class="f">Too big file (max filesize exceeded)</div>';
                handleComplete(nextFile.size);
            } else {
                uploadFile(nextFile, status);
            }
        } else {
            dropArea.className = '';
        }
    }

    initHandlers();
})();

Большая часть кода уже прокомментирована. Я надеюсь, что вы можете понять весь этот код. В любом случае — некоторое объяснение того, как это работает: в начале мы связали два обработчика с нашей DropArea: «drop» и «dropover». Когда мы помещаем перетаскиваемые файлы в область перетаскивания, мы можем применять собственные стили к нашей области перетаскивания. Затем, когда мы отбрасываем наши файлы, наш скрипт начинает выполнять функцию ‘processFiles’ (она помещает все отброшенные файлы в массив и начинает их загрузку шаг за шагом). В результате мы отправляем данные через объект XMLHttpRequest на пользовательский сервер получателей. Во время отправки файлов мы также отображаем общий прогресс в нашем элементе canvas.

Шаг 4. PHP

upload.php

<?php

// set error reporting level
if (version_compare(phpversion(), '5.3.0', '>=') == 1)
  error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
else
  error_reporting(E_ALL & ~E_NOTICE);

function bytesToSize1024($bytes, $precision = 2) {
    $unit = array('B','KB','MB');
    return @round($bytes / pow(1024, ($i = floor(log($bytes, 1024)))), $precision).' '.$unit[$i];
}

if (isset($_FILES['myfile'])) {
    $sFileName = $_FILES['myfile']['name'];
    $sFileType = $_FILES['myfile']['type'];
    $sFileSize = bytesToSize1024($_FILES['myfile']['size'], 1);

    echo <<<EOF
<div class="s">
    <p>Your file: {$sFileName} has been successfully received.</p>
    <p>Type: {$sFileType}</p>
    <p>Size: {$sFileSize}</p>
</div>
EOF;
} else {
    echo '<div class="f">An error occurred</div>';
}