Html5 асинхронная загрузка изображений

27 Сен
2011

Статья рассматривает применение новой технологии html5 для асинхронной загрузки изображений на сервер. Я рассмотрю новое API JavaScript и приведу несколько примеров кода выбора файлов, создание предварительных изображений, предварительного поворота на 90 градусов, предварительного изменения размеров картинок на стороне браузера и последующую загрузку файлов на сервер. Данные примеры были протестированы и работают пока лишь в браузерах Mozilla Firefox, Google Chrome последних версий.

Для работы примеров создадим документ html:
<!DOCTYPE html>
HTML5 uploader images
Send


В документе обязательным условием является указание doctype html. Форма выбора файлов создержит поле input с type=”file”. Для обеспечения выбора нескольких файлов для input применен аттрибут multiple=”true”. Обработчик события окончания выбора файлов handleFiles имеет аргумент, представляющий собой массив выбранных файлов.
Создаем файл js, где будет весь алгоритм загрузчика:
window.URL = window.URL || window.webkitURL; //в google chrome нет window.URL, но есть //window.webkitURL. Он необходим для создания URL локального изображения 
var currImg = 0; //номер текущего загружаемого в браузер изображения
var k = 0;//номер текущего обрабатываемого изображения
var list = document.createElement(“ul”); //создаем список ul
var imgData = [];//массив canvas, где хранятся canvas изображений для последующей загрузки
function handleFiles(files) { //обработчик события выбора файлов
for(var i = 0; i < files.length; i++) {
var file = files[i];
var li = document.createElement("li");
list.appendChild(li);
var img = document.createElement("img");
var canvas = document.createElement("canvas");
canvas.setAttribute("id","canvas_" + k);
img.onload = function(e) {//обработка события загрузки изображения. //e.target – текущий объект изображения
window.URL.revokeObjectURL(e.target.src);//очищаем ObjectURL
var canvas = document.getElementById("canvas_" + currImg); //мы создаем //canvas для последующей работы с изображением в браузере. Тем самым мы даем //пользователю предварительно поправить (например, повернуть) картинку перед ее загрузкой.
canvas.width = e.target.width;
canvas.height = e.target.height;
canvas.getContext("2d").drawImage(e.target,0,0,e.target.width,e.target.height);//здесь рисуем на //canvas наше изображение
imgData[currImg] = canvas;//добавляем в массив
currImg++;//отдельный счетчик сделан из-за того, что картинки загружаются //параллельно работе обработчика выбора файлов, и как следствие намного медленнее
}
img.src = window.URL.createObjectURL(file);//присваиваем локальный адрес //картинки в src объекта img
var info = document.createElement("span");
info.innerHTML = files[i].name + ": " + Math.floor(files[i].size / 1024) + " Kb"; //выводим информацию о загруженной картинке
li.appendChild(info);
li.appendChild(canvas);
k++;//увеличиваем счетчик обрабатываемых изображений
}
document.getElementById("selectedFiles").appendChild(list);
}

