Загрузка файла на сервер без использования формы
Для HTTP запроса типа POST существует два варианта передачи полей из HTML форм, а именно, используя алгоритм application/x-www-form-urlencoded и multipart/form-data. Алгоритм первого типа создавался давным-давно, когда в языке HTML еще не предусматривали возможность передачи файлов через HTML формы.
Со временем возникла необходимость через формы отсылать еще и файлы. Тогда консорциум W3C взялся за доработку формата POST запроса. К тому времени уже достаточно широко применялся формат MIME (Multipurpose Internet Mail Extensions — многоцелевые расширения протокола для формирования Mail сообщений), поэтому, чтобы не изобретать велосипед заново, решили использовать часть данного формата формирования сообщений для создания POST запросов в протоколе HTTP.
Главное отличие multipart/form-data от application/x-www-form-urlencoded в том, что тело запроса теперь можно поделить на разделы, которые разделяются границами. Каждый раздел может иметь свой собственный заголовок для описания данных, которые в нем хранятся, т.е. в одном запросе можно передавать данные различных типов (как в теле письма можно одновременно с текстом передавать файлы). Пример запроса:
Content-Type: multipart/form-data; boundary=ff4ed67396bc8e1d6dbf19d65b6c6348 Content-Length: 417339 --ff4ed67396bc8e1d6dbf19d65b6c6348 Content-Disposition: form-data; name="name" Евгений --ff4ed67396bc8e1d6dbf19d65b6c6348 Content-Disposition: form-data; name="message" Какое-то сообщение от пользователя --ff4ed67396bc8e1d6dbf19d65b6c6348 Content-Disposition: form-data; name="upload"; filename="image.jpg" Content-Type: image/jpeg Content-Transfer-Encoding: binary ...содержимое файла image.jpg... --ff4ed67396bc8e1d6dbf19d65b6c6348--
Boundary (граница) — это последовательность байтов, которая не должна встречаться внутри передаваемых данных. Content-Length — суммарный объём, включая дочерние заголовки. Само содержимое полей при этом оставляется «как есть».
CURL, multipart/form-data
$file = 'image.jpg'; $postdata = array( 'name' => 'Евгений', 'message' => 'Какое-то сообщение от пользователя', 'upload' => '@'.$file ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://server.com/get.php'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); curl_exec($ch); curl_close($ch);
Файл get.php на сервере http://server.com:
print_r($_POST); print_r($_FILES); move_uploaded_file($_FILES['upload']['tmp_name'], 'image.jpg');
Результат работы:
Array ( [name] => Евгений [message] => Какое-то сообщение от пользователя ) Array ( [upload] => Array ( [name] => image.jpg [type] => application/octet-stream [tmp_name] => C:\Windows\Temp\php504D.tmp [error] => 0 [size] => 416919 ) )
Важный момент: на форуме PHPCLUB.RU встретил упоминание, что может потребоваться указание полного пути файла — иначе CURL выдает ошибку.
$file = 'C:/work/localhost/www/image.jpg';
CURL, application/x-www-form-urlencoded
$file = 'image.jpg'; // данные POST-запроса $postdata = array( 'name' => 'Евгений', 'message' => 'Какое-то сообщение от пользователя', 'upload' => file_get_contents($file) ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://server.com/get.php'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); curl_exec($ch); curl_close($ch);
Файл get.php на сервере http://server.com:
print_r($_POST); file_put_contents('image.jpg', $_POST['upload']);
Результат работы:
Array ( [name] => Евгений [message] => Какое-то сообщение от пользователя [upload] => ...содержимое файла image.jpg... )
Сокеты, multipart/form-data
// устанавливаем соединение с сервером $fp = fsockopen('server.com', 80, $errno, $errstr, 30); if (!$fp) die($errstr.' ('.$errno.')'); $name = 'Евгений'; $message = 'Какое-то сообщение от пользователя'; $file = 'image.jpg'; // содержимое файла $content = file_get_contents($file); // разделитель $boundary = md5(uniqid(time())); $body = '--'.$boundary."\r\n"; $body = $body.'Content-Disposition: form-data; name="name"'."\r\n\r\n"; $body = $body.$name."\r\n"; $body = $body.'--'.$boundary."\r\n"; $body = $body.'Content-Disposition: form-data; name="message"'."\r\n\r\n"; $body = $body.$message."\r\n"; $body = $body.'--'.$boundary."\r\n"; $body = $body.'Content-Disposition: form-data; name="upload"; filename="image.jpg"'."\r\n"; $body = $body.'Content-Type: image/jpeg'."\r\n"; $body = $body.'Content-Transfer-Encoding: binary'."\r\n\r\n"; $body = $body.$content."\r\n"; $body = $body.'--'.$boundary.'--'; // пишем в сокет метод, URI и протокол fwrite($fp, 'POST /get.php HTTP/1.1'."\r\n"); // а также имя хоста fwrite($fp, 'Host: server.com'."\r\n"); // отправляем заголовки fwrite($fp, 'Content-Type: multipart/form-data; boundary='.$boundary."\r\n"); fwrite($fp, 'Content-Length: '.strlen($body)."\r\n\r\n"); // теперь передаем данные fwrite($fp, $body); // получаем ответ $result = ''; while ( !feof($fp) ) $result .= fgets($fp, 1024); // закрываем соединение fclose($fp); // выводим ответ в браузер echo $result;
Файл get.php на сервере http://server.com:
print_r( $_POST ); print_r( $_FILES ); move_uploaded_file($_FILES['upload']['tmp_name'], 'image.jpg')
Результат:
HTTP/1.1 200 OK Server: Apache/2.0 (Win32) PHP/5.1 X-Powered-By: PHP/5.1 Content-Length: 310 Array ( [name] => Евгений [message] => Какое-то сообщение от пользователя ) Array ( [upload] => Array ( [name] => image.jpg [type] => image/jpeg [tmp_name] => C:\Windows\Temp\phpA457.tmp [error] => 0 [size] => 416919 ) )
Сокеты, application/x-www-form-urlencoded
// устанавливаем соединение с сервером $fp = fsockopen('server.com', 80, $errno, $errstr, 30); if (!$fp) die($errstr.' ('.$errno.')'); $file = 'image.jpg'; // содержимое файла $content = file_get_contents($file); // данные POST-запроса $data = 'name=' . urlencode('Евгений') . '&message=' . urlencode('Какое-то сообщение от пользователя') . '&upload='.urlencode($content); // заголовоки запроса $headers = 'POST /get.php HTTP/1.1'."\r\n"; $headers .= 'Host: server.com'."\r\n"; $headers .= 'Content-type: application/x-www-form-urlencoded'."\r\n"; $headers .= 'Content-Length: '.strlen($data)."\r\n\r\n"; // отправляем запрос серверу fwrite($fp, $headers.$data); // получаем ответ $result = ''; while ( !feof($fp) ) $result .= fgets($fp, 1024); // закрываем соединение fclose($fp); // выводим ответ в браузер echo $result;
Файл get.php на сервере http://server.com:
print_r($_POST); file_put_contents('image.jpg', $_POST['upload']);
Результат работы:
HTTP/1.1 200 OK Server: Apache/2.0 (Win32) PHP/5.1 X-Powered-By: PHP/5.1 Transfer-Encoding: chunked Array ( [name] => Евгений [message] => Какое-то сообщение от пользователя [upload] => ...содержимое файла image.jpg... )
Метод PUT
Описанные выше способы работают для относительно небольших файлов (примерно до 2-х мегабайт, для получения более точного значения необходимо смотреть в настройках PHP максимальный объем принимаемых данных методом POST). Чтобы обойти это ограничение, будем передавать файл методом PUT:
$url = 'http://server.com/get.php'; $file = 'image.jpg'; // Открываем передаваемый файл на чтение для дальнейшей его передачи $fp = fopen($file, 'r'); // Инициализируем сеанс CURL $ch = curl_init(); // Указываем URL скрипта, который примет наш запрос. К имени скрипта // добавляем еще две переменные, передаваемые методом GET curl_setopt($ch, CURLOPT_URL, $url . '?name=' . urlencode('Евгений') . '&message=' . urlencode('Какое-то сообщение от пользователя')); // Дескриптор файла, который собираемся передать curl_setopt($ch, CURLOPT_INFILE, $fp); // Указываем размер отправляемого файла curl_setopt($ch, CURLOPT_INFILESIZE, filesize($file)); // Указываем, что файл передается методом PUT curl_setopt($ch, CURLOPT_PUT, 1); // Указываем, что будет производиться закачка на удаленный сервер curl_setopt($ch, CURLOPT_UPLOAD, 1); // Выполняем запрос CURL curl_exec($ch); // Завершаем сеанс CURL curl_close($ch);
Файл get.php на сервере http://server.com:
print_r($_GET); file_put_contents ('image.jpg', file_get_contents('php://input'));
Результат работы:
Array ( [name] => Евгений [message] => Какое-то сообщение от пользователя )
Поиск: $_FILES • $_POST • CURL • HTML • HTTP • PHP • POST • PUT • Web-разработка • application/x-www-form-urlencoded • boundary • move_uploaded_file • multipart/form-data • socket • Файл • сокет