Это продолжение записки "PHP модуль Sockets для HTTP запросов", здесь будет дополнение к ранее написанному и я не буду приводить весь код класса.
Уже при написании HttpSocket мне было интересно узнать, как составить HTTP заголовок для отправки файла. Поэтому я решил разобраться что и как, и дописать эту функциональность.
Как указано в RFC 1867, при отправке файла методом POST, заголовок Content-Type должен принять значение «multipart/form-data». Также Content-Type должен содержать определение boundary:
Content-Type: multipart/form-data; boundary=ABCuniq_idABC
Более того теперь тело запроса разделено на несколько частей начинающихся, разделённых и заканчивающихся строкой указанной в boundary. Каждая часть содержит свой «подзаголовок» и выглядит следующим образом:
--ABCuniq_idABC
Content-Disposition: form-data; name="var_name1"
value1
--ABCuniq_idABC
Content-Disposition: form-data; name="var_name2"
value2
--ABCuniq_idABC
Content-Disposition: form-data; name="img_file"; filename="IMG_20120305_102159.jpg"
Content-Type: image/jpeg
<содержимое файла>
--ABCuniq_idABC--
Обратите внимание, как к строке boundary добавляются символы «--».
Указанная информация о файле, в случае удачной отправки, на принимающей стороне может выглядеть примерно так:
$_FILES = Array(
[img_file] => Array(
[name] => IMG_20120305_102159.jpg
[type] => image/jpeg
[tmp_name] => C:\WINDOWS\Temp\php5DF.tmp
[error] => 0
[size] => 450055
)
)
В дополнение к сделанному, я думал что лучше будет отправлять файл закодированным в base64, чтобы данные не исказились, да и при выводе заголовка он выглядит лучше. Я исследовал такие поля как Content-Encoding, Transfer-Encoding и даже Content-Transfer-Encoding.
Как выяснилось, протокол HTTP принимает двоичные данные (содержимое файла) без проблем и кодирование не нужно. Поля Content-Encoding, Transfer-Encoding нужны соотвественно для сжатия и передачи содержимого по частям, а поле Content-Transfer-Encoding не поддерживается HTTP и используется в почтовых протоколах.
Если с заголовком всё понятно, то написание метода не создаст проблем. Но перед этим, одно «но»: на данный момент в PHP нет надёжного встроенного способа определить MIME-тип произвольного файла. Зато есть простой способ определить MIME-тип изображений. Поэтому я буду отправлять только изображения.
Получившийся у меня код метода класса HttpSocket:
public function reqPostFile($addr, $var_arr = array(), $file_path = null) { $this->parseAddr($addr); $boundary = '>>============' . uniqid() . '<<'; $req_body = ''; foreach ($var_arr as $var_name => $var_val) { $req_body .= '--' . $boundary . NL; $req_body .= 'Content-Disposition: form-data;' . ' name="' . $var_name . '"' . DNL; $req_body .= $var_val . NL; } if (!is_null($file_path)) { if (($mime = getimagesize($file_path)) !== false) { $mime = $mime['mime']; $req_body .= '--' . $boundary . NL; $req_body .= 'Content-Disposition: form-data;' . ' name="img_file";' . ' filename="' . basename($file_path) . '"' . NL; $req_body .= 'Content-Type: ' . $mime . DNL; $req_body .= file_get_contents($file_path) . NL; } else { trigger_error('Mime type of image "' . $file_path . '" can`t be detected', E_USER_ERROR); } } $req_body .= '--' . $boundary . '--' . NL; $header = 'POST ' . $this->query_str . ' HTTP/1.1' . NL; $header .= 'Host: ' . $this->host_str . NL; $header .= 'Content-Type: multipart/form-data;' . ' boundary=' . $boundary . NL; $header .= 'Content-Length: ' . strlen($req_body) . NL; $header .= 'Connection: Close' . DNL; $header .= $req_body; return $this->doQuery($header); }
Комментариев нет:
Отправить комментарий