Blog. Just Blog

Отправка файла на сервер с помощью сокетов

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Web-мастеру и не только | Автор: ManHunter
Иногда при разработке проектов возникает задача отправки данных на другие серверы. В случае текстовых данных или небольших объемов бинарных данных можно ограничиться POST- или GET-запросами. Этот способ никаких сложностей не представляет и здесь описываться не будет. А как быть, если на сторонний сервер требуется передать не только данные, но и файлы? Например, вы загружаете картинки через форму на своем сервере, но фактически храните их на каком-нибудь другом. В этом случае нам надо полностью проэмулировать работу браузера, а именно его обмен данными с удаленным сервером, как будто бы пользователь заполнил и отправил форму с web-страницы. Предположим, что для загрузки файлов на сервер используется следующая форма:
  1. <form action="/uploader.php" method="post" enctype="multipart/form-data">
  2.     Выберите файл: <input type="file" name="my_file"><br>
  3.     Описание: <input type="text" name="ext_field_1"><br>
  4.     <input type="submit" value="Загрузить">
  5. </form>
C HTML все понятно, а вот чтобы понять как передаются данные после нажатия в браузере кнопки "Загрузить", рекомендую прочитать официальную документацию. В заголовках HTTP-запроса, отправляемого на сервер, обязательно должен присутствовать заголовок "Content-Type: multipart/form-data; boundary=NNNNN". Значение параметра boundary - это уникальная строка, используемая в качестве разделителя полей в теле запроса. Она не должна встречаться ни в одном значении поля формы. Обычно для этого достаточно сгенерировать хэш от случайного числа и использовать его в качестве искомого значения.

Каждое текстовое поле в теле запросе кодируется следующим образом:

--boundary
Content-Disposition: form-data; name="имя_поля_в_форме"

значение поля

Файлы при передаче кодируются несколько иначе:

--boundary
Content-Disposition: form-data; name="имя_поля" filename="имя_файла"
Content-Type: mime-тип файла

двоичное содержимое файла

Как узнать mime-тип файла по его расширению, написано в этой статье. В заголовках запроса также обязательно должны присутствовать данные об общем размере тела запроса, а именно длина всех кодированных полей формы. Эта информация передается заголовком "Content-Length: NNNN". То есть сперва в вашем обработчике формируется тело запроса, а только после него заголовок.

Теперь мы знаем какие данные и в каком виде надо передавать, осталось сделать рабочий скрипт. Сперва, как я говорил выше, формируем тело запроса.
  1. // Данные для формирования запроса
  2. $boundary=md5(rand());
  3. $crlf "\r\n"
  4.  
  5. // Название поля в форме отправки файла
  6. $field_name='my_file';
  7. // Имя файла
  8. $file_name='my_file.bin';
  9. // Тип файла
  10. $file_type='application/octet-stream';
  11. // Содержимое файла
  12. $file_data=str_repeat('x',100);
  13.  
  14. // Подготовить тело запроса с файлом
  15. $body  '--'.$boundary.$crlf;
  16. $body .= 'Content-Disposition: form-data; ';
  17. $body .= 'name="'.$field_name.'"; filename="'.$file_name.'"'.$crlf;
  18. $body .= 'Content-Type: '.$file_type.$crlf;
  19. $body .= $crlf;
  20. $body .= $file_data;
  21. $body .= $crlf;
  22. // Дополнительные поля формы (если надо)
  23. $body .= '--'.$boundary.$crlf;
  24. $body .= 'Content-Disposition: form-data; name="ext_field_1"'.$crlf;
  25. $body .= $crlf;
  26. $body .= 'Some text data....'.$crlf;
  27. // Конец тела запроса
  28. $body .= '--'.$boundary.$crlf;
  29. $body .= $crlf;
Тело запроса собрано, переходим к формированию заголовка запроса. Здесь приведен минимальный пример, в реальной ситуации может потребоваться передавать в заголовке cookies для авторизации или идентификации сессии, адрес referer или какие-нибудь иные данные, если удаленный сервер требует их наличия. Отследить это все можно при помощи расширения LiveHTTPheaders для браузера Firefox.
  1. $host='www.example.ru'// Хост для приема файла
  2. $url='/uploader.php';   // URL скрипта для приема файла
  3.  
  4. // Подготовить заголовок POST-запроса
  5. $header 'POST '.$url.' HTTP/1.1'.$crlf;
  6. $header .= 'Host: '$host $crlf;
  7. $header .= 'User-Agent: Uploader/1.0.0'.$crlf;
  8. $header .= 'Content-Type: multipart/form-data; boundary='.$boundary.$crlf;
  9. $header .= 'Content-Length: '.strlen($body).$crlf;
  10. $header .= 'Connection: keep-alive'.$crlf;
  11. $header .= 'Keep-Alive: 300'.$crlf;
  12. $header .= $crlf;
