На работе поставили задачу перевести нескольких сотрудников на терминальный сервер, чтобы все-все делалось на нем, в т. ч. сканирование с локального сканера. Пользователь работает в Windows. Можно было бы пробросить USB, но драйверов для Виндоус Сервера у этой модели сканера не было.

Поискал в сети по запросу Scan over RDP, нашел программы типа Scan Redirector RDP Edition, TSX Scan и т. п., за которые хотят в районе сотни-другой баксов за 1 лицензию, вроде бы даже видел за 800 баксов.

Я не люблю нелицензионный софт, поэтому решил реализовать сам:



Здесь вы видите главную страницу управления удаленным сканером. Плюс моего решения в том, что на терминальном сервере (или любом другом компьютере, с которого захотите сканировать) решительно ничего устанавливать не нужно, нужен только браузер.

Если нажать New JPG scan, увидим следующее:

image

А потом:

image

Картинку можно сохранить по правой кнопке. Нажмем New PDF scan:

image

Нажмем пару раз Scan PDF next page:



По клику по картинке видим ее на весь экран:



Нажмем Delete page 2:



Нажмем Make PDF:



Нажмем Browse images:



Тут можно просмотреть все, что было отсканированно ранее. Как-то так оно работает. Поддерживается любой TWAIN-совместимый сканер. Протестировано в Хроме.

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

Безопасность минимальная: чтобы сканером не воспользовался посторонний, файл называется scan57578.php (имя можно изменить на другое). А в index.php напишите «Access denied».

Что нужно установить на компьютер со сканером?

Как вы уже наверно догадались, нам понадобится веб-сервер. Я использовал Denwer, который в данном случае из пушки по воробьям, но можно использовать любой другой с поддержкой PHP. При этом нужно будет соответствующим образом поправить код, так как в нем используются пути Денвера (диск Z: и т. п.).

Что нужно еще: программа CmdTwain, которая собственно сканирует из командной строки, и консольная же утилита jpeg2pfg. Устанавливаем CmdTwain, запускаем из Пуска CmdTwain\Select Source и выбираем наш сканер. Устанавливаем Денвер, соглашаемся со всем по умолчанию.

Затем на диске Z нужно найти php.ini и задать параметр max_execution_time = 130. Значение выбираем, замерив, сколько длится одно сканирование на вашем сканере. Затем Restart Denwer. Берем с рабочего стола ярлык Start Denwer и кидаем его в автозагрузку. Задаем компьютеру статический IP.

Создаем в папке Z:\home папку с названием равным IP компьютера. В моем случае папка называется 10.74.0.98. В ней создаем папку www, а в ней папку, в которой будут лежать сканы. В моем случае она называется scans. Полный путь выглядит так: Z:\home\10.74.0.98\www\scans.

Веб-сервер настроен. Далее создайте папку Z:\jpeg2pdf. В нее скопируйте jpeg2pdf.exe, который вы скачали по ссылке, которую я давал выше. А также создайте в ней файл makepdf.bat со следующим содержимым:

@echo off
z:
cd z:\home\10.74.0.98\www\scans\%1
z:\jpeg2pdf\jpeg2pdf.exe -z none *.jpg -o %1.pdf
move *.pdf ..

Исправьте IP на нужный. Этот файл создает PDF из набора JPG. В качестве параметра ему передается имя папки с этим набором.

В папку Z:\home\10.74.0.98\www поместите index57578.php (или как вы хотите его назвать) со следующим содержимым:

<?php
$ip = '10.74.0.98';
$pagename = 'scan57578.php';
$folder = 'scans';
$jpeg2pdf = 'z:\\jpeg2pdf\\makepdf.bat';
?>

<html>

<script>

function $_GET(key) {
    var s = window.location.search;
    s = s.match(new RegExp(key + '=([^&=]+)'));
    return s ? s[1] : false;
}

