Upload File via RESTful API? - php

I was trying to make an RESTful API call to upload videos through POST method. What I am lacking is that I don't know the best practices for writing this kind of API as well I don't find any resource on the internet to follow. Right now I am doing this:
I am working in PHP and zend framework ( Zend_Rest_Route ).
First approach:
using file_get_contents on client side and POST it to API using curl, and on server side using file_put_contents to write that data and sending an appropriate response.
Second:
using Zend_File_Treansfer to receive file at server side, and putting address of my upload api end point in zend_form with setting method as post. In this case file is uploaded to server, but after submitting the form, the url in address bar points to the api server and never comes back to the form.
Am I doing it right?, if not do let me know what are the best practices and how to accomplish this.
Thank you for your time.

Something like this worked for me:
public function postAttachment($fileName, $fileMimetype, $fileContents, $postURL, $username, $password)
{
$auth = base64_encode($username . ':' . base64_decode($password));
$header = array("Authorization: Basic ".$auth);
array_push($header, "Accept: */*");
$boundary = "----------------------------".substr(md5(rand(0,32000)), 0, 12);
$data = "";
$data .= "--".$boundary."\r\n";
//Collect Filedata
$data .= "Content-Disposition: form-data; name=\"file\"; filename=\"".$fileName."\"\r\n";
$data .= "Content-Type: ".$fileMimetype."\r\n";
$data .= "\r\n";
$data .= $fileContents."\r\n";
$data .= "--".$boundary."--";
// add more parameters or files here
array_push($header, 'Content-Type: multipart/form-data; boundary='.$boundary);
$params = array('http' => array(
'method' => 'POST',
'protocol_version' => 1.1,
'user_agent' => 'File Upload Agent',
'header' => $header,
'content' => $data
));
$ctx = stream_context_create($params);
$fp = fopen($postURL, 'rb', false, $ctx);
if (!$fp) {
throw new Exception("Problem with ".$postURL." ".$php_errormsg);
}
$responseBody = #stream_get_contents($fp);
if ($responseBody === false) {
throw new Exception("Problem reading data from ".$postURL.", ".$php_errormsg);
}
}
If you want to post several files, or add other multi-part parameters, it's easy to add these in other boundaries too.
I found some of this code on another post, and you can probably find similar code in the PHP wiki (http://www.php.net/manual/en/function.stream-context-create.php#90411). BUT ... That code was not properly handling the carriage return + line feeds and my server was summarily rejecting that post. In addition, that the older code was also using HTTP version 1.0 -- (which does not re-use sockets). When using HTTP 1.1 sockets are re-used when posting lots of files. (This works with HTTPS too.) I added my own user agent - If your are tricking some server into thinking this is a browser post, you might want to change the user agent to spoof a browser.

have you tried adding a redirect to end of your controller action that handles the upload? (if not you really should as its good practice to redirect after post) (make sure you redirect AFTER your logic has executed). In essence the 'page' that receives the post data should just work on the data, and any information you want to return to the user about that post action should be given to them on the page you redirect them to.
[form]--POST-->['post' controller action]--redirect (302)-->[page with success/failure info]

Related

Request another PHP file with any Method (NOT cURL!)

I am building an API in PHP and want the user to include some plug-ins written in any language of choice in a designated directory. The way this should work is that the API sends a request to that user's plug-in file and returns the result.
This should be accomplished without cURL, because cURL is unavailable in this particular environment, so answers using cURL won't be helpful.
The issue that I am having is that my function literally reads the contents of the file (without executing it) when the plug-in is also written in PHP.
This is my code:
function sendRequest($url, $method, $body){
$http = array(
'method' => $method,
'header' => 'Authorization: sometoken' . "\r\n".
'Content-Type: application/json' . "\r\n",
'content' => $body
);
$context = stream_context_create(array('http' => $http));
if($file = file_get_contents($url, false, $context)){
return $file;
}
else{
return 'Error';
}
}
This is an example of a simple plug-in file written in PHP:
<?php
$input = file_get_contents('php://input');
$input = json_decode($input, true);
echo($input['a'] + $input['b']);
?>
When it is requested from the API it should return the value of a + b. The above code could also be implemented in any other language, it should work either way. These files are stored locally, not on a remote server (where it would work flawlessly).
Is there any way force the file to be executed without cURL? I imagine include and require are also not an option, since the plug-in file should be in a language of choice, not necessarily PHP.
Thanks in advance!
You'll want to look at the PHP command exec
This would allow you do to something like:
exec('php plugin.php param1', $output);
Then read the results back via the $output variable.
Similar things could be done with other applications/scripts (provided they process with absolutely no interaction).

How to send URL variables programmatically with response from my PHP endpoint

I have a basic API endpoint set up on my site, which a 3rd party site will use to verify certain info that is entered into a form by the user.
Here's the flow:
1. User is on 3rd party site.
2. User enters info into a form
3. Info is sent to my site's endpoint.
4. My site checks the information and returns a JSON object.
As you can see from #4, my API is currently set up to return a JSON object. After the info is checked, something like this happens:
header('content-type: application/json; charset=utf-8');
echo json_encode($response);
exit;
However, the 3rd party site is only set up to receive URL variables. Is there a way to pass back url variables programmatically? I realize I could theoretically send a new request, but it's not clear to me where that request should go (the internal workings of the 3rd party site aren't well documented), so I'd much prefer to send it as a response.
I hope this makes sense. Please comment if it doesn't. Thanks in advance!
You don't get to send GET/POST parameters in the response, but in the response body you can send whatever you want in whatever format you want - and they can use curl or file_get_content and parse it on their side (3rd party's website).
For example (on the 3rd party's website):
//setting a call to your server
$opts = array('http' =>
array(
'method' => 'POST',
'header' => "Content-Type: text/xml\r\n".
"Authorization: Basic ".base64_encode("$https_user:$https_password")."\r\n",
'content' => $body,
'timeout' => 60
)
);
$context = stream_context_create($opts);
$url = 'https://'.$https_server;
// Here they call your server
$result = file_get_contents($url, false, $context, -1, 40000);
// Here you'll parse the $result

What are ways to send data using PHP?

I need to send HTTP POST data to a webpage. My host is missing some extensions (I'm not sure which ones). I tried cURL and fopen, neither of them work.
What are other ways to send data?
Edit: By the way, I can send $_GET data as well. So as long as I can open a url (eg. file_get_contents), it's works.
Checkout the very powerful PHP stream functions.
However, if the file/stream and cURL functions are disabled - then make them on the frontend using AJAX requests. jQuery is good at this as long as the data isn't sensitive.
I built an entire blog system using just jQuery JSONP requests on the frontend since I wanted to move the load to the user instead of my server.
This may work. The context is not really needed, but allows you to set custom timeout and user-agent.
/* Set up array with options for the context used by file_get_contents(). */
$opts = array(
'http'=>array(
'method' => 'GET',
'timeout' => 4,
'header' => "Accept-language: en\r\n" .
"User-Agent: Some UA\r\n"
)
);
/* Create context. */
$context = stream_context_create($opts);
/* Make the request */
$response = #file_get_contents('http://example.com/?foo=bar', null, $context);
if($response === false) {
/* Could not make request. */
}
You can use http_build_query() to build your query string from an array.

PHP POST with custom headers giving HTTP 400 Bad Request Response (Bug?)

I'm having some trouble POSTing data from a client machine to our internal site using PHP. The server accepts data via HTTPS with basic authentication. Here is the code I'm using to POST with:
$parameters = array('http' => array(
'method' => 'POST',
'content' => $data
)
);
if ($optionalHeaders !== NULL) {
$parameters['http']['header'] = $optionalHeaders;
}
$ctx = stream_context_create($parameters);
$fp = fopen($url, 'rb', false, $ctx);
With the following header:
$postHeader = "POST /index.php HTTP/1.1\r\n".
"Host:my.host\r\n".
"Content-Type: application/x-www-form-urlencoded\r\n".
"User-Agent: PHP-Code\r\n".
"Content-Length: " . strlen($postData) . "\r\n".
"Authorization: Basic ".base64_encode($user.':'.$password)."\r\n".
"Connection: close\r\n";
Now, I can get this to work, and it posts just fine on one of my clients with PHP version 5.2.5, but the on another client I get this error message:
fopen(magical_url): failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request
And Apache error log gives:
request failed: error reading the headers
The only difference I can see is that the latter client has PHP version 5.1.6.
Does anyone know if this is a bug? Or am I doing something wrong somewhere...
I've looked through the PHP site and found this bug listed for version 5.2.6 of PHP https://bugs.php.net/bug.php?id=45540 but this post-dates the version it works on!
Thanks,
Jak
You should not provide all those headers. That's bound to fail.
Headers like POST, Host, Content-Length and Connection are automatically provided. Remove all your optional headers (should work then) and add them step by step.

Send a file through a POST request with http_socket in CakePHP?

I need to make a HTTP POST call to a Hudson CI server from CakePHP. The call is parametrized and contains a few key/value pairs. Some of those are in fact files which need to be uploaded.
Since I'm using CakePHP, I'd rather use the HttpSocket class which comes with the framework rather then try and write my own cURL based implementation.
So far, the call looks like this:
$result = $http->post($apiCall, $params, $auth);
$apiCall being the URI of the Hudson REST API.
$params being an array of parameters which go with the POST call.
$auth containing a user/pass for Basic Auth which configured with this is instance of Hudson.
I'm a bit puzzled though: what would I need to do to also included files in my $params array?
We are running Hudson v1.371 which should - as far as I've gathered - support file uploads coming from a parametrized build call.
Thanks!
I don't know if the HttpSocket class handles multipart Http requests. But you can create it manually. I've done this in my CakePHP GData Plugin that includes functionality for uploading videos to YouTube. The save method in the YouTubeVideo model creates a multipart Http request with the first part containing an XML document with meta data about the video and the second part is the binary contents of the video file being uploaded:
// The boundary string is used to identify the different parts of a
// multipart http request
$boundaryString = 'Next_Part_' . String::uuid();
// Build the multipart body of the http request
$body = "--$boundaryString\r\n";
$body.= "Content-Type: application/atom+xml; charset=UTF-8\r\n";
$body.= "\r\n";
$body.= $doc->saveXML()."\r\n";
$body.= "--$boundaryString\r\n";
$body.= "Content-Type: {$data[$this->alias]['file']['type']}\r\n";
$body.= "Content-Transfer-Encoding: binary\r\n";
$body.= "\r\n";
$body.= file_get_contents($data[$this->alias]['file']['tmp_name'])."\r\n";
$body.= "--$boundaryString--\r\n";
$this->request = array(
'method' => 'POST',
'uri' => array(
'host' => 'uploads.gdata.youtube.com',
'path' => '/feeds/api/users/default/uploads',
),
'header' => array(
'Content-Type' => 'multipart/related; boundary="' . $boundaryString . '"',
'Slug' => $data[$this->alias]['file']['name']
),
'auth' => array(
'method' => 'OAuth',
),
'body' => $body,
);
This might get you there.
I tried using typical syntax $result = $HttpSocket->post($url,$data,$request); and it works for me. Btw, there are some instances that httpsocket sends blank data (which I haven't resolved yet) so I used cURL for instead of this.
Also, make sure you're using multipart form for your form. :)

Categories