Далее напишем обработчик нажатия кнопки загрузки (id=”sendfile”):
var sendBtn = document.getElementById(“sendfile);
sendBtn.addEventListener(“click”,upload,false);//добавляем «прослушиватель события нажатия»
function upload(e) {
for(var i = 0; i < imgData.length; i++) {
var canvas = imgData[i];
var dataurl = canvas.toDataURL("image/jpeg");
//для отправки изображения я воспользовался библиотекой jQuery
jQuery.post("/get.php",{image:dataurl},function(data){
div.innerHTML = data;
});
}

Поясню некоторые особо важные моменты. В функции upload мы пробегаемся по массиву canvas`ов. Далее нам необходимо получить данные изображения из текущей канвы для отправки на сервер. Для этого у canvas есть три метода:
  1. toDataURL(in optional DOMString type, in any …args)
  2. toBlob(in Function callback, in optional DOMString type, in any …args)
  3. mozGetAsFile(in DOMString name, in optional DOMString type)

Метод toBlob я не рассматривал, и, если я не ошибаюсь у этого метода есть баг. Метод mozGetAsFile возвращает объект File из canvas. Он специфичный и работает только лишь в браузерах на движке Gecko 2.0 (Mozilla Firefox), и как следствие, не стал работать в Google Chrome.
Метод toDataURL возвращает строку вида: «data:image/png;base64,/9j/4AAQSkZJR…»
В зависимости от первого аргумента, после слова data: может быть image/png либо image/jpeg, смотря в каком формате вам нужна картинка. По-умолчанию формат будет image/png. Вы можете установить image/jpeg передав в первый параметр image/jpeg. У метода toDataURL при установке первого параметра image/jpeg есть возможность установить второй числовой параметр от 0.0 до 1.0, который будет обозначать качество сохранения изображения в формате jpeg. Но здесь подстерегает баг. В Mozilla Firefox при установке второго параметра, будет вызвано исключение с ошибкой безопасности номер 1000. Пока баг не исправлен, можем довольствоваться сохранением картинки в jpeg формате с качеством сжатия по-умолчанию.
В строке, что возвращает метод toDataURL сразу после слова base64 и запятой идет строка, закодированная в base64, которая является данными изображения. Следовательно на сервере нам будет необходимо перед сохранением в файл вырезать эту строку и расшифровать ее. Вот как я сделал на php для формата image/jpeg:
<?php
$filedata = substr($_POST['image'], 23);
$fp = fopen($file, "w");
fwrite($fp, base64_decode($filedata));
fclose($fp);

Теперь о изменении размеров изображений на стороне браузера. Я приведу пример функции осуществляющий алгортим изменения размеров на canvas:
function resizeImg(canvas, max_width, max_height) {
canvas.style.display = "none";
var width = canvas.width;
var height = canvas.height;
if (width > height) {
if (width > max_width) {
height *= max_width / width;
width = max_width;
}
} else {
if (height > max_height) {
width *= max_height / height;
height = max_height;
}
}
var copy = document.createElement("canvas");
copy.width = width;
copy.height = height;
copy.getContext("2d").drawImage(canvas,0,0,width,height);
canvas.width = width;
canvas.height = height;
canvas.getContext("2d").drawImage(copy,0,0);
canvas.style.display = "block";
}

Данная функция пропорционально изменяет размер изображения. Пример применения с функцией handleFiles:
img.onload = function(e) {//обработка события загрузки изображения. //e.target – текущий объект изображения
window.URL.revokeObjectURL(e.target.src);//очищаем ObjectURL
var canvas = document.getElementById("canvas_" + currImg); //мы создаем //canvas для последующей работы с изображением в браузере. Тем самым мы даем //пользователю предварительно поправить (например, повернуть) картинку перед ее загрузкой.
canvas.width = e.target.width;
canvas.height = e.target.height;
canvas.getContext("2d").drawImage(e.target,0,0,e.target.width,e.target.height);//здесь рисуем на //canvas наше изображение
resizeImg(canvas, 188, 188); //создаем thumbnail
imgData[currImg] = canvas;//добавляем в массив
currImg++;//отдельный счетчик сделан из-за того, что картинки загружаются //параллельно работе обработчика выбора файлов, и как следствие намного медленнее
}

Функция поворота изображения выглядит так:
/**
* Процедура поворта изображения на холсте
* @param canvas холст
* @param width ширина
* @param height высота
* @param angle угол в градусах
* @return void
*/
function rotateImg(canvas, width, height, angle) {
var copy = document.createElement('canvas');
copy.width = width;
copy.height = height;
copy.getContext("2d").drawImage(canvas, 0,0,width,height);
angle = -parseFloat(angle) * Math.PI / 180;
var dimAngle = angle;
if (dimAngle > Math.PI*0.5)
dimAngle = Math.PI - dimAngle;
if (dimAngle < -Math.PI*0.5)
dimAngle = -Math.PI - dimAngle;
var diag = Math.sqrt(width*width + height*height);
var diagAngle1 = Math.abs(dimAngle) - Math.abs(Math.atan2(height, width));
var diagAngle2 = Math.abs(dimAngle) + Math.abs(Math.atan2(height, width));
var newWidth = Math.abs(Math.cos(diagAngle1) * diag);
var newHeight = Math.abs(Math.sin(diagAngle2) * diag);
canvas.width = newWidth;
canvas.height = newHeight;
var ctx = canvas.getContext("2d");
ctx.translate(newWidth/2, newHeight/2);
ctx.rotate(angle);
ctx.drawImage(copy,-width/2,-height/2);
}

Здесь так же ничего сложного нет. Для применения вам необходимо будет создать кнопку и повесить на ее событие клика эту функцию.
Кроме поворота можно применить еще множество различных эффектов перед загрузкой на сервер.
В ходе исследования и разработки загрузчика изображений были использованы материалы следующих источников:
How to develop a HTML5 Image Uploader
Информация о canvas
По материалам Хабрахабр.



загрузка...

Комментарии:

Наверх