How to Receive a File via Http Put with PHP

How to receive a file via HTTP PUT with PHP

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-data request body - the MIME type used when uploading files via HTTP POST through an HTML form.

PUT requests should exactly compliment the response to a GET request - they send you the file contents in the message body, and nothing else.

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).

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.


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).

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.

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.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.

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.

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.

The URI is the location on the server of the resource we are requesting. In general, this consists of a path component, 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.

Let's imagine you type http://server.domain.tld/path/to/document.ext?key=value into 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.

The generated HTTP 1.1 request will look (at a minimum) like this:

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.

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.

Request Methods

RFC 2616 (HTTP/1.1) defines 8 request methods.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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

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.

Sending a file via HTTP PUT in PHP

Aha! After a little "rubber ducking" with the grumpy dwarf stuffed doll on my desk here, I figured out the solution:


$data = file_get_contents($tmpFile);
$params = array(
'http' => array(
'method' => 'PUT',
'header' => "Authorization: Basic " . base64_encode($this->ci->config->item('ws_login') . ':' . $this->ci->config->item('ws_passwd')) . "\r\nContent-type: text/xml\r\n",
'content' => file_get_contents($tmpFile)
)
);
$ctx = stream_context_create($params);
$response = @file_get_contents($url, false, $ctx);

return ($response == '');

How to upload files using PUT instead of POST with PHP

PHP provides support for the HTTP PUT method used by some clients to store files on a server. PUT requests are much simpler than a file upload using POST requests and they look something like this:

PUT /path/filename.html HTTP/1.1

The following code is in the official PHP documentation for uploading files via PUT:

<?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);
?>

How to receive a file(ie. a text file) from a http post in php?

Yes, $_POST will be empty, you should check $_FILES variable for uploaded files:
Here is quick snippet:

<?php
$uploaddir = "uploads/";
$uploadfile = $uploaddir . basename( $_FILES['file']['name']);

if(move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile))
{
echo "The file has been uploaded successfully";
}
else
{
echo "There was an error uploading the file";
}
?>

The contents of $_FILES from above script is as follows.

$_FILES['file']['name'] The original name of the file on the client machine.

$_FILES['file']['type'] The mime type of the file, if the browser provided this information. An example would be “image/gif”.

$_FILES['file']['size'] The size, in bytes, of the uploaded file.

$_FILES['file']['tmp_name'] The temporary filename of the file in which the uploaded file was stored on the server.

$_FILES['file']['error'] Since PHP 4.2.0, PHP returns an appropriate following error code along with the file array

  • UPLOAD_ERR_OK – Value: 0; There is no error, the file uploaded with
    success.
  • UPLOAD_ERR_INI_SIZE Value: 1; The uploaded file exceeds the
    upload_max_filesize directive in php.ini.
  • UPLOAD_ERR_FORM_SIZE Value: 2; The uploaded file exceeds the
    MAX_FILE_SIZE directive that was specified in the HTML form.
  • UPLOAD_ERR_PARTIAL Value: 3; The uploaded file was only partially
    uploaded.
  • UPLOAD_ERR_NO_FILE Value: 4; No file was uploaded.

Uploaded Files will by default be stored in the server’s default temporary directory. Variable $_FILES['file']['tmp_name'] will hold the info about where it is stored. The move_uploaded_file function needs to be used to store the uploaded file to the correct location

How to get PUT content from HTTP request in PHP

While there is no official $_PUT variable in PHP, you can create one yourself like this:

$method = $_SERVER['REQUEST_METHOD'];
if ('PUT' === $method) {
parse_str(file_get_contents('php://input'), $_PUT);
var_dump($_PUT); //$_PUT contains put fields
}

Source: https://stackoverflow.com/a/41959141/4379151

Curl PUT request with file upload to PHP

I have some ideas for debugging.

Do a var_dump(file_get_contents('php://input')); instead of an echo. According to the reference:

This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.

If you get a bool(false) as output, there's something wrong which makes you cannot read php://input - most likely a PHP issue. If you get string(0) "", there's just nothing in php://input (anymore?), which makes it more likely that this is an nginx issue.

Also, according to the php:// reference, you cannot use php://input with enctype="multipart/form-data". Are you sure you don't use that one? You could also try an HTML file if that's more familiar.

You can also check the error logs, /var/log/nginx/error.log by default. Also, check the HTTP response code. Is it 200? If not, is it a helpful code?

php file upload with file_get_contents to a server

Why don't you use cURL instead of streams? It's so easy:

$ch = curl_init('http://www.url.com');

curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'file_input' => '@/path/to/file',
));

curl_exec($ch);

PHP file upload PUT request alters file, while POST does not

^M are windows line ending characters CR LF (0x0D 0x0A or \r\n).

When opening the stream $putdata = fopen("php://input","r");

The headers are sent first and then the file (header and body is sperated by \r\n\r\n).

I don't know which part of your setup is causing the error but you should check for that characters and if they exist, you know the file starts after \r\n\r\n.

You have something wrong in your request. The way you try to PUT the request is wrong.

How to receive a file via HTTP PUT with PHP

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.

This means, you may not have multipart request in a single PUT request.
There must be a single content-type. (Multipart is indicated by: ------WebKitFormBoundaryhPjNpS0gBhbCC8aR)

In a POST request, there is multipart allowed (think of: enctype='multipart/form-data').

A put request with a file must not contain anything else. Means, if you want to put a file + variables, the variables need to be appended to the URI, but not like in POST, in the body.

Update with possible workaround (I know it's a dirty solution, please don't blame me....):

while ($data = fread($putdata, 1024)) {
if (strpos($data, '\r\n\r\n') !== false) {
$data = explode("\r\n\r\n", $data); // seperate multipart header from body
$data = $data[1]; // assign body to $data
} elseif(strpos($data, '\r\n-' !== false) {
$data = explode("\r\n-", $data); // seperate footer from body
$data = $data[0]; // assign body to $data
}

fwrite($fp, $data);
}


Related Topics



Leave a reply



Submit