Последний шаг - отправка запроса удаленному серверу. Для этого воспользуемся функцией PHP fsockopen.
  1. $port=80;
  2. $timeout=30;
  3.  
  4. // Открыть соединение с сервером
  5. $f=fsockopen($host$port$errno$errstr$timeout);
  6.  
  7. // Отправить файл на сервер данные
  8. fputs($f$header.$body);
  9.  
  10. // Получить ответ от удаленного сервера
  11. $response='';
  12. while(!feof($f)) {
  13.     $line=fgets($f,1000);
  14.     if ($line=='') { break; }
  15.     $response.=$line
  16. }    
  17. echo $response;
  18.  
  19. // Закрыть соединение с сервером
  20. fclose($f);
Как проверить корректность работы скрипта? Сделаем свой скрипт uploader.php следующего содержания и пропишем его в качестве приемника отправляемых данных:
  1. echo 'Данные $_POST:<br>';
  2. print_r($_POST);
  3. echo 'Данные $_FILES:<br>';
  4. print_r($_FILES);
Результат его работы:

Данные $_POST:
Array (
    [ext_field_1] => Some text data....
)

Данные $_FILES:
Array (
    [my_file] => Array (
        [name] => my_file.bin
        [type] => application/octet-stream
        [tmp_name] => /tmp/php1P8imr
        [error] => 0
        [size] => 100
    )
)

Как видите, данные из несуществующей формы отправляются и принимаются успешно, в том числе и файлы. Обратите внимание, что в качестве $file_data могут быть любые данные, то есть мы можем отправлять на удаленные серверы "виртуальные" файлы, которые сгенерированы динамически и существуют только в памяти нашего процесса. Для больших объемов данных информацию на сервер можно отправлять по частям, однако заголовок Content-Length в любом случае должен содержать правильное значение.

Это не единственный способ передачи файлов, можно также воспользоваться cURL с методом PUT или функциями PHP file_put_contents и ftp_put, все зависит от поставленной задачи. Ну и конечно же не забывайте о безопасности ваших проектов и всегда проверяйте все полученные данные.

Поделиться ссылкой ВКонтакте
Просмотров: 12113 | Комментариев: 10

Метки: PHP
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (03.05.2016 в 17:42):
Я читал, что порог вхождения в PHP очень низкий, но полным идиотам там что делать? Иди вагоны разгружай, если по готовой статье не в состоянии разобраться.
Максим (03.05.2016 в 10:41):
Неужели трудно было до конца написать?
Если уж сказали, что передавать нужно двоичное содержимое файла, то почему нет примера как это сделать?
Если уж сказали что большие файлы можно отправлять по частям, где опять же пример?
Зачем вообще что-то писали? Ну написали бы, что можно с помощью PHP отправлять файлы и пусть все ищут в гугле как это сделать...
Alexey (03.11.2014 в 09:36):
Спасибо огромнейшее! Все очень доходчиво. Даже я, полный неуч, и то начал хоть что-то понимать прочтя эту статью.
Все отлично работает. Просто выручили. Пожалуй, самая ясная и адекватная статья по данной проблеме, из всех прочитанных мною ранее.
ManHunter (24.04.2013 в 11:42):
Это кусок html, выдранный хер знает откуда, а не команда. По клику на картинке открывается popup-окно и выполняется функция CreateFile, которая хер знает что делает и которой я тут не наблюдаю.
А вообще это не имеет НИКАКОГО отношения к теме статьи. Если нужен разбор скриптов - деньги в кассу и после этого может быть помогу.
Евгений (24.04.2013 в 11:36):
Всем привет
Можно ли отправить файл на сревре без формы
по следующей команде?
<area shape="rect" coords="334,640,390,694" href="#1" onClick="CreateFile();window.open('TraNS.html', 'newWin', 'Toolbar=0, Location=0, Directories=0, Status=0, Menubar=0, Scrollbars=0, Resizable=0, Copyhistory=1, Width=800, Height=600')">
User (10.12.2012 в 10:41):
у меня файлы приходят с ошибкой номер 3

нормально отправляется только обычный текст, и то, только без переносов \r\n
ПРОГтч (02.12.2012 в 23:53):
спасибо на самом деле работает  здорова !!!
здорова
ManHunter (19.06.2012 в 14:00):
Сперва выучи матчасть. Через cURL ты можешь отправить ТОЛЬКО реальный файл, существующий на хосте отправителя. Через сокеты ты можешь отправлять любые данные, в том числе сгенерированные на лету или из переменных или вообще похер откуда.
VY_CMa (19.06.2012 в 13:56):
На самом деле для этого удобнее использовать cUrl.
morgot (03.02.2012 в 16:22):
Спасибо за статью, очень актуально и в тему!

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0059 сек.) / Память: 4.5 Mb
Наверх