HTML5 загрузка изображений

Как загрузить фотографии на сайт это интересный вопрос. Основная загрузка файлов немного скучная вещь, поэтому добавим важную особенность — обрезка. Кроме того, мы будем использовать HTML5 FileReader для того, чтобы выполнить обрезку с Jcrop (JQuery библиотеки) в размере клиента. Это позволит избавиться от ненужных шагов. В результате — мы должны получить 3-ступенчатый процесс: выбрать Файл -> обрезать -> готово (покажет результат в новом окне).

Во время выбора файла, будет проверяться тип и размер файла (что бы избежать больших файлов) по умолчанию файлы с расширением jpg, png не разрешены! Размеры изображения указаны в пикселях. После обрезки изображения — кликнем (готово) этот файл загрузится на нашем сайте (в определенной папке). Пожалуйста, обратите внимание, что GD библиотеки, необходимые для обработки изображений. Если вы готовы — давайте начнем.

Демо

Шаг 1. HTML



<!-- стили -->
	<link href=
http://pixelcom.crimea.ua/"css/main.css" rel="stylesheet" type="text/css" />
	<link href=
http://pixelcom.crimea.ua/"css/jquery.Jcrop.min.css" rel="stylesheet" type="text/css" />

<!-- скрипты -->
<script type="text/javascript" src=
http://pixelcom.crimea.ua/"js/jquery.min.js"></script>
<script type="text/javascript" src=
http://pixelcom.crimea.ua/"js/jquery.Jcrop.min.js"></script>
<script type="text/javascript" src=
http://pixelcom.crimea.ua/"js/script.js"></script>


И теперь, форма HTML5 загрузка и обрезка изображений:



<div class="bbody">
 <!-- форма загрузки -->
<form id="upload_form" action="upload.php" enctype="multipart/form-data" method="post" onsubmit="return checkForm()"><!-- скрытые параметры -->
 <input id="x1" type="hidden" name="x1" />
 <input id="y1" type="hidden" name="y1" />
 <input id="x2" type="hidden" name="x2" />
 <input id="y2" type="hidden" name="y2" />

<h2>Шаг1: Выбрать файл изображения</h2>

<div><input id="image_file" type="file" name="image_file" onchange="fileSelectHandler()" /></div>
<div class="error"></div>
<div class="step2">

<h2>Шаг 2: Указать новый размер</h2>

 <img id="preview" alt="" />
<div class="info"><label>Размер файла</label> <input id="filesize" type="text" name="filesize" />
 <label>Тип</label> <input id="filetype" type="text" name="filetype" />
 <label>Размер изображения</label> <input id="filedim" type="text" name="filedim" />
 <label>Ширина</label> <input id="w" type="text" name="w" />
 <label>Высота</label> <input id="h" type="text" name="h" /></div>
 <input type="submit" value="Готово" /></div>
</form></div>


Это обычная форма загрузки, скрытые и видимые поля, как только мы выбрали изображение, мы увидим второй этапе (обрезка). Как только мы обрезали изображение до не обходимого размера, после нажатия (готово) мы можем посмотреть результат.

Шаг 2. CSS

css/main.css



.bheader {
    background-color: #DDDDDD;
    border-radius: 10px 10px 0 0;
    padding: 10px 0;
    text-align: center;
}
.bbody {
    color: #000;
    overflow: hidden;
    padding-bottom: 20px;
    text-align: center;

    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);
}
.bbody h2, .info, .error {
    margin: 10px 0;
}
.step2, .error {
    display: none;
}
.error {
    font-size: 18px;
    font-weight: bold;
    color: red;
}
.info {
    font-size: 14px;
}
label {
    margin: 0 5px;
}
input {
    border: 1px solid #CCCCCC;
    border-radius: 10px;
    padding: 4px 8px;
    text-align: center;
    width: 70px;
}
.jcrop-holder {
    display: inline-block;
}
input[type=submit] {
    background: #e3e3e3;
    border: 1px solid #bbb;
    border-radius: 3px;
    -webkit-box-shadow: inset 0 0 1px 1px #f6f6f6;
    box-shadow: inset 0 0 1px 1px #f6f6f6;
    color: #333;
    font: bold 12px/1 "helvetica neue", helvetica, arial, sans-serif;
    padding: 8px 0 9px;
    text-align: center;
    text-shadow: 0 1px 0 #fff;
    width: 150px;
}
input[type=submit]:hover {
    background: #d9d9d9;
    -webkit-box-shadow: inset 0 0 1px 1px #eaeaea;
    box-shadow: inset 0 0 1px 1px #eaeaea;
    color: #222;
    cursor: pointer;
}
input[type=submit]:active {
    background: #d0d0d0;
    -webkit-box-shadow: inset 0 0 1px 1px #e3e3e3;
    box-shadow: inset 0 0 1px 1px #e3e3e3;
    color: #000;
}


