如何使用 PHP 通过 HTTP PUT 接收文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12005790/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to receive a file via HTTP PUT with PHP
提问by Maurice
This is something that has been bugging me for a while.. I'm building of a RESTful API that has to receive files on some occasions.
这是困扰我一段时间的事情。我正在构建一个 RESTful API,它必须在某些情况下接收文件。
When using HTTP POST, we can read data from $_POSTand files from $_FILES.
使用时HTTP POST,我们可以读取data from $_POST和files from $_FILES。
When using HTTP GET, we can read data from $_GETand files from $_FILES.
使用时HTTP GET,我们可以读取data from $_GET和files from $_FILES。
However, when using HTTP PUT, AFAIK the only way to read data is to use the php://input stream.
但是,在使用 时HTTP PUT,AFAIK 读取数据的唯一方法是使用php://input stream.
All good and well, untill I want to send a file over HTTP PUT. Now the php://input stream doesn't work as expected anymore, since it has a file in there as well.
一切都很好,直到我想通过 HTTP PUT 发送文件。现在 php://input 流不再按预期工作,因为它也有一个文件。
Here's how I currently read data on a PUT request:
以下是我目前在 PUT 请求中读取数据的方式:
(which works great as long as there are no files posted)
(只要没有发布文件,它就很好用)
$handle = fopen('php://input', 'r');
$rawData = '';
while ($chunk = fread($handle, 1024)) {
$rawData .= $chunk;
}
parse_str($rawData, $data);
When I then output rawData, it shows
当我输出 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
Does anyone know how to properly receive files over HTTP PUT, or how to parse files out of the php://input stream?
有谁知道如何通过 HTTP PUT 正确接收文件,或者如何从 php://input 流中解析文件?
===== UPDATE #1 =====
===== 更新 #1 =====
I have tried only the above method, don't really have a clue as to what I can do else.
我只试过上面的方法,真的不知道我还能做什么。
I have gotten no errors using this method, besides that I don't get the desired result of the posted data and files.
使用这种方法我没有得到任何错误,除了我没有得到发布的数据和文件的预期结果。
===== UPDATE #2 =====
===== 更新#2 =====
I'm sending this test request using Zend_Http_Client, as follows: (haven't had any problems with Zend_Http_Client so far)
我使用 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' => '...'
));
===== SOLUTION =====
===== 解决方案 =====
Turns out I made some wrong assumptions, mainly that HTTP PUT would be similar to HTTP POST. As you can read below, DaveRandom explained to me that HTTP PUT is not meant for transferring multiple files on the same request.
结果我做了一些错误的假设,主要是 HTTP PUT 类似于 HTTP POST。正如您在下面看到的,DaveRandom 向我解释说 HTTP PUT 并不意味着在同一个请求上传输多个文件。
I have now moved the transferring of formdata from the body to url querystring. The body now holds the contents of a single file.
我现在已经将 formdata 从正文转移到 url 查询字符串。正文现在包含单个文件的内容。
For more information, read DaveRandom's answer. It's epic.
有关更多信息,请阅读 DaveRandom 的回答。这是史诗般的。
回答by DaveRandom
The data you show does not depict a valid PUT request body (well, it could, but I highly doubt it). What it shows is a multipart/form-datarequest body - the MIME type used when uploading files via HTTP POST through an HTML form.
您显示的数据没有描述有效的 PUT 请求正文(好吧,它可以,但我非常怀疑)。它显示的是multipart/form-data请求正文 - 通过 HTML 表单通过 HTTP POST 上传文件时使用的 MIME 类型。
PUT requests should exactly compliment the response to a GET request - they send you the file contents in the message body, and nothing else.
PUT 请求应该完全补充对 GET 请求的响应 - 它们向您发送消息正文中的文件内容,而不是其他任何内容。
Essentially what I'm saying is that it is not your code to receive the file that is wrong, it is the code that is making the request - the client code is incorrect, not the code you show here (although the parse_str()call is a pointless exercise).
基本上我要说的是,接收错误文件的代码不是您的代码,而是发出请求的代码 - 客户端代码不正确,而不是您在此处显示的代码(尽管parse_str()调用是毫无意义的)锻炼)。
If you explain what the client is (a browser, script on other server, etc) then I can help you take this further. As it is, the appropriate request method for the request body that you depict is POST, not PUT.
如果您解释什么是客户端(浏览器、其他服务器上的脚本等),那么我可以帮助您进一步了解。实际上,您描述的请求正文的适当请求方法是 POST,而不是 PUT。
Let's take a step back from the problem, and look at the HTTP protocol in general - specifically the client request side - hopefully this will help you understand how all of this is supposed to work. First, a little history (if you're not interested in this, feel free to skip this section).
让我们从这个问题退后一步,看看一般的 HTTP 协议——特别是客户端请求端——希望这能帮助你理解所有这些应该如何工作。首先是一点历史(如果您对此不感兴趣,请跳过本节)。
History
历史
HTTP was originally designed as a mechanism for retrieving HTML documents from remote servers. At first it effectively supported only the GET method, whereby the client would request a document by name and the server would return it to the client. The first public specification for HTTP, labelled as HTTP 0.9, appeared in 1991 - and if you're interested, you can read it here.
HTTP 最初被设计为一种从远程服务器检索 HTML 文档的机制。起初,它仅有效地支持 GET 方法,即客户端将按名称请求文档,服务器将其返回给客户端。HTTP 的第一个公开规范(标记为 HTTP 0.9)出现于 1991 年 - 如果您有兴趣,可以在此处阅读。
The HTTP 1.0 specification (formalised in 1996 with RFC 1945) expanded the capabilities of the protocol considerably, adding the HEAD and POST methods. It was not backwards compatible with HTTP 0.9, due to a change in the format of the response - a response code was added, as well as the ability to include metadata for the returned document in the form of MIME format headers - key/value data pairs. HTTP 1.0 also abstracted the protocol from HTML, allowing for the transfer of files and data in other formats.
HTTP 1.0 规范(在 1996 年与RFC 1945一起正式化)大大扩展了协议的功能,添加了 HEAD 和 POST 方法。它不向后兼容 HTTP 0.9,因为响应格式发生了变化 - 添加了响应代码,以及能够以 MIME 格式标头的形式包含返回文档的元数据 - 键/值数据对。HTTP 1.0 还将协议从 HTML 中抽象出来,允许以其他格式传输文件和数据。
HTTP 1.1, the form of the protocol that is almost exclusively in use today is built on top of HTTP 1.0 and was designed to be backwards compatible with HTTP 1.0 implementations. It was standardised in 1999 with RFC 2616. If you are a developer working with HTTP, get to know this document - it is your bible. Understanding it fully will give you a considerable advantage over your peers who do not.
HTTP 1.1 是当今几乎唯一使用的协议形式,它建立在 HTTP 1.0 之上,旨在向后兼容 HTTP 1.0 实现。它于 1999 年通过RFC 2616进行了标准化。如果您是使用 HTTP 的开发人员,请了解此文档 - 它是您的圣经。完全理解它会让你比不了解它的同龄人具有相当大的优势。
Get to the point already
已经进入正题
HTTP works on a request-response architecture - the client sends a request message to the server, the server returns a response message to the client.
HTTP 工作在请求-响应架构上——客户端向服务器发送请求消息,服务器向客户端返回响应消息。
A request message includes a METHOD, a URI and optionally, a number of HEADERS. The request METHOD is what this question relates to, so it is what I will cover in the most depth here - but first it is important to understand exactly what we mean when we talk about the request URI.
一个请求消息包括一个 METHOD、一个 URI 和一些可选的 HEADERS。请求方法是这个问题所涉及的,所以我将在这里最深入地介绍它——但首先,当我们谈论请求 URI 时,准确理解我们的意思很重要。
The URI is the location on the server of the resource we are requesting. In general, this consists of a pathcomponent, and optionally a query string. There are circumstances where other components may be present as well, but for the purposes of simplicity we shall ignore them for now.
URI 是我们请求的资源在服务器上的位置。通常,这由路径组件和可选的查询字符串组成。在某些情况下,其他组件也可能存在,但为了简单起见,我们现在将忽略它们。
Let's imagine you type http://server.domain.tld/path/to/document.ext?key=valueinto the address bar of your browser. The browser dismantles this string, and determines that it needs to connect to an HTTP server at server.domain.tld, and ask for the document at /path/to/document.ext?key=value.
假设您http://server.domain.tld/path/to/document.ext?key=value在浏览器的地址栏中键入内容。浏览器拆解这个字符串,并确定它需要连接到位于 的 HTTP 服务器server.domain.tld,并请求位于 的文档/path/to/document.ext?key=value。
The generated HTTP 1.1 request will look (at a minimum) like this:
生成的 HTTP 1.1 请求将(至少)如下所示:
GET /path/to/document.ext?key=value HTTP/1.1
Host: server.domain.tld
The first part of the request is the word GET- this is the request METHOD. The next part is the path to the file we are requesting - this is the request URI. At the end of this first line is an identifier indicating the protocol version in use. On the following line you can see a header in MIME format, called Host. HTTP 1.1 mandates that the Host:header be included with every request. This is the only header of which this is true.
请求的第一部分是单词GET——这是请求方法。下一部分是我们请求的文件的路径 - 这是请求 URI。第一行的末尾是一个标识符,指示正在使用的协议版本。在下一行中,您可以看到 MIME 格式的标题,名为Host. HTTP 1.1 要求Host:每个请求都包含标头。这是唯一正确的标题。
The request URI is broken into two parts - everything to the left of the question mark ?is the path, everything to the right of it is the query string.
请求URI被分成两个部分-一切问号左边?是路径,一切以它的右边是查询字符串。
Request Methods
请求方法
RFC 2616 (HTTP/1.1) defines 8 request methods.
RFC 2616 (HTTP/1.1) 定义了8 种请求方法。
OPTIONS
OPTIONS
The OPTIONS method is rarely used. It is intended as a mechanism for determining what kind of functionality the server supports before attempting to consume a service the server may provide.
OPTIONS 方法很少使用。它旨在作为一种机制,用于在尝试使用服务器可能提供的服务之前确定服务器支持哪种功能。
Off the top of my head, the only place in fairly common usage that I can think of where this is used is when opening documents in Microsoft office directly over HTTP from Internet Explorer - Office will send an OPTIONS request to the server to determine if it supports the PUT method for the specific URI, and if it does it will open the document in a way that allows the user to save their changes to the document directly back to the remote server. This functionality is tightly integrated within these specific Microsoft applications.
在我的头顶上,我能想到的唯一一个相当常见的用法是从 Internet Explorer 直接通过 HTTP 在 Microsoft Office 中打开文档时 - Office 将向服务器发送一个 OPTIONS 请求以确定它是否支持特定 URI 的 PUT 方法,如果支持,它将以允许用户将他们对文档的更改直接保存回远程服务器的方式打开文档。此功能紧密集成在这些特定的 Microsoft 应用程序中。
GET
GET
This is by far and away the most common method in every day usage. Every time you load a regular document in your web browser it will be a GET request.
这是迄今为止最常见的日常使用方法。每次在 Web 浏览器中加载常规文档时,它都会是一个 GET 请求。
The GET method requests that the server return a specific document. The only data that should be transmitted to the server is information that the server requires to determine which document should be returned. This can include information that the server can use to dynamically generate the document, which is sent in the form of headers and/or query string in the request URI. While we're on the subject - Cookies are sent in the request headers.
GET 方法请求服务器返回特定文档。应该传输到服务器的唯一数据是服务器确定应该返回哪个文档所需的信息。这可以包括服务器可以用来动态生成文档的信息,该文档以请求 URI 中的标头和/或查询字符串的形式发送。当我们讨论这个主题时 - Cookie 是在请求标头中发送的。
HEAD
HEAD
This method is identical to the GET method, with one difference - the server will not return the requested document, if will only return the headers that would be included in the response. This is useful for determining, for example, if a particular document exists without having to transfer and process the entire document.
此方法与 GET 方法相同,但有一个区别 - 服务器不会返回请求的文档,如果只会返回将包含在响应中的标头。例如,这对于确定特定文档是否存在非常有用,而无需传输和处理整个文档。
POST
POST
This is the second most commonly used method, and arguably the most complex. POST method requests are almost exclusively used to invoke some actions on the server that may change its state.
这是第二个最常用的方法,可以说是最复杂的。POST 方法请求几乎专门用于调用服务器上可能更改其状态的某些操作。
A POST request, unlike GET and HEAD, can (and usually does) include some data in the body of the request message. This data can be in any format, but most commonly it is a query string (in the same format as it would appear in the request URI) or a multipart message that can communicate key/value pairs along with file attachments.
与 GET 和 HEAD 不同,POST 请求可以(并且通常确实)在请求消息的正文中包含一些数据。此数据可以是任何格式,但最常见的是查询字符串(与出现在请求 URI 中的格式相同)或多部分消息,可以与文件附件一起传递键/值对。
Many HTML forms use the POST method. In order to upload files from a browser, you would need to use the POST method for your form.
许多 HTML 表单使用 POST 方法。为了从浏览器上传文件,您需要对表单使用 POST 方法。
The POST method is semantically incompatible with RESTful APIs because it is not idempotent. That is to say, a second identical POST request may result in a further change to the state of the server. This contradicts the "stateless" constraint of REST.
POST 方法在语义上与 RESTful API 不兼容,因为它不是幂等的。也就是说,第二个相同的POST请求可能会导致服务器状态的进一步变化。这与 REST 的“无状态”约束相矛盾。
PUT
PUT
This directly complements GET. Where a GET requests indicates that the server should return the document at the location specified by the request URI in the response body, the PUT method indicates that the server should store the data in the request body at the location specified by the request URI.
这直接补充了 GET。GET 请求指示服务器应在响应正文中的请求 URI 指定的位置返回文档,而 PUT 方法指示服务器应将数据存储在请求正文中由请求 URI 指定的位置。
DELETE
DELETE
This indicates that the server should destroy the document at the location indicated by the request URI. Very few internet facing HTTP server implementations will perform any action when they receive a DELETE request, for fairly obvious reasons.
这表明服务器应该在请求 URI 指示的位置销毁文档。由于相当明显的原因,很少有面向 Internet 的 HTTP 服务器实现会在收到 DELETE 请求时执行任何操作。
TRACE
TRACE
This provides an application-layer level mechanism to allow clients to inspect the request it has sent as it looks by the time it reaches the destination server. This is mostly useful for determining the effect that any proxy servers between the client and the destination server may be having on the request message.
这提供了一个应用层级机制,允许客户端在它到达目标服务器时检查它发送的请求。这对于确定客户端和目标服务器之间的任何代理服务器可能对请求消息产生的影响非常有用。
CONNECT
CONNECT
HTTP 1.1 reserves the name for a CONNECT method, but does not define its usage, or even its purpose. Some proxy server implementations have since used the CONNECT method to facilitate HTTP tunnelling.
HTTP 1.1 保留了 CONNECT 方法的名称,但没有定义它的用法,甚至它的目的。一些代理服务器实现已经使用 CONNECT 方法来促进 HTTP 隧道。
回答by kjurkovic
I've never tried using PUT (GET POST and FILES were sufficient for my needs) but this example is from the php docs so it might help you (http://php.net/manual/en/features.file-upload.put-method.php):
我从来没有尝试过使用 PUT(GET POST 和 FILES 足以满足我的需要)但这个例子来自 php 文档,所以它可能对你有帮助(http://php.net/manual/en/features.file-upload.php)。 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);
?>
回答by Daniel Sikes
Here is the solution that I found to be the most useful.
这是我发现最有用的解决方案。
$put = array();
parse_str(file_get_contents('php://input'), $put);
$put = array();
parse_str(file_get_contents('php://input'), $put);
$putwill be an array, just like you are used to seeing in $_POST, except now you can follow true REST HTTP protocol.
$put将是一个数组,就像您习惯于在 中看到的一样$_POST,但现在您可以遵循真正的 REST HTTP 协议。
回答by user4157069
Use POST and include an X- header to indicate the actual method (PUT in this case). Usually this is how one works around a firewall which does not allow methods other than GET and POST. Simply declare PHP buggy (since it refuses to handle multipart PUT payloads, it IS buggy), and treat it as you would an outdated/draconian firewall.
使用 POST 并包含一个 X- 标头来指示实际方法(在本例中为 PUT)。通常这是绕过防火墙的工作方式,防火墙不允许使用 GET 和 POST 以外的方法。简单地声明 PHP 有问题(因为它拒绝处理多部分 PUT 有效负载,它是有问题的),并像对待过时/严酷的防火墙一样对待它。
The opinions as to what PUT means in relation to GET are just that, opinions. The HTTP makes no such requirement. It simply states 'equivalent' .. it is up to the designer to determine what 'equivalent' means. If your design can accept a multi-file upload PUT and produce an 'equivalent' representation for a subsequent GET for the same resource, that's just fine and dandy, both technically and philosophically, with the HTTP specifications.
关于 PUT 相对于 GET 意味着什么的意见只是意见。HTTP 没有这样的要求。它只是简单地说明“等效”……由设计师来确定“等效”的含义。如果您的设计可以接受多文件上传 PUT 并为相同资源的后续 GET 生成“等效”表示,那么使用 HTTP 规范在技术和哲学上都很好。
回答by Naftali aka Neal
Just follow what it says in the DOC:
只需按照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);
?>
This shouldread the whole file that is on the PUT stream and save it locally, then you could do what you want with it.
这应该读取 PUT 流上的整个文件并将其保存在本地,然后您可以使用它做您想做的事情。

