
Отправка файла на сервер с помощью сокетов
Иногда при разработке проектов возникает задача отправки данных на другие серверы. В случае текстовых данных или небольших объемов бинарных данных можно ограничиться POST- или GET-запросами. Этот способ никаких сложностей не представляет и здесь описываться не будет. А как быть, если на сторонний сервер требуется передать не только данные, но и файлы? Например, вы загружаете картинки через форму на своем сервере, но фактически храните их на каком-нибудь другом. В этом случае нам надо полностью проэмулировать работу браузера, а именно его обмен данными с удаленным сервером, как будто бы пользователь заполнил и отправил форму с web-страницы. Предположим, что для загрузки файлов на сервер используется следующая форма:Code (HTML) : Убрать нумерацию
- <form action="/uploader.php" method="post" enctype="multipart/form-data">
- Выберите файл: <input type="file" name="my_file"><br>
- Описание: <input type="text" name="ext_field_1"><br>
- <input type="submit" value="Загрузить">
- </form>
Каждое текстовое поле в теле запросе кодируется следующим образом:
--boundary
Content-Disposition: form-data; name="имя_поля_в_форме"
значение поля
Файлы при передаче кодируются несколько иначе:
--boundary
Content-Disposition: form-data; name="имя_поля" filename="имя_файла"
Content-Type: mime-тип файла
двоичное содержимое файла
Как узнать mime-тип файла по его расширению, написано в этой статье. В заголовках запроса также обязательно должны присутствовать данные об общем размере тела запроса, а именно длина всех кодированных полей формы. Эта информация передается заголовком "Content-Length: NNNN". То есть сперва в вашем обработчике формируется тело запроса, а только после него заголовок.
Теперь мы знаем какие данные и в каком виде надо передавать, осталось сделать рабочий скрипт. Сперва, как я говорил выше, формируем тело запроса.
Code (PHP) : Убрать нумерацию
- // Данные для формирования запроса
- $boundary=md5(rand());
- $crlf = "\r\n";
- // Название поля в форме отправки файла
- $field_name='my_file';
- // Имя файла
- $file_name='my_file.bin';
- // Тип файла
- $file_type='application/octet-stream';
- // Содержимое файла
- $file_data=str_repeat('x',100);
- // Подготовить тело запроса с файлом
- $body = '--'.$boundary.$crlf;
- $body .= 'Content-Disposition: form-data; ';
- $body .= 'name="'.$field_name.'"; filename="'.$file_name.'"'.$crlf;
- $body .= 'Content-Type: '.$file_type.$crlf;
- $body .= $crlf;
- $body .= $file_data;
- $body .= $crlf;
- // Дополнительные поля формы (если надо)
- $body .= '--'.$boundary.$crlf;
- $body .= 'Content-Disposition: form-data; name="ext_field_1"'.$crlf;
- $body .= $crlf;
- $body .= 'Some text data....'.$crlf;
- // Конец тела запроса
- $body .= '--'.$boundary.$crlf;
- $body .= $crlf;
Code (PHP) : Убрать нумерацию
- $host='www.example.ru'; // Хост для приема файла
- $url='/uploader.php'; // URL скрипта для приема файла
- // Подготовить заголовок POST-запроса
- $header = 'POST '.$url.' HTTP/1.1'.$crlf;
- $header .= 'Host: '. $host . $crlf;
- $header .= 'User-Agent: Uploader/1.0.0'.$crlf;
- $header .= 'Content-Type: multipart/form-data; boundary='.$boundary.$crlf;
- $header .= 'Content-Length: '.strlen($body).$crlf;
- $header .= 'Connection: keep-alive'.$crlf;
- $header .= 'Keep-Alive: 300'.$crlf;
- $header .= $crlf;
Code (PHP) : Убрать нумерацию
- $port=80;
- $timeout=30;
- // Открыть соединение с сервером
- $f=fsockopen($host, $port, $errno, $errstr, $timeout);
- // Отправить файл на сервер данные
- fputs($f, $header.$body);
- // Получить ответ от удаленного сервера
- $response='';
- while(!feof($f)) {
- $line=fgets($f,1000);
- if ($line=='') { break; }
- $response.=$line;
- }
- echo $response;
- // Закрыть соединение с сервером
- fclose($f);
Code (PHP) : Убрать нумерацию
- echo 'Данные $_POST:<br>';
- print_r($_POST);
- echo 'Данные $_FILES:<br>';
- 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, все зависит от поставленной задачи. Ну и конечно же не забывайте о безопасности ваших проектов и всегда проверяйте все полученные данные.
Просмотров: 12359 | Комментариев: 10
Метки: PHP

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(03.05.2016 в 17:42):
Я читал, что порог вхождения в PHP очень низкий, но полным идиотам там что делать? Иди вагоны разгружай, если по готовой статье не в состоянии разобраться.

Максим
(03.05.2016 в 10:41):
Неужели трудно было до конца написать?
Если уж сказали, что передавать нужно двоичное содержимое файла, то почему нет примера как это сделать?
Если уж сказали что большие файлы можно отправлять по частям, где опять же пример?
Зачем вообще что-то писали? Ну написали бы, что можно с помощью PHP отправлять файлы и пусть все ищут в гугле как это сделать...
Если уж сказали, что передавать нужно двоичное содержимое файла, то почему нет примера как это сделать?
Если уж сказали что большие файлы можно отправлять по частям, где опять же пример?
Зачем вообще что-то писали? Ну написали бы, что можно с помощью 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')">
Можно ли отправить файл на сревре без формы
по следующей команде?
<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
нормально отправляется только обычный текст, и то, только без переносов \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):
Спасибо за статью, очень актуально и в тему!

Добавить комментарий
Заполните форму для добавления комментария