Шаг 3. JS

js/script.js



// convert bytes into friendly format
function bytesToSize(bytes) {
    var sizes = ['Bytes', 'KB', 'MB'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
};

// check for selected crop region
function checkForm() {
    if (parseInt($('#w').val())) return true;
    $('.error').html('Please select a crop region and then press Upload').show();
    return false;
};

// update info by cropping (onChange and onSelect events handler)
function updateInfo(e) {
    $('#x1').val(e.x);
    $('#y1').val(e.y);
    $('#x2').val(e.x2);
    $('#y2').val(e.y2);
    $('#w').val(e.w);
    $('#h').val(e.h);
};

// clear info by cropping (onRelease event handler)
function clearInfo() {
    $('.info #w').val('');
    $('.info #h').val('');
};

function fileSelectHandler() {

    // get selected file
    var oFile = $('#image_file')[0].files[0];

    // hide all errors
    $('.error').hide();

    // check for image type (jpg and png are allowed)
    var rFilter = /^(image\/jpeg|image\/png)$/i;
    if (! rFilter.test(oFile.type)) {
        $('.error').html('Please select a valid image file (jpg and png are allowed)').show();
        return;
    }

    // check for file size
    if (oFile.size > 250 * 1024) {
        $('.error').html('You have selected too big file, please select a one smaller image file').show();
        return;
    }

    // preview element
    var oImage = document.getElementById('preview');

    // prepare HTML5 FileReader
    var oReader = new FileReader();
        oReader.onload = function(e) {

        // e.target.result contains the DataURL which we can use as a source of the image
        oImage.src = e.target.result;
        oImage.onload = function () { // onload event handler

            // display step 2
            $('.step2').fadeIn(500);

            // display some basic image info
            var sResultFileSize = bytesToSize(oFile.size);
            $('#filesize').val(sResultFileSize);
            $('#filetype').val(oFile.type);
            $('#filedim').val(oImage.naturalWidth + ' x ' + oImage.naturalHeight);

            // Create variables (in this scope) to hold the Jcrop API and image size
            var jcrop_api, boundx, boundy;

            // destroy Jcrop if it is existed
            if (typeof jcrop_api != 'undefined')
                jcrop_api.destroy();

            // initialize Jcrop
            $('#preview').Jcrop({
                minSize: [32, 32], // min crop size
                aspectRatio : 1, // keep aspect ratio 1:1
                bgFade: true, // use fade effect
                bgOpacity: .3, // fade opacity
                onChange: updateInfo,
                onSelect: updateInfo,
                onRelease: clearInfo
            }, function(){

                // use the Jcrop API to get the real image size
                var bounds = this.getBounds();
                boundx = bounds[0];
                boundy = bounds[1];

                // Store the Jcrop API in the jcrop_api variable
                jcrop_api = this;
            });
        };
    };

    // read selected file as DataURL
    oReader.readAsDataURL(oFile);
}


Есть несколько общих функций в начале: bytesToSize , checkForm , UpdateInfo и clearInfo — они лёгкие. Следующая функция fileSelectHandler является более сложной, в основном, это главная функция. Когда мы выбрали файл (файл изображения), мы будем проверять этот файл на тип и размер. Здесь можно увидеть фильтр для формата изображений: PNG и JPG. Кроме того, мы не нуждаемся в очень больших изображений, я думаю, что 250кб более чем достаточно. Затем, мы можем увидеть наш выбранный файл, используя FileReader :: readAsDataURL (HTML5 функции). И, как только изображение загружено, для продолжения отображаем шаг2 с разделом изображение и информацией, а потом — мы должны инициализировать (или — инициализация) Jcrop покажет изображение. Вот как это работает. Как только указали новый размер изображения, нажимаем кнопку «Готово», чтобы отправить результаты на сервер в папку cache.

Шаг 4. PHP

Функция PHP покажет результат на сервере:

upload.php



function uploadImageFile() { // Note: GD library is required for this function

    if ($_SERVER['REQUEST_METHOD'] == 'POST') {
        $iWidth = $iHeight = 200; // desired image result dimensions
        $iJpgQuality = 90;

        if ($_FILES) {

            // if no errors and size less than 250kb
            if (! $_FILES['image_file']['error'] && $_FILES['image_file']['size'] < 250 * 1024) {                 if (is_uploaded_file($_FILES['image_file']['tmp_name'])) {                     // new unique filename                     $sTempFileName = 'cache/' . md5(time().rand());                     // move uploaded file into cache folder                     move_uploaded_file($_FILES['image_file']['tmp_name'], $sTempFileName);                     // change file permission to 644                     @chmod($sTempFileName, 0644);                     if (file_exists($sTempFileName) && filesize($sTempFileName) > 0) {
                        $aSize = getimagesize($sTempFileName); // try to obtain image info
                        if (!$aSize) {
                            @unlink($sTempFileName);
                            return;
                        }

                        // check for image type
                        switch($aSize[2]) {
                            case IMAGETYPE_JPEG:
                                $sExt = '.jpg';

                                // create a new image from file
                                $vImg = @imagecreatefromjpeg($sTempFileName);
                                break;
                            case IMAGETYPE_PNG:
                                $sExt = '.png';

                                // create a new image from file
                                $vImg = @imagecreatefrompng($sTempFileName);
                                break;
                            default:
                                @unlink($sTempFileName);
                                return;
                        }

                        // create a new true color image
                        $vDstImg = @imagecreatetruecolor( $iWidth, $iHeight );

                        // copy and resize part of an image with resampling
                        imagecopyresampled($vDstImg, $vImg, 0, 0, (int)$_POST['x1'], (int)$_POST['y1'], $iWidth, $iHeight, (int)$_POST['w'], (int)$_POST['h']);

                        // define a result image filename
                        $sResultFileName = $sTempFileName . $sExt;

                        // output image to file
                        imagejpeg($vDstImg, $sResultFileName, $iJpgQuality);
                        @unlink($sTempFileName);

                        return $sResultFileName;
                    }
                }
            }
        }
    }
}

