Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail 02.02.2012 | Категория: 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, все зависит от поставленной задачи. Ну и конечно же не забывайте о безопасности ваших проектов и всегда проверяйте все полученные данные.

Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на Я.ру Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Поделиться ссылкой на Friendfeed Добавить в Яндекс.Закладки Добавить в закладки Memori Добавить в закладки Google
Просмотров: 1629 | Комментариев: 1

Комментарии

Отзывы посетителей сайта о статье
morgot (03.02.2012 в 16:22):
Спасибо за статью, очень актуально и в тему!

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

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

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