Как получить файл через HTTP PUT с PHP

это то, что беспокоило меня некоторое время.. Я создаю RESTful API, который должен получать файлы в некоторых случаях.

при использовании HTTP POST, мы можем читать data from $_POST и files from $_FILES.

при использовании HTTP GET, мы можем читать data from $_GET и files from $_FILES.

Впрочем, при использовании HTTP PUT, AFAIK единственный способ считывания данных-использовать php://input stream.

все хорошо и хорошо, пока я не хочу отправить файл через HTTP PUT. Теперь php: / / input stream больше не работает так, как ожидалось, так как у него есть файл.

вот как я в настоящее время читаю данные по запросу PUT:

(который отлично работает, пока нет файлов, опубликованных)

$handle  = fopen('php://input', 'r');
$rawData = '';
while ($chunk = fread($handle, 1024)) {
    $rawData .= $chunk;
}

parse_str($rawData, $data);

когда я затем вывожу rawData, он показывает

-----ZENDHTTPCLIENT-44cf242ea3173cfa0b97f80c68608c4c
Content-Disposition: form-data; name="image_01"; filename="lorem-ipsum.png"
Content-Type: image/png; charset=binary

�PNG
���...etc etc...
���,
-----ZENDHTTPCLIENT-8e4c65a6678d3ef287a07eb1da6a5380
Content-Disposition: form-data; name="testkey"

testvalue
-----ZENDHTTPCLIENT-8e4c65a6678d3ef287a07eb1da6a5380
Content-Disposition: form-data; name="otherkey"

othervalue

кто-нибудь знает, как правильно получать файлы через HTTP PUT или как анализировать файлы из потока php://input?

===== обновление #1 =====

Я пробовал только вышеуказанный метод, на самом деле не имею понятия о том, что я могу сделать еще.

Я не получил никаких ошибок с помощью этого метода, кроме того, что я не получаю желаемый результат опубликованных данных и файлов.

===== обновление #2 =====

Я отправляю этот тестовый запрос с помощью Zend_Http_Client, следующим образом: (не было никаких проблем с Zend_Http_Client так далеко)