$sImage = uploadImageFile();
echo '<img alt="" src=
http://pixelcom.crimea.ua/"'.$sImage.'" />';


Мы должны проверить размер и формат на стороне сервера тоже. В результате — мы получаем двойную защиту (на стороне пользователя и на стороне сервера) от ненужных файлов. После того как мы загрузили изображение с помощью move_uploaded_file — мы можем обрезать его с помощью функции: imagecreatefromjpeg , imagecreatetruecolor и imagecopyresampled , а также — включить результат в файл изображения с помощью функции imagejpeg . Пожалуйста, обратите внимание — что в результате мы получим небольшое изображение (которое onle 200 × 200), так что, кроме обрезки, мы также изменим размер изображения. Я выбрал следующий нужный размер для всех входящих фотографий: 200 × 200 (это хороший формат .. аватары профиля в качестве примера). Наконец — мы можем увидеть это изображение на экране. Вот и все.

Статья подготовлена для вас коллективом сайта www.pixelcom.crimea.ua
Оригинал статьи: script-tutorials.com/html5-image-uploader-with-jcrop
Перевел: Виктор Клим
Правила использования материалов сайта!

12 комментариев на тему “HTML5 загрузка изображений”

  1. комментарии

    Спасибо, пригодилось! Вот тоже хороший материал

  2. комментарии

    Как изменить пропорцию при выдилении?

  3. комментарии

    Пропорции без выделения, размер указывается в пикселях, соотношение сторон указывать самому.

  4. комментарии

    Спасибо. Хоть бы ссылку оставили на js скрипты.

  5. комментарии

    Виктор, здравствуйте.
    Пробую сделать кроп изображения по вашему примеру.
    Столкнулся со следующей проблемой:
    Когда выбираю изображение (через input) первый раз, то все нормально работает, подгружается превью и т.д.
    Но если после этого выбрать другое изображение, то див с превьюшкой не обновится, и превью остается от первого, выбранного изображения.
    Как с этим бороться? Как заставить превью меняться каждый раз после выбора новой картинки?
    Пока что у меня вариантов нет, из-за чего такая проблема.
    Буду благодарен за ответ.

  6. комментарии

    И еще вопрос.
    Как сделать, чтобы изначально была выбрана для кропа какая-то часть изображения?
    Я так понял, что за выбранную зону отвечают параметры boundx и boundy, но сделать, чтобы была выделена область сразу после загрузки изображения не получается.

  7. комментарии

    Привет Виктор,
    что бы обновилось изображение нужно его загрузить, перед этим обновить страницу клавишей F5.
    Эта форма имеет немного другое предназначение, она не для выбора зоны, возможно Вам подойдёт другой вариант обрезки.
    Смотрите страницу с примером http://www.pixelcom.crimea.ua/obrezka-i-izmeneniya-razmera-izobrazhenij.html

  8. комментарии

    Спасибо!
    В том примере насколько я понял сначала идет загрузка изображения на сервер, а затем его изменение.
    В принципе мне и этот вариант подходит. Даже удобнее выбирать зону перед загрузкой.

    Вы говорите об обновлении страницы, для того, чтобы в превью появилось новое изображение.
    Я все-таки думаю, что можно немного видоизменить код, чтобы превью обновлялась каждый раз.
    Постараюсь найти решение.

  9. комментарии

    Привет Виктор,
    Вы можете менять код как угодно. Не забудьте поделиться видоизменением.

  10. комментарии

    Обязательно, если сделаю что-нибудь толковое :)

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML -теги и атрибуты: <a href= http://pixelcom.crimea.ua/"" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>