- PVSM.RU - https://www.pvsm.ru -

Скриншоты без десктопа на HTML5

Практически каждый день я пользуюсь почтой Gmail, но вот недавно заметил, что если сделать скриншот экрана (www.take-a-screenshot.org/ [1]), то простым нажатием Ctrl + V этот скриншот можно скопировать прямо в текст письма Gmail. Это работает везде, но естественно кроме IE. Заинтересовавшись вопросом как это происходит нагуглил следующий пост на Stackoverflow [2]. Под сильным впечатлением от возможностей HTML5 clipboardData решил сделать простенький портал, где без всяких Desktop приложений любой юзер может загрузить скриншот [3] просто скопировав его.

Поддержка браузерами

Во первых надо отметить, что для того, чтобы что-то скопировать из буфера (например скриншот) в браузере должна быть поддержка Clipboard API [4]. Как можно видеть на caniuse [5] только самые последние браузеры работают с этим API. IE, к сожалению, курит в сторонке не смотря на partial support.

Загрузка скриншотов на javascript

Для загрузки изображения из буфера (скриншота) сначала нужно определить обработчик paste события:

document.body.addEventListener("paste", function(e) {
   ...
});

Обработчик будет вызываться всегда, когда в рабочей области окна браузера происходит событие «Вставить», например по нажатию Ctrl + V. Далее нужно определить код, который собственно и примет файл изображения из буфера. Этот код идентичен для браузеров Chrome и Opera, но разный для FireFox, так как у последнего по каким-то соображениям безопасности закрыты методы объекта clipboardData.

Chrome, Opera

// e.clipboardData.items - это и есть файлы находящиеся в буфер обмене
for (var i = 0; i < e.clipboardData.items.length; i++) {
    
    // выбираем только картинки
    if (e.clipboardData.items[i].kind == "file" && e.clipboardData.items[i].type == "image/png") {

        // Получаем файл как Blob (бинарные данные)
        var imageFile = e.clipboardData.items[i].getAsFile();
        var fileReader = new FileReader();
        fileReader.onloadend = function(e) {
            // Файл прочитан в this.result его Base64 представление
            loadImg(this.result);
        };

        // Читаем Blob как DataURL (Base64 представление бинарных данных)
        fileReader.readAsDataURL(imageFile);
        e.preventDefault();
        break;
    }
}
FireFox

В FireFox у нас нет возможности прочитать файл через clipboardData. При вставке, браузер самостоятельно создает <img> тег с src в виде DataURL. Поэтому придется сделать «костыль»:

// При инициализации js, если браузера Mozilla, то добавляем скрытый, редактируемый (contenteditable) div и
// ставим на него фокус.
if ($.browser.mozilla) {
    $(document.body).prepend('<div id="temp" contenteditable="true" style="height:1px;width:1px;color:#FFFFFF;"></div>');
    $('#temp').focus();
}

// Определяем событие "Вставить"
document.body.addEventListener("paste", function(e) {

    if ($.browser.mozilla) {

        // Если Mozilla, то фокусируем на созданный ранее div
        // (это нужно для того, чтобы FireFox вставил img в нужном месте)
    	$('#temp').focus();

        // Удаляем предыдущий img (вдруг мы копируем 2, 3, ..., N раз)
    	$('#temp img').remove();

        // У FireFox при создании img нет callback-а и единственный способ получить img - использовать небольшую паузу
        setTimeout(function() {
            // Вот тут FireFox точно вставил img в temp div, мы можем взять его DataURL и загрузить картинку
            loadImg($('#temp img').attr('src'));
        }, 1);
        return true;
    }
});

Куда загрузить картинку?

Загрузить картинку можно сразу на сервер, передавая через POST DataURL или например в canvas как сделано на Your screen [3]:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
...
function loadImg(dataURL) {
    var imageObj = new Image();
    imageObj.onload = function() {
        var width = this.width, height = this.height;
        canvas.width  = width;
        canvas.height = height;
        ctx.drawImage(this, 0, 0, width, height);
    };
    imageObj.src = dataURL;
}

Canvas удобен тем, что перед загрузкой скриншота на сервер можно его отредактировать: подчеркнуть, нарисовать, обвести, вырезать и т.д.

Что происходит на сервере?

На сервере должен быть POST контроллер, который бы принимал body в формате Base64 (DataURL) и декодировал его в бинарный файл изображения, например png. Контроллер может быть написан на любом языке, например Java с использованием SpringMVC:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody String save(@RequestBody String b64) {
    File file = null;
    FileOutputStream out = null;
    try {
        // Создаем Output поток
        out = new FileOutputStream("/opt/files/somename.png");

        // Декодируем Base64 в байты и сохраняем в поток
        out.write(Base64.decodeBase64(StringUtils.replace(b64, "data:image/png;base64,", "")));

        // Закрываем поток
        IOUtils.closeQuietly(out);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return file.getName();
}

Для внимательных отмечу, что вот эта конструкция StringUtils.replace(b64, «data:image/png;base64,», "") нужна для нормализации передаваемого из Javascript DataURL. Дело в том, что при создании DataURL вначале ставится его тип и формат и после идет Base64 представление, например data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABVYAAAMAC…

Дополнительно можно ещё проверить размер файла, и если он превышает определенный максимум сжать в jpg.

Варианты использования

Сервис Your screen [3] мы в компании Cackle используем каждый день в качестве быстрого и простого средства для снимка экранов наших клиентов. К сожалению, подобных решений в сети очень мало и те, что есть неудобны или работают только через Desktop:

  • prntscr.com [6] — копировать скриншот можно только в Chrome и без редактирования
  • snag.gy [7] — уже лучше, но скрншоты загружаются очень медленно и только один раз (первый)
  • Больше не нашел...

В скором времени мы планируем перенести весь этот функционал в наш Онлайн консультант Cackle [8]. Таким образом у клиентов будет возможность сделать скриншот и передать изображение прямо через консультанта оператору. Это в некотором смысле лучшая замена Co-Browser, так как позволяет показывать весь экран (а не только браузер), снизить издержки на разработку и минимизировать ошибки.

В заключении скажу, что проект Your screen [3] совсем новый и будет развиваться дальше, поэтому жду вашей критики и пожеланий по функционалу.

Автор: cackle

Источник [9]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/47746

Ссылки в тексте:

[1] www.take-a-screenshot.org/: http://www.take-a-screenshot.org/

[2] Stackoverflow: http://stackoverflow.com/questions/6393280/paste-image-into-rich-text-like-gmail

[3] может загрузить скриншот: http://yourscr.com

[4] Clipboard API: http://dev.w3.org/2006/webapi/clipops

[5] caniuse: http://caniuse.com/clipboard

[6] prntscr.com: http://prntscr.com

[7] snag.gy: http://snag.gy

[8] Онлайн консультант Cackle: http://cackle.me

[9] Источник: http://habrahabr.ru/post/201190/