function gettime() {
var d = new Date();
var curr_date = d.getDate();
if (curr_date < 10) curr_date = '0'+curr_date;

var curr_month = d.getMonth() + 1;
if (curr_month < 10) curr_month = '0'+curr_month;

var curr_year = d.getFullYear();

var curr_hour = d.getHours();
if (curr_hour < 10) curr_hour = '0'+curr_hour;

var curr_min = d.getMinutes();
if (curr_min < 10) curr_min = '0'+curr_min;

var curr_sec = d.getSeconds();
if (curr_sec < 10) curr_sec = '0'+curr_sec;

return curr_year+'-'+curr_month+'-'+curr_date+'-'+curr_hour+'-'+curr_min+'-'+curr_sec;
}

function jpgclick() {
document.getElementById('scanprogress').style.display = 'block';
document.getElementById('scanjpg').href='/<?=$pagename?>?scantype=jpg&time='+gettime();
}

function pdfclick() {
document.getElementById('scanprogress').style.display = 'block';
document.getElementById('scanpdf').href='/<?=$pagename?>?scantype=pdf&time='+gettime();
}

function pdfnextclick() {
document.getElementById('scanprogress').style.display = 'block';
document.getElementById('pdfnext').href='/<?=$pagename?>?scantype=pdf&time='+$_GET('time');
}

</script>

<body>
<div style="font-size: 150%">
	     
	<a id="scanpdf" href="#" style="color: #00C" onclick="pdfclick()">New PDF scan</a>        
	<a id="pdfnext" href="#" style="display: none; color: #00C" onclick="pdfnextclick()">Scan PDF next page</a>        
	<a id="makepdf" href="/<?=$pagename?>?scantype=makepdf&time=<?=$_GET['time']?>" style="color: #000; display: none">Make PDF</a><br /><br />
	     
	<a id="scanjpg" href="#" style="color: #00C" onclick="jpgclick()">New JPG scan</a><br /><br />
	     
	<a href="/<?=$folder?>/" target="_blank" style="color: #090">Browse images</a>
</div>
<br /><br />

<div id="scanprogress" style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: white; display: none">
	<h1 style="position: absolute; left: 20%; top: 30%">Scanning in progress. Don't close browser tab.</h1>
</div>


<?php

if($_GET['scantype']=='jpg')
{
	$filename = 'Z:\\home\\'.$ip.'\\www\\'.$folder.'\\'.$_GET['time'].'.jpg';
	if(!is_file($filename)){
		$cmd = '"C:\Program Files\GssEziSoft\CmdTwain\CmdTwain.exe" "'.$filename.'"';
		system($cmd);
	}
	echo ('<a href="/'.$folder.'/'.$_GET['time'].'.jpg"><img style="height: 80%" src="/'.$folder.'/'.$_GET['time'].'.jpg" alt="Picture" /></a>');
}

if($_GET['scantype']=='pdf')
{

	if($_GET['delete']!='')
	{

		$i = -2;
		$files = scandir($folder.'/'.$_GET['time']);
		foreach($files as $filename)
		{
			$i++;
			if ($filename[0] == '.') continue;
			if ($_GET['delete'] == $i) system('del '.'Z:\\home\\'.$ip.'\\www\\'.$folder.'\\'.$_GET['time'].'\\'.$filename);
			
		}
	}
		else

	{
		if (!is_dir($folder.'/'.$_GET['time']))
		{
			system('md Z:\\home\\'.$ip.'\\www\\'.$folder.'\\'.$_GET['time']);
			$filename = 'Z:\\home\\'.$ip.'\\www\\'.$folder.'\\'.$_GET['time'].'\\0001.jpg';
			$cmd = '"C:\Program Files\GssEziSoft\CmdTwain\CmdTwain.exe" "'.$filename.'"';
			system($cmd);
		}
			else
		{
			$n = 1;
			$files = scandir($folder.'/'.$_GET['time']);
			foreach($files as $filename)
			{
				if ($filename[0] == '.') continue;
				$t = substr($filename, 0, -4)+0;
				if ($t > $n) $n = $t;
			}
			$n++;

			$fn = str_pad($n, 4, "0", STR_PAD_LEFT).'.jpg';

			$filename = 'Z:\\home\\'.$ip.'\\www\\'.$folder.'\\'.$_GET['time'].'\\'.$fn;
			$cmd = '"C:\Program Files\GssEziSoft\CmdTwain\CmdTwain.exe" "'.$filename.'"';
			system($cmd);
		}
	}

	$i = -2;
	$files = scandir($folder.'/'.$_GET['time']);
	foreach($files as $filename)
	{
		$i++;
		if ($filename[0] == '.') continue;
		echo "<table style='float: left' width='19%'><tr><td><a href='#' onclick='document.getElementById(\"preview\").style.display=\"block\"; document.getElementById(\"prepic\").src=\"/".$folder."/".$_GET['time']."/".$filename."\"'><img alt='Picture' style='width: 100%' src='/".$folder."/".$_GET['time']."/".$filename."' /></a></td></tr><tr><td align='center'><a href='/".$pagename."?scantype=pdf&time=".$_GET['time']."&delete=".$i."'>Delete page ".$i."</a><br /><br /></td></tr></table>";
	}

	if ($i == 0) echo '<div style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: white"><br /><br /><br /><center><h1><a href="/'.$pagename.'">На главную</a></h1></center></div>';
}