$client = new Zend_Http_Client();
$client->setConfig(array(
    'strict'       => false,
    'maxredirects' => 0,
    'timeout'      => 30)
);
$client->setUri( 'http://...' );
$client->setMethod(Zend_Http_Client::PUT);
$client->setFileUpload( dirname(__FILE__) . '/files/lorem-ipsum.png', 'image_01');
$client->setParameterPost(array('testkey' => 'testvalue', 'otherkey' => 'othervalue');
$client->setHeaders(array(
    'api_key'    => '...',
    'identity'   => '...',
    'credential' => '...'
));

===== решение =====

оказывается, я сделал некоторые неправильные предположения, в основном, что HTTP PUT будет похож на HTTP POST. Как вы можете прочитать ниже, DaveRandom объяснил мне, что HTTP PUT не предназначен для передачи нескольких файлов по одному запросу.

теперь я переместил передачу formdata из тела в строку запроса url. Тело теперь содержит содержимое одного файла.

дополнительные информация, прочитал ответ Даверандома. Это эпично.

5 ответов


данные, которые вы показываете, не отображают допустимое тело запроса PUT (ну, это мог бы, но я очень сомневаюсь в этом). То, что он показывает, является multipart/form-data тело запроса-тип MIME, используемый при загрузке файлов через HTTP POST через HTML-форму.

запросы PUT должны точно дополнять ответ на запрос GET - они отправляют вам содержимое файла в теле сообщения и ничего больше.

по сути, я говорю, что это не ваш код для получения файл, который неверен, это код, который делает запрос-код клиента неверен, а не код, который вы показываете здесь (хотя parse_str() call-бессмысленное упражнение).

если вы объясните, что такое клиент (браузер, скрипт на другом сервере и т. д.), Я могу помочь вам сделать это дальше. Как бы то ни было, соответствующий метод запроса для тела запроса, которое вы изображаете, - это POST, а не PUT.


давайте сделаем шаг назад от проблемы и посмотреть на HTTP протокол в целом-в частности, сторона запроса клиента-надеюсь, это поможет вам понять, как все это должно работать. Во-первых, немного истории (если вы не заинтересованы в этом, не стесняйтесь пропустить этот раздел).

история

HTTP изначально был разработан как механизм для извлечения HTML-документов с удаленных серверов. Сначала он эффективно поддерживал только метод GET, при котором клиент запрашивал документ по имени, и сервер вернет его клиенту. Первая публичная спецификация HTTP, обозначенная как HTTP 0.9, появилась в 1991 году - и если вам интересно, вы можете прочитать ее здесь.

спецификация HTTP 1.0 (формализована в 1996 году с RFC 1945) значительно расширил возможности протокола, добавив методы HEAD и POST. Он не был обратно совместим с HTTP 0.9 из-за изменения формата ответа-a добавлен код ответа, а также возможность включения метаданных для возвращаемого документа в виде заголовков формата MIME - пар данных ключ/значение. HTTP 1.0 также абстрагировал протокол от HTML, позволяя передавать файлы и данные в других форматах.

HTTP 1.1, форма протокола, который почти исключительно используется сегодня, построена поверх HTTP 1.0 и была разработана для обратной совместимости с реализациями HTTP 1.0. Она была унифицирована в 1999 с RFC 2616. Если вы разработчик, работающий с HTTP, познакомьтесь с этим документом-это ваша Библия. Понимание этого полностью даст вам значительное преимущество перед вашими сверстниками, которые этого не делают.

переходите уже к делу

HTTP работает на архитектуре запрос-ответ-клиент отправляет сообщение запроса на сервер, сервер возвращает ответное сообщение клиенту.

сообщение-запрос включает в себя метод, URI и, необязательно, несколько заголовков. Метод запроса - это то, к чему относится этот вопрос, поэтому это то, что я рассмотрю здесь наиболее подробно, но сначала важно точно понять, что мы имеем в виду, когда говорим об URI запроса.

URI-это расположение на сервере запрашиваемого нами ресурса. В общем, это состоит из путь компонент, и дополнительно строку запроса. Есть обстоятельства там, где могут присутствовать и другие компоненты, но для простоты мы пока их проигнорируем.

давайте представим, что вы типа http://server.domain.tld/path/to/document.ext?key=value в адресную строку Вашего браузера. Браузер разбирает эту строку и определяет, что ей необходимо подключиться к HTTP-серверу по адресу server.domain.tld, и попросите документ на /path/to/document.ext?key=value.

сгенерированный запрос HTTP 1.1 будет выглядеть (как минимум) следующим образом:

GET /path/to/document.ext?key=value HTTP/1.1
Host: server.domain.tld

первая часть запроса слово GET - это метод запроса. Следующая часть-это путь к файлу, который мы запрашиваем, - это URI запроса. В конце первой строки находится идентификатор используемой версии протокола. В следующей строке вы можете увидеть заголовок в формате MIME, называемый Host. HTTP 1.1 мандаты, что Host: заголовок будет включен в каждый запрос. Это единственный заголовок, в котором это верно.

запрос URI разбивается на две части-все слева вопросительного знака ? - это путь, все справа от него-это строку запроса.

Методы Запроса

RFC 2616 (HTTP/1.1) определяет 8 методов запросу.

OPTIONS

метод OPTIONS редко используется. Он предназначен как механизм для определения того, какую функциональность поддерживает сервер, прежде чем пытаться использовать сервис, который может предоставить сервер.

С моей головы, единственное место в довольно распространенном использовании, которое я могу думать о том, где это используется, - это при открытии документов в Microsoft office непосредственно по HTTP из Internet Explorer-Office отправит запрос параметров на сервер, чтобы определить, поддерживает ли он метод PUT для конкретного URI, и если да, то он откроет документ таким образом, чтобы пользователь мог сохранить свои изменения в документе непосредственно сервер. Эта функция тесно интегрирована в этих конкретных приложений Microsoft.

GET

это, безусловно, самый распространенный метод в повседневном использовании. Каждый раз, когда вы загружаете обычный документ в свой веб-браузер, это будет запрос GET.

метод GET запрашивает, чтобы сервер вернул определенный документ. Единственные данные, которые должны быть переданы на сервер, - это информация, необходимая серверу для определения документ должен быть возвращен. Это может включать информацию, которую сервер может использовать для динамического создания документа, который отправляется в виде заголовков и / или строки запроса в URI запроса. Пока мы находимся в теме-Cookies отправляются в заголовках запроса.

HEAD

этот метод идентичен методу GET, с одним отличием-сервер не будет возвращать запрошенный документ, если будет возвращать только заголовки, которые будут включены в ответ. Это полезно для определения, например, если конкретный документ существует без необходимости передачи и обработки всего документа.

POST

это второй наиболее часто используемый метод, и, пожалуй, самый сложный. Запросы метода POST почти исключительно используются для вызова некоторых действий на сервере, которые могут изменить его состояние.

запрос POST, в отличие от GET и HEAD, может (и обычно включает) включать некоторые данные в тело сообщение запроса. Эти данные могут быть в любом формате, но чаще всего это строка запроса (в том же формате, что и в URI запроса) или составное сообщение, которое может передавать пары ключ/значение вместе с вложениями файлов.

многие HTML-формы используют метод POST. Чтобы загрузить файлы из браузера, вам нужно будет использовать метод POST для вашей формы.

метод POST семантически несовместим с RESTful APIs, потому что он не идемпотентных. То есть второй идентичный запрос POST может привести к дальнейшему изменению состояния сервера. Это противоречит "" ограничение отдыха без гражданства.

PUT

это непосредственно дополняет GET. Если запрос GET указывает, что сервер должен вернуть документ в расположение, указанное URI запроса в теле ответа, метод PUT указывает, что сервер должен хранить данные в теле запроса по адресу расположение, указанное URI запроса.

DELETE

это означает, что сервер должен уничтожить документ в месте, указанном URI запроса. Очень немногие интернет-реализации HTTP-сервера будут выполнять какие-либо действия при получении запроса на удаление по довольно очевидным причинам.

TRACE

это обеспечивает механизм уровня application-layer, позволяющий клиентам проверять запрос, который он отправил как выглядит к тому времени, когда он достигает сервера назначения. Это в основном полезно для определения влияния прокси-серверов между клиентом и конечным сервером на сообщение запроса.

CONNECT

HTTP 1.1 резервирует имя для метода CONNECT, но не определяет его использование или даже его назначение. Некоторые реализации прокси-сервера с тех пор использовали метод CONNECT для облегчения туннелирования HTTP.


Я никогда не пробовал использовать PUT (получить сообщение и файлы были достаточны для моих нужд), но этот пример из документов php, поэтому он может помочь вам (http://php.net/manual/en/features.file-upload.put-method.php):

<?php
/* PUT data comes in on the stdin stream */
$putdata = fopen("php://input", "r");

/* Open a file for writing */
$fp = fopen("myputfile.ext", "w");

/* Read the data 1 KB at a time
   and write to the file */
while ($data = fread($putdata, 1024))
  fwrite($fp, $data);

/* Close the streams */
fclose($fp);
fclose($putdata);
?>

вот решение, которое я нашел наиболее полезным.

$put = array(); parse_str(file_get_contents('php://input'), $put);

$put будет массивом, как вы привыкли видеть в $_POST, за исключением теперь вы можете следовать истинному протоколу HTTP REST.


просто следуйте тому, что он говорит в DOC:

<?php
/* PUT data comes in on the stdin stream */
$putdata = fopen("php://input", "r");

/* Open a file for writing */
$fp = fopen("myputfile.ext", "w");

/* Read the data 1 KB at a time
   and write to the file */
while ($data = fread($putdata, 1024))
  fwrite($fp, $data);

/* Close the streams */
fclose($fp);
fclose($putdata);
?>

этой должны прочитайте весь файл, который находится в потоке PUT и сохраните его локально, тогда вы можете делать с ним все, что хотите.


используйте POST и включите X - заголовок, чтобы указать фактический метод (в этом случае). Обычно так работает брандмауэр, который не позволяет использовать другие методы, кроме GET и POST. Просто объявите PHP багги (так как он отказывается обрабатывать многопартийные PUT полезные нагрузки, это багги) и относитесь к нему, как к устаревшему/драконовскому брандмауэру.

мнения о том, что положить означает по отношению к получить, - это просто мнения. HTTP не делает такого требования. В нем просто говорится: "эквивалент".. дизайнер должен определить, что означает "эквивалент". Если ваш дизайн может принять загрузку нескольких файлов и создать "эквивалентное" представление для последующего GET для того же ресурса, это просто отлично и денди, как технически, так и философски, со спецификациями HTTP.