Получение фото с android-смартфона прямо в html форму

в 10:06, , рубрики: android, canvas, ipwebcam, javascript, Веб-разработка, фото, метки: , , , ,

Здравствуйте!

Возникла задача для интернет-магазина сфотографировать большое количество товаров. Специфика товара такова, что не столько важна красивая картинка, сколько особенности конструкции товара (с какой стороны находится шлейф, есть ли петли крепления и т.п.)

Фотографировать обычным фотоаппаратом, потом заливать фото на компьютер, искать каждый товар в админке, потом искать соответствующее фото, показалось очень долго. Гораздо проще открыть товар в админке и навести телефон на товар. Тем более, что при хорошо выставленном освещении, современные смартфоны выдают вполне качественную картинку.

Теория

В андроид-маркете лежит замечательная программа IpWebCam, которая позволяет превратить свой телефон в полноценную веб-камеру. Кроме того у нее есть api для получения фотографий с автофокусом. При запуске IpWebCam, на телефоне поднимается web-сервер, который позволяет с локальной машины по wi-fi получать текущий кадр с телефона по адресу вида 192.168.0.14:8080/shot.jpg

Идея была следующая:

  1. Вставить в форму <img> с адресом фотографии со смартфона
  2. Создать canvas и в него скопировать содержимое <img>
  3. Сохранить данные при помощи canvas.toDataURL()
  4. Отправить данные на сервер при помощи ajax


К сожалению, из-за кросс-доменных политик метод toDataURL() сохраняет только черный квадрат вместо изображения. Поэтому canvas надо создавать на том же домене, из которого берется изображение. Сходив на сайт программы, я узнал, что IpWebCam позволяет создавать собственные html-страницы на своем внутреннем сервере. Для этого их достаточно залить на sd-карту и указать программе, в какой папке их искать.

Алгоритм следующий:

  1. На смартфоне создаем специальную страницу (например my.html)
  2. В форме на нашем сайте создаем <iframe>, в который грузим html-страницу со смартфона.
  3. На телефоне в html странице создаем canvas, в который грузим изображение с камеры.
  4. Сохраняем данные при помощи canvas.toDataURL() в переменную
  5. Передаем данные в родительскую страницу при помощи window.postMessage()
  6. В родительской странице получаем изображение и отправляем данные на сервер при помощи ajax
  7. На сервере сохраняем изображение в файл.

Решение

Во-первых, ставим на смартфон программу IpWebCam из маркета.

Создаем на смартфоне папку webcam, а в ней создаем файл my.html со следующим содержимым:

Content-Type: text/html

<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.jplayer.min.js"></script>
<script type="text/javascript" src="common.js"></script>
<style>
	* { margin: 0; padding: 0}
</style>
	
<script type="text/javascript">
$(loadJsWindowed);
function shot()
{
	$('#msg').text('Please wait...');
	var img = new Image();
	img.onload =  function(){
		var canvas = $('<canvas width="' + img.width + '" height="' + img.height + '">');
		canvas[0].getContext('2d').drawImage(img, 0, 0);
		var data = canvas[0].toDataURL('image/jpeg').replace(/data:image/jpeg;base64,/, '');
		canvas.remove();
		//засылаем картинку на сервер
		window.parent.postMessage(data, '*');
	}
	img.src = 'photoaf.jpg';
}
</script>

</head>
<body>
<div id="msg" style="text-align: center; background-color: #000000; color: #FFFFFF; font-weight: bold; cursor: pointer;" onclick="shot();">Click to take a shot..</div>
<img id="img1" src="/shot.jpg?1" style="cursor: pointer; position:absolute;"/>
<img id="img2" src="/shot.jpg?2" style="cursor: pointer; position:absolute;"/>
</body>
</html>

Обратите внимание, что в начале файла идут HTTP заголовки и пустая строка перед основным содержимым страницы. За основу был взят javascript видео-проигрыватель из самой программы.

Теперь надо указать, где искать файлы для веб-сервера приложения. Для этого откройте программу на телефоне, нажмите хардварную кнопку меню и выберите единственный пункт Cheats. Теперь введите в открывшемся диалоговом окне команду set(HtmlPath,/sdcard/webcam). Обратите внимание, что после запятой не допускаются пробелы.

Теперь на нашем сервере создаем файл jquery плагина jquery.ipwebcam.js:

(function($) {
	$.fn.ipWebCam = function(options) {
		var settings = $.extend( {
			ip: '',
			width: 640,
			height: 480,
			action: '?',
			callback: function(){}
		}, options);
		
		function ipWebCam_listener(event){
			$('#ipWebCam_wnd').prev().remove();
			$('#ipWebCam_wnd').remove();
			$.post(settings.action, {data:event.data}, settings.callback);
		}

		if (window.addEventListener){
			window.addEventListener('message', ipWebCam_listener,false);
		} else {
			window.attachEvent('onmessage', ipWebCam_listener);
		}
		
		return this.each(function() {
			$(this).click(function(){
				
				if(settings.ip=='')
					settings.ip = prompt('IP Webcam address:');
				
				$('<iframe>').css({
					position: 'fixed',
					width: settings.width + 'px',
					marginLeft: '-' + (settings.width/2) + 'px',
					left: '50%',
					height: settings.height + 'px',
					marginTop: '-' + (settings.height/2) + 'px',
					top: '50%',
					border: 0,
					overflow: 'hidden',
					backgroundColor: '#777777'
				})
				.attr('width', settings.width)
				.attr('height', settings.height)
				.attr('src', 'http://' + settings.ip + ':8080/my.html')
				.attr('id', 'ipWebCam_wnd')
				.prependTo('body');

				$('<div>').css({
					position:'fixed',
					left: 0,
					top: 0,
					right:0,
					bottom:0,
					backgroundColor: '#000000',
					opacity: 0.5
				}).click(function(){
					$('#ipWebCam_wnd').prev().remove();
					$('#ipWebCam_wnd').remove();
				}).prependTo('body');
				
			});
		});
	};
})( jQuery )

Плагин цепляется к кнопке вызова диалога. Например:

$('#camera_button').ipWebCam({
	ip: '', //ip адрес камеры, если не задан, то попросит ввести через prompt
	action: 'save_img.php', //адрес скрипта сохранения картинки, по умолчанию та же страница
	callback: function(rep){ // будет вызвана после отправки данных на сервер
		window.location.reload();
	},
	width: 640, // размеры iframe
	height: 480
});

Плагин передает данные на сервер методом POST в переменной data. Сохранить файл на php:

if(isset($_POST['data']))
{
	$name = 'img/shot.jpg';
	//записываем, не забывая перекодировать из base64
	file_put_contents($name, base64_decode($_POST['data'] ));
	
	echo 'ok';
}

На этом все, если кому-нибудь пригодится, буду рад.

Автор: PyroRed

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js