if($_GET['scantype']=='makepdf')
{
	$cmd = $jpeg2pdf.' '.$_GET['time'];
	echo '<span style="display: none">';
	system($cmd);
	echo "</span><h1><a href='scans/".$_GET['time'].".pdf'>Download PDF</a></h1>";
}


?>

<div id="preview" style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: black; color: white; display: none"><img id="prepic" style="position: relative; left: 23%; height: 100%" src="" alt="Picture" /><div style="position: absolute; top: 0; right: 0; color: #FFF"><a href="#" onclick="document.getElementById('preview').style.display='none'; return false" style="text-decoration: none; font-size: 250%; color: white; font-family: arial">X </a></div></div>

</body>

<script>
	if ($_GET('scantype') == 'pdf') document.getElementById('pdfnext').style.display = 'inline';
	if ($_GET('scantype') == 'pdf') document.getElementById('makepdf').style.display = 'inline';
</script>

</html>

Вверху задаются 4 параметра: IP компьютера со сканером, имя файла php, имя папки со сканами, путь к makepdf.bat.

Минусы: нельзя задать качество сканирования – сканирует всегда в 200 DPI и в цвете. Может как-то можно поменять параметры (например в реестре), но для офисных задач они подходят.

И да, я знаю, что функцию gettime() можно написать короче и красивее. Предлагайте самые красивые варианты в комментариях.

Добавлено позже (спасибо Faramant):
Параметры сканирования можно задавать в командной строке. Примеры из документации:
C:\> CmdTwain –c «PAPER LETTER DPI 300 GRAY»…
или (параметры в из файле):
C:\> type map.txt
PAPER A3
DPI 300
COLOR
BR 200.0
C:\> CmdTwain –c map.txt 75 scan.jpg

Спасибо за внимание!
Поделиться с друзьями
-->

