Ни о чём →
Javascript: Отправка изображения на канвасе на сервер
Здравствуйте хабровчане.
Решил я сделать отправку изображения с канваса на сервер.
А что из этого получилось смотрите под катом.
Итак, нам нужен браузер, поддерживающий холст.
Я решил сделать возможность мышкой чего-нибудь нарисовать на канвасе и отправить эту картинку на сервер.
Сразу скажу, что код javascript-овый будет написан на javascript`е.
Ничего против библиотек не имею, сам их использую. Просто, это более общий случай. При желании можно будет переписать под библиотеки.
Создаём канвас/холст (нужное подчеркнуть):
Теперь надо сделать так, чтобы можно было рисовать на канвасе.
Для рисования нам нужно получить контекст отображения 2D, создать путь, переместиться в начальную точку,
вызвать метод LineTo() для проведения линии в конечную точку, вызвать метод stroke() для рисования контура, закрыть путь.
Всё это можно видеть в моём коде, в обработчиках onmousedown, onmouseup и onmousemove.
Но с холстом-то ладно, есть куча библиотек для работы с ним. Теперь о том, как отправлять картинки на сервер.
Нам нужно сделать ajax-запрос методом POST.
Только картиночку надо посылать не вот так:
«image=%01%02%03...», а как файл.
Нам нужно поставить Content-type не «application/x-www-form-urlencoded», а «multipart/form-data».
Это значит, что тело запроса будет состоять из других подзапросов, которые сами будут иметь свои собственные заголовки и тела.
Как выглядит тело такого запроса можно посмотреть в LiveHttpHeaders в Firefox-е, создав html-страничку с формой,
с атрибутом enctype=«multipart/form-data»:
Каждый такой подзапрос, соответствует определенному полю формы.
На рисунке у меня отправляется форма с текстовым полем с name=«id» и полем «file» для отправки файла.
Я загружаю файл «loading.gif»
Нам нужно придумать граничный разделитель (boundary), который будет разделять тела запросов.
На рисунке этим разделителем является строка "---------------------------12722593819037", этот разделитель должен разделять эти
подзапросы и не должен встречаться в них.
Перед каждым таким подзапросом должна быть строка "--" + boundary, а после всех — строка "--" + boundary + "--"
Об этом можно почитать здесь: rfc 1521
О form-data можно почитать в rfc 1867.
Замечу, что на 19.12.2010 в Хроме уже есть объект FormData для создания form-data, а в Файрфоксе будет только в 4 версии.
Для отправки содержимого канваса, будем использовать метод dataURL, который возвращает картинку в виде base64 строки.
Пример:
Для электронных писем можно написать заголовок «Content-Transfer-Encoding» со значением «base64» для указания того,
что прикрепленный файл зашифрован в base64. К сожалению у меня добавление такого заголовка ничего не дало.
Пришлось декодировать base64 на сервере самому.
Одно лишь маленькое «но», я скажу.
Когда читаешь файл кусочками и декодируешь из base64, нужно читать за один раз число байтов кратное 4.
Это связано с кодировкой base64. Она рассматривает каждые 3 байта, как набор из 4 6-битовых символов, а потом каждый
такой 6-битовый символ отображает как какой-нибудь обычный 8-битовый текстовый символ.
(если в конце не хватает двух или одного байта, то берутся нулевые байты, а в полученном тексте добавляется "=" или "==")
Соответственно, если за один раз прочитать количество символов не кратное 4, то мы не получим картинки (можете проверить, подправив мой код).
Собственно, вот код декодирования картинки на PHP:
Собственно всё это можно посмотреть на труъкодинг.рф/canvas/
Слева будет картинка, чего-нибудь на ней рисуете, нажимаете кнопку «Send», картинка отправляется на сервер,
а потом отображается справа.
Код здесь: труъкодинг.рф/canvas/canvas.zip,
и здесь: http://www.rapidshare.ru/1709200,
и здесь: http://narod.ru/disk/1762612001/canvas.zip.html
на яндексе иногда проскакивает сообщение «файл не найден», пробуйте несколько раз.
Пример здесь: труъкодинг.рф/canvas/
Ссылки:
RFC MIME
form-data
Canvas
Удачи!
upd: Здесь несколько прикольных изображений, залитых пользователями: http://труъкодинг.рф/cpg/thumbnails.php?album=1
upd: Подобное оказывается реализовано здесь: http://www.nihilogic.dk/labs/canvas2image/
Решил я сделать отправку изображения с канваса на сервер.
А что из этого получилось смотрите под катом.
Итак, нам нужен браузер, поддерживающий холст.
Я решил сделать возможность мышкой чего-нибудь нарисовать на канвасе и отправить эту картинку на сервер.
Поехали
Холст
Сразу скажу, что код javascript-овый будет написан на javascript`е.
Ничего против библиотек не имею, сам их использую. Просто, это более общий случай. При желании можно будет переписать под библиотеки.
Создаём канвас/холст (нужное подчеркнуть):
<canvas id="canvas" ...></canvas>
Теперь надо сделать так, чтобы можно было рисовать на канвасе.
Для рисования нам нужно получить контекст отображения 2D, создать путь, переместиться в начальную точку,
вызвать метод LineTo() для проведения линии в конечную точку, вызвать метод stroke() для рисования контура, закрыть путь.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.closePath();
Всё это можно видеть в моём коде, в обработчиках onmousedown, onmouseup и onmousemove.
Отправка на сервер
Но с холстом-то ладно, есть куча библиотек для работы с ним. Теперь о том, как отправлять картинки на сервер.
Нам нужно сделать ajax-запрос методом POST.
Только картиночку надо посылать не вот так:
«image=%01%02%03...», а как файл.
Нам нужно поставить Content-type не «application/x-www-form-urlencoded», а «multipart/form-data».
Это значит, что тело запроса будет состоять из других подзапросов, которые сами будут иметь свои собственные заголовки и тела.
Как выглядит тело такого запроса можно посмотреть в LiveHttpHeaders в Firefox-е, создав html-страничку с формой,
с атрибутом enctype=«multipart/form-data»:
Каждый такой подзапрос, соответствует определенному полю формы.
На рисунке у меня отправляется форма с текстовым полем с name=«id» и полем «file» для отправки файла.
Я загружаю файл «loading.gif»
Нам нужно придумать граничный разделитель (boundary), который будет разделять тела запросов.
На рисунке этим разделителем является строка "---------------------------12722593819037", этот разделитель должен разделять эти
подзапросы и не должен встречаться в них.
Перед каждым таким подзапросом должна быть строка "--" + boundary, а после всех — строка "--" + boundary + "--"
Об этом можно почитать здесь: rfc 1521
О form-data можно почитать в rfc 1867.
Замечу, что на 19.12.2010 в Хроме уже есть объект FormData для создания form-data, а в Файрфоксе будет только в 4 версии.
Для отправки содержимого канваса, будем использовать метод dataURL, который возвращает картинку в виде base64 строки.
Пример:
var canvas = document.getElementById('canvas');
var body = canvas.todataURL();
...
xmlhttprequest.open('POST', url);
...
xmlhttprequest.setRequestHeader("Content-type", "multipart/form-data; boundary=" + boundary);
...
var data =
/*--boundary*/
"--" + boundary + "\n" +
/*заголовок*/
"Content-Disposition: form-data; name=\"file\"; filename=\"filename\"\n" +
"Content-type: image/png\n\n" +
/*тело*/
body +
/*--boundary--*/
"\n--" + boundary + "--\n";
...
xmlhttprequest.send(data);
Серверная часть
Для электронных писем можно написать заголовок «Content-Transfer-Encoding» со значением «base64» для указания того,
что прикрепленный файл зашифрован в base64. К сожалению у меня добавление такого заголовка ничего не дало.
Пришлось декодировать base64 на сервере самому.
Одно лишь маленькое «но», я скажу.
Когда читаешь файл кусочками и декодируешь из base64, нужно читать за один раз число байтов кратное 4.
Это связано с кодировкой base64. Она рассматривает каждые 3 байта, как набор из 4 6-битовых символов, а потом каждый
такой 6-битовый символ отображает как какой-нибудь обычный 8-битовый текстовый символ.
(если в конце не хватает двух или одного байта, то берутся нулевые байты, а в полученном тексте добавляется "=" или "==")
Соответственно, если за один раз прочитать количество символов не кратное 4, то мы не получим картинки (можете проверить, подправив мой код).
Собственно, вот код декодирования картинки на PHP:
$f = fopen ($filename, 'r') or die('Cannot open file');#Читаем загруженный файл
$f2 = fopen ($path . '/' . $name, 'w') or die('Cannot open file');#Записываем новый фалй
$length = 64;//должно быть кратно 4!
$error = FALSE;
while ($content = fread($f, $length)) {
$content = base64_decode($content, TRUE);
if ($content === FALSE) {
$error = TRUE;
break;
}
fwrite($f2, $content);
}
fclose($f);
fclose($f2);
Собственно всё это можно посмотреть на труъкодинг.рф/canvas/
Слева будет картинка, чего-нибудь на ней рисуете, нажимаете кнопку «Send», картинка отправляется на сервер,
а потом отображается справа.
Код здесь: труъкодинг.рф/canvas/canvas.zip,
и здесь: http://www.rapidshare.ru/1709200,
и здесь: http://narod.ru/disk/1762612001/canvas.zip.html
на яндексе иногда проскакивает сообщение «файл не найден», пробуйте несколько раз.
Пример здесь: труъкодинг.рф/canvas/
Ссылки:
RFC MIME
form-data
Canvas
Удачи!
upd: Здесь несколько прикольных изображений, залитых пользователями: http://труъкодинг.рф/cpg/thumbnails.php?album=1
upd: Подобное оказывается реализовано здесь: http://www.nihilogic.dk/labs/canvas2image/
19.12.2010 22:16+0300