В статье собран мой опыт по передаче данных на веб-сервер.
В ходе реализации одного проекта потребовалось организовать автоматическую передачу данных на веб-сервер посредством регламентного задания.
Покопавшись прежде всего в мануалах, синтаксис-помощнике 1С Предприятия, порыскав в Сети, с удивлением обнаружил наличие отсутствия каких-либо вменяемых материалов по теме. Вернее, материалы в Сети есть, но там больше вопросы с абстрактными ответами.
Все нижеприведённые примеры кода всего лишь примеры, хоть и взяты из рабочей конфигурации.
Для передачи данных на сервер методом POST в 1С Предприятии 8.X применяется метод HTTPСоединение
ОтправитьДляОбработки(<Источник>, <Адрес ресурса>, <Имя выходного файла>, <Заголовки>)
(подробнее см. СП)
Прежде всего необходимо создать файл отправки данных (собственно содержимое POST-запроса) и, при необходимости, подготовить двоичные данные.
Определяем разделитель разделов — границу boundary в POST-запросе в формате RFC (подробнее MIME: Организация данных ). В качестве границы может быть использована строка, состоящая из латинских букв и цифр.
Чтобы ничего не выдумывать, воспользуемся штатным классом 1С Предприятия УникальныйИдентификатор.
//Подготовка переменных
Boundary = СтрЗаменить(Строка(Новый УникальныйИдентификатор()), "-", "");
//Определяем имя файла ответа от веб-сервера.
ИмяФайлаОтвета = КаталогВременныхФайлов() + "answer.tmp";
Содержимое файла ответа будет представлять собой какой-либо контент, отданный скриптом на стороне веб-сервера агенту пользователя. Подробнее смотрите пример скрипта php ниже.
Подготавливаем двоичные данные. В моём случае это файл архива zip, но может быть что угодно, хоть изображение.
//Какой-то ранее созданный файл с двоичными данными.
ИмяФайлаДляЗагрузки = "data.zip";
Передача содержимого файла
В 1С Предприятии отсутствуют средства чтения двоичных файлов в обычную строку, как в PHP или PERL.
Одним из способов формирования строкового содержимого двоичного файла является кодирование с помощью штатных функций Base64Строка() или XMLСтрока().
!! Имейте в виду, кодирование по Base64 увеличивает размер передаваемых данных примерно на 30%.
Декодировать содержимое файла на сервере можно при помощи функции php base64_decode() или аналогичных для используемого вами серверного ПО.
Другой способ состоит в использовании штатной функции ОбъединитьФайлы(, ) (подробнее см. в СП). В этом случае формирование файла POST-запроса будет происходить немного сложнее.
Оба способа описаны ниже.
Впрочем, двоичные данные лучше закодировать, иначе при приёме на веб-сервере файл может быть повреждён или не принят вовсе, если он будет содержать URL-значащие символы.
Следует также помнить об ограничениях хостинга и контролировать размер передаваемых данных. Как правило, для php это 2Мб. (см. файл /usr/local/php5/php.ini)
; Maximum allowed size for uploaded files.
upload_max_filesize = 2M
В противном случе, на веб-сервере вы получите пустой POST-запрос.
//Если файл кодируется
//Закодируем содержимое файла по Base64, то есть преобразуем его к URL-неактивному виду.
//Base64 - специальный формат хранения данных в текстовом формате
СодержимоеФайла = Base64Строка(Новый ДвоичныеДанные(ИмяФайлаДляЗагрузки));
//Проверка размера кодированного файла.
Если СтрДлина(СодержимоеФайла) > ДопустимыйРазмерФайлаВБайтах Тогда
//Какие-то ваши действия, возможно
Возврат;
КонецЕсли;
//Если файл не кодируется
//Проверка размера обычного файла.
Файл = Новый Файл(ИмяФайлаДляЗагрузки);
Если Файл.Размер() > ДопустимыйРазмерФайлаВБайтах Тогда
//Какие-то ваши действия, возможно
Возврат;
КонецЕсли;
ИмяФайлаОтправки = КаталогВременныхФайлов() + "post.txt";
Создаём файл отправки или содержимое POST-запроса.
Вариант с передачей кодированного файла.
КодироватьФайл = Истина;
ФайлОтправки = Новый ЗаписьТекста(ИмяФайлаОтправки, КодировкаТекста.ANSI, Символы.ПС, Ложь);
//Определяем раздел двоичных данных
ФайлОтправки.ЗаписатьСтроку("--" + Boundary);
//Указываем имя файла для передачи
//На сервере оно появится в массиве $_FILES['datafile']['name']
ФайлОтправки.ЗаписатьСтроку("Content-Disposition: form-data; name=""datafile""; filename=""" + ИмяФайлаДанных + """");
//Указываем тип передаваемых данных.
//С таким же успехом в Content-Type можно указать application/x-octet-stream
ФайлОтправки.ЗаписатьСтроку("Content-Type: application/x-zip-compressed" + Символы.ПС + Символы.ПС);
//Записываем кодированные двоичные данные
ФайлОтправки.ЗаписатьСтроку(СодержимоеФайла);
ФайлОтправки.ЗаписатьСтроку("--" + Boundary);
//Определяем раздел для каких либо других POST-данных, например содержимого полей виртуальной HTML-формы.
//Соответствует полю HTML-формы input type="submit" name="submit" value="Submit" /
ФайлОтправки.ЗаписатьСтроку("--" + Boundary);
ФайлОтправки.ЗаписатьСтроку("Content-disposition: form-data; name=""submit""" + Символы.ПС);
ФайлОтправки.ЗаписатьСтроку("1");
ФайлОтправки.ЗаписатьСтроку("--" + Boundary);
//Соответствует полю HTML-формы input type="checkbox" name="decode" value="decode" /
ФайлОтправки.ЗаписатьСтроку("--" + Boundary);
ФайлОтправки.ЗаписатьСтроку("Content-disposition: form-data; name=""decode""" + Символы.ПС);
ФайлОтправки.ЗаписатьСтроку(?(КодироватьФайл, "1", "0"));
ФайлОтправки.ЗаписатьСтроку("--" + Boundary);
//Соответствует полю HTML-формы input type="text" name="some_field" value="Some text" /
ФайлОтправки.ЗаписатьСтроку("--" + Boundary);
ФайлОтправки.ЗаписатьСтроку("Content-disposition: form-data; name=""message""" + Символы.ПС);
ФайлОтправки.ЗаписатьСтроку("Передача файла из базы данных 1С Предприятия");
//Завершение сообщения для сервера
ФайлОтправки.ЗаписатьСтроку("--" + Boundary + "--");
ФайлОтправки.Закрыть();
Вариант с передачей обычного файла.
КодироватьФайл = Ложь;
//Определяем массив для процедуры ОбъединитьФайлы
МассивФайловДляОбъединения = Новый Массив;
//Формируем начальный фрагмент файла POST-запроса
ИмяФайлаОтправкиНачало = ПолучитьИмяВременногоФайла("txt");
ФайлОтправкиНачало = Новый ЗаписьТекста(ИмяФайлаОтправкиНачало, КодировкаТекста.ANSI, Символы.ПС, Ложь);
//Определяем раздел двоичных данных
ФайлОтправкиНачало.ЗаписатьСтроку("--" + Boundary);
//Указываем имя файла для передачи
//На сервере оно появится в массиве $_FILES['datafile']['name']
ФайлОтправкиНачало.ЗаписатьСтроку("Content-Disposition: form-data; name=""datafile""; filename=""" + ИмяФайлаДляЗагрузки + """");
//Указываем тип передаваемых данных.
//С таким же успехом в Content-Type можно указать application/x-octet-stream или application/x-zip-compressed
ФайлОтправкиНачало.ЗаписатьСтроку("Content-Type: application/x-octet-stream");
ФайлОтправкиНачало.ЗаписатьСтроку("");
ФайлОтправкиНачало.Закрыть();
МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиНачало);
//Копируем файл для загрузки во временный и добавляем в массив файлов для объединения
ИмяФайлаДляЗагрузкиВременный = ПолучитьИмяВременногоФайла("tmp");
КопироватьФайл(ИмяФайлаДляЗагрузки, ИмяФайлаДляЗагрузкиВременный);
МассивФайловДляОбъединения.Добавить(ИмяФайлаДляЗагрузкиВременный);
//Формируем конечный фрагмент файла POST-запроса
ИмяФайлаОтправкиКонец = ПолучитьИмяВременногоФайла("txt");
ФайлОтправкиКонец = Новый ЗаписьТекста(ИмяФайлаОтправкиКонец, КодировкаТекста.ANSI, Символы.ПС, Ложь);
//Завершение раздела двоичных данных
ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary);
//Определяем раздел для каких либо других POST-данных, например содержимого полей виртуальной HTML-формы.
//Соответствует полю HTML-формы input type="submit" name="submit" value="Submit" /
ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary);
ФайлОтправкиКонец.ЗаписатьСтроку("Content-disposition: form-data; name=""submit""" + Символы.ПС);
ФайлОтправкиКонец.ЗаписатьСтроку("1");
ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary);
//Соответствует полю HTML-формы input type="checkbox" name="decode" value="decode" /
ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary);
ФайлОтправкиКонец.ЗаписатьСтроку("Content-disposition: form-data; name=""decode""" + Символы.ПС);
ФайлОтправкиКонец.ЗаписатьСтроку(?(КодироватьФайл, "1", "0"));
ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary);
//Соответствует полю HTML-формы input type="text" name="some_field" value="Some text" /
ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary);
ФайлОтправкиКонец.ЗаписатьСтроку("Content-disposition: form-data; name=""message""" + Символы.ПС);
ФайлОтправкиКонец.ЗаписатьСтроку("Передача файла из базы данных 1С Предприятия");
//Завершение сообщения для сервера
ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary + "--");
ФайлОтправкиКонец.Закрыть();
МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиКонец);
//Теперь сформированные фрагменты сообщения для сервера объединяем в один файл POST-запроса
ОбъединитьФайлы(МассивФайловДляОбъединения, ИмяФайлаОтправки);
Формируем заголовок POST-запроса.
ЗаголовокHTTP = Новый Соответствие();
//Обязательные поля заголовка
//Укажем формат данных Content-Type
ЗаголовокHTTP.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
//Укажем длину POST-запроса Content-Length
ФайлОтправки = Новый Файл(ИмяФайлаОтправки);
РазмерФайлаОтправки = XMLСтрока(ФайлОтправки.Размер());
ЗаголовокHTTP.Вставить("Content-Length", РазмерФайлаОтправки);
//При необходимости зададим Referer, например таким образом
СтрокаСоединения = СтрокаСоединенияИнформационнойБазы();
СерверИсточник = НСтр(СтрокаСоединения, "Srvr") + НСтр(СтрокаСоединения, "Ref");
ЗаголовокHTTP.Вставить("Referer", СерверИсточник);
Если вы получите ошибку сервера вида Failed sending data to the peer, попробуйте убрать параметр Referer из заголовка.
Инициализируем HTTPСоединение. При необходимости задаём параметры прокси.
СерверПриемник = "www.some_site.ru"; //Естественно, следует указать свой адрес.
Порт = "80"; //Это общепринятый порт. Возможно, для вашего сервера применяется другой. Конкретное значение уточните у хостера.
//ИспользоватьПрокси - какая-то логическая переменная, может быть значение флажка на форме или переключатель
Если ИспользоватьПрокси Тогда
Прокси = Новый ИнтернетПрокси;
Прокси.НеИспользоватьПроксиДляЛокальныхАдресов = Истина;
Прокси.Пароль = "ПарольПрокси"; // укажите своё значение
Прокси.Пользователь = "ПользовательПрокси"; // укажите своё значение
НТТР = Новый HTTPСоединение( СерверПриемник , Порт, , , Прокси);
Иначе
НТТР = Новый HTTPСоединение( СерверПриемник , Порт);
КонецЕсли;
Собственно, отправка данных серверу.
АдресСкрипта = "some_script.php"; //Естественно, следует указать имя своего скрипта.
Попытка
НТТР.ОтправитьДляОбработки(ИмяФайлаОтправки, АдресСкрипта, ИмяФайлаОтвета, ЗаголовокHTTP);
Исключение
//Пример обработки ошибки соединения.
#Если Клиент Тогда
Сообщить("Неудачная попытка соединения: " + ОписаниеОшибки());
#Иначе
ЗаписьЖурналаРегистрации("HTTPСоединение", УровеньЖурналаРегистрации.Ошибка, , , "Неудачная попытка соединения: " + ОписаниеОшибки());
#КонецЕсли
Возврат;
КонецПопытки;
//Удаляем файлы POST-запроса и фрагменты сообщения. Больше они не нужны
УдалитьФайлы(ИмяФайлаОтправки);
Если НЕ КодироватьФайл Тогда
Для каждого ЭлементМассива Из МассивФайловДляОбъединения Цикл
УдалитьФайлы(ЭлементМассива);
КонецЦикла;
КонецЕсли;
Отправили данные, анализируем ответ сервера.
ФайлОтвета = Новый Файл(ИмяФайлаОтвета);
Если ФайлОтвета.Существует() Тогда
ТекстОтвета = Новый ТекстовыйДокумент();
ТекстОтвета.Прочитать(ИмяФайлаОтвета);
Если ТекстОтвета.КоличествоСтрок() > 0 Тогда
ОтветСервера = ТекстОтвета.ПолучитьТекст();
#Если Клиент Тогда
Сообщить(ОтветСервера);
#КонецЕсли
Иначе
#Если Клиент Тогда
Сообщить("Отправка файла на сервер: Получен пустой ответ сервера.");
#Иначе
ЗаписьЖурналаРегистрации("HTTPСоединение", УровеньЖурналаРегистрации.Ошибка, , , "Получен пустой ответ сервера.");
#КонецЕсли
КонецЕсли;
//Удалим файл ответа. Больше он нам не нужен.
УдалитьФайлы(ИмяФайлаОтвета);
Иначе
#Если Клиент Тогда
Сообщить("Отправка файла на сервер: Ответ сервера не получен.");
#Иначе
ЗаписьЖурналаРегистрации("HTTPСоединение", УровеньЖурналаРегистрации.Ошибка, , , "Ответ сервера не получен.");
#КонецЕсли
КонецЕсли;
Подробную реализацию отправки файла с кодированием и без кодирования смотрите в приложенной обработке Загрузка файлов на веб-сервер.epf.
И наконец, примерное содержимое скрипта-приёмника данных на веб-сервере "some_script.php". Скрипт вы также можете извлечь из макета обработки.
//Coded by Sergey aka Porutchik http://forum.aeroion.ru
//http://forum.aeroion.ru/topic446.html
//Читаем текстовые данные POST-запроса
$submit = ( isset($_POST['submit']) ) ? intval($_POST['submit']) : false;
$decode = ( isset($_POST['decode']) ) ? intval($_POST['decode']) : false;
$message = ( isset($_POST['message']) ) ? htmlspecialchars($_POST['message']) : '';
//Проверим user-agent, хотя большого толку от такой проверки нет. См. статью.
if ( $_SERVER['HTTP_USER_AGENT'] != '1C+Enterprise/8.1' )
{
@header('HTTP/1.0 403 Forbidden');
die('Hacking attempt');
}
if ( $submit )
{
//Здесь работаем с содержимым переданного файла.
$uploadFile = $_FILES['datafile'];
$tmp_name = $uploadFile['tmp_name'];
$data_filename = $uploadFile['name'];
if ( !is_uploaded_file($tmp_name) )
{
die('Ошибка при загрузке файла ' . $data_filename);
}
else
{
//Считываем файл в строку
$data = file_get_contents($tmp_name);
if ($decode)
{
//При необходимости декодируем данные
$data = base64_decode($data);
}
//Теперь нормальный файл можно сохранить на диске
if ( !empty($data) && ($fp = @fopen($data_filename, 'wb')) )
{
@fwrite($fp, $data);
@fclose($fp);
}
else
{
die('Ошибка при записи файла ' . $data_filename);
}
@header('HTTP/1.1 200 Ok');
@header('Content-type: text/html; charset=windows-1251');
$answer = "\n" . 'Файл ' . $data_filename . ' успешно загружен. ' . "\n" . 'Переданное сообщение: ' . $message;
print ($answer);
}
}
?>
В завершение статьи процитирую.
Обращаю ваше внимание, что указанная здесь методика освещает возможность загрузки файлов на сервер. Использование их "в чистом виде" без доработки может быть небезопасным, и является потенциальной возможностью для взлома вашего сайта. А именно, отсутствие обработки имени файла, который пришел на сервер, отсутствие авторизации, определения источника, посылающего файл, может дать злоумышленнику возможность загрузить и выполнить вредоносный код на вашем сайте. Рассмотрение способов защиты не входит в данную статью и остается на ваше усмотрение.
Список использованной литературы:
- Генерация HTTP запросов. http://www.phpclub.ru/det.....tp_request
- Загрузка на сервер нескольких файлов. http://php.net/manual/ru/.....ltiple.php (ru.)
- Upload файла через ОтправитьДляОбработки, 1Сv8. http://forum.codeby.net/i.....opic=16780
- Справочник по PHP : Советы : Как отправить файл на сервер. http://www.spravkaweb.ru/.....t/putfile/
- Сайт, для которого всё это реализовывалось: http://agrosnabsklad.oren.....ivolga.ru/