Комментарии (20)


  1. mihmig
    13.11.2016 18:49
    +1

    Спасибо за CmdTwain, потестим
    Также рекомендую: http://unit6.ru/twain-web
    Установил в офисе — полёт нормальный 3 года. Единственный недостаток — мерзкий .Net — служба периодически зависает, решил перезапуском. Пробовал обновить версию — не увидела сканера, откатился. Надо будет не полениться и написать разработчикам.

    Сам же несколько лет назад писал на делфи консольную утилиту: сканирует в несколько форматов, разрешений и цветностей, и главное — заданную область — скорость сканирования маленьких бланков выросла в разы!

    А вообще отдельно разработчикам стандарта Twain и некоторым производителям сканеров хочется послать лучи поноса:
    1. Некоторые сканеры сканируют всегда всё, игнорируя параметры.
    2. Некоторые драйверы отображают сканер в системе при его физическом отсутствии.
    3. Два сканера одной модели в системе? Не, не слышали…


  1. Zagrebelion
    13.11.2016 19:54
    +2

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

    Задумка интересная, а за CmdTwain особое спасибо, но не слишком ли много хождений туда-сюда?


    1. akopylov
      13.11.2016 20:27
      +1

      Это задумывалось для сканирования на терминальном сервере, когда сканер подключен к локальному компьютеру.


    1. how
      14.11.2016 16:41

      Сейчас во многих телефонах есть браузер


  1. MaximIs
    13.11.2016 20:48
    -1

    А можно ли автоматизировать данный процесс используя python


    1. akopylov
      13.11.2016 20:49
      +1

      А что в данном процессе не автоматизировано?


      1. MaximIs
        13.11.2016 20:54

        Дело не в том, что здесь что-то не реализовано. Личный интерес, возможно ли сделать тоже самое, но использую Python?


        1. akopylov
          13.11.2016 21:02
          +2

          Я думаю, никаких препятствий к этому нет.


  1. Merkat0r
    13.11.2016 21:40
    +2

    Боже мой...
    image


    1. YourChief
      13.11.2016 23:36
      +3

      Мне другое интересно: 3 тысячи человек просмотрело статью и лишь единицы заметили, что автор даёт удалённый шелл на машину с правами основного пользователя:

      			$filename = 'Z:\\home\\'.$ip.'\\www\\'.$folder.'\\'.$_GET['time'].'\\'.$fn;
      			$cmd = '"C:\Program Files\GssEziSoft\CmdTwain\CmdTwain.exe" "'.$filename.'"';
      			system($cmd);
      

      пишем в адресной строке
      time=%26shutdown%20-r%20-t%200%26
      и хихикаем над пользователем и горе-админом


      1. POS_troi
        13.11.2016 23:57
        +4

        не пользователя а администратора, автор юзает денвер а денвер запускается из под админа что-бы открыть порт 80 :)


      1. akopylov
        14.11.2016 07:19
        +1

        Спасибо, я скорее горе-программист :-)
        Как бы вы починили?
        Первое, что приходит в голову, это проверить time на соответствие шаблону.


        1. alexk24
          14.11.2016 08:03

          escapeshellcmd()
          escapeshellarg()


    1. zomby
      14.11.2016 02:51

      >понимаю почему средняя зарплата админов идет вниз
      Потому что прграммировать не умеют? Ну да, если б все админы это умели, они бы массово меняли профессию при проседании зарплат, пока суммы не поднялись бы до уровня программистов.


  1. YourChief
    13.11.2016 22:25
    -2

    Крайне полезная статья, иллюстрирующая, чем отличается код джуниоров от кода более высокоуровневых специалистов. Буду ссылаться на неё.


    1. SeTM
      13.11.2016 23:58
      -1

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


  1. bano-notit
    14.11.2016 12:39

    Вопрос такой. Указано что никаких программ кроме браузера не нужно, но при этом дальше оказывается нужен денвер, программа для скана из шела и ещё разные интересные вещи) Можете точно указать, что пользователю для её работы кроме браузера ничего не нужно? А то я сюда зашёл, думая, что браузеры теперь и сканировать могут.


    А вообще, интересный способ. Спасибо, и не жалко времени на статью.
    Программка записана в блокнотик "к изучению".


    1. akopylov
      14.11.2016 12:55

      Денвер и прочее нужно установить на компьютер со сканером, на удаленном компьютере нужен только браузер.


      1. bano-notit
        14.11.2016 13:05

        Это я из текста статьи понял уже)


  1. Faramant
    14.11.2016 12:52

    Минусы: нельзя задать качество сканирования – сканирует всегда в 200 DPI и в цвете. Может как-то можно поменять параметры (например в реестре), но для офисных задач они подходят.

    Параметры сканирования можно задавать в командной строке. Примеры их документации:
    C:\> CmdTwain –c «PAPER LETTER DPI 300 GRAY»…
    или (параметры в из файле):
    C:\> type map.txt
    PAPER A3
    DPI 300
    COLOR
    BR 200.0
    C:\> CmdTwain –c map.txt 75 scan.jpg