force download using ZF2 - zend-framework2

I am trying to do force download using ZF2. Here is the snippet to my code
use Zend\Http\Request;
.....
public function downloadAction() {
$response = new Request();
$response->setHeaders(Request::fromString("Content-Type: application/octet-stream\r\nContent-Length: 9\r\nContent-Disposition: attachment; filename=\"ultimate_remedy_readme.txt\""));
}
now i am getting this error
/var/www/whowantsmymoney/vendor/zendframework/zendframework/library/Zend/Http/Request.php:88
Message:
A valid request line was not found in the provided string
Stack trace:
#0 /var/www/whowantsmymoney/module/Admin/src/Admin/Controller/LanguageController.php(93): Zend\Http\Request::fromString('Content-Type: a...')

This code should help you for a simple file download.
public function downloadAction() {
$fileName = 'somefile';
if(!is_file($fileName)) {
//do something
}
$fileContents = file_get_contents($fileName);
$response = $this->getResponse();
$response->setContent($fileContents);
$headers = $response->getHeaders();
$headers->clearHeaders()
->addHeaderLine('Content-Type', 'whatever your content type is')
->addHeaderLine('Content-Disposition', 'attachment; filename="' . $fileName . '"')
->addHeaderLine('Content-Length', strlen($fileContents));
return $this->response;
}
I imagine this code leaves a lot to be desired, but should work in simple cases, as was mine. I'm not sure how you might handle reading the file in chunks. Maybe somebody else could shed some light?
Edit - Sending streams
I've added this here for informational purposes. It is probably the better way to force downloads as it will use much less memory.
public function downloadAction() {
$fileName = 'somefile';
$response = new \Zend\Http\Response\Stream();
$response->setStream(fopen($fileName, 'r'));
$response->setStatusCode(200);
$headers = new \Zend\Http\Headers();
$headers->addHeaderLine('Content-Type', 'whatever your content type is')
->addHeaderLine('Content-Disposition', 'attachment; filename="' . $fileName . '"')
->addHeaderLine('Content-Length', filesize($fileName));
$response->setHeaders($headers);
return $response;

Thanks to #Aydin Hassan for response, but several important headers are missing in his answer. Be careful of that.
Full headers stack:
public function downloadAction() {
$file = 'path/to/file';
$response = new \Zend\Http\Response\Stream();
$response->setStream(fopen($file, 'r'));
$response->setStatusCode(200);
$response->setStreamName(basename($file));
$headers = new \Zend\Http\Headers();
$headers->addHeaders(array(
'Content-Disposition' => 'attachment; filename="' . basename($file) .'"',
'Content-Type' => 'application/octet-stream',
'Content-Length' => filesize($file),
'Expires' => '#0', // #0, because zf2 parses date as string to \DateTime() object
'Cache-Control' => 'must-revalidate',
'Pragma' => 'public'
));
$response->setHeaders($headers);
return $response;
}

Related

CUrl, Pecl HTTPRequest, file_get_contents() NOTHING WORKS

Maybe I just have another bad day, but I've tried all of the possible ways to send GET Request with Cookies and every single one gets stacked. My page is just reloading forever.
my $url = "http://localhost/content-search/demo/"
first attempt to use class HTTPRequest from Pecl... I deleted it,
but here are my attempts to send it with curl:
public static function sendRequest($url, array $cookies) {
$c = curl_init($url);
curl_setopt($c, CURLOPT_VERBOSE, 1);
curl_setopt($c, CURLOPT_COOKIE, $cookies);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
$page = curl_exec($c);
curl_close($c);
return $page;
}
And with file_get_contents:
public static function sendRequest($url, $cookies)
{
if(is_array($cookies)) {
$cookies = self::formatCookies($cookies);
}
$options = array(
'http' => array(
'method' => 'GET',
'header' => 'Connection: close\r\nCookie: ' . $cookies
)
);
return file_get_contents($url, NULL, stream_context_create($options));
}
again, they all three get stacked and the request is being sent like forever.
Ok solved!
problem was in cookies.
the session_write_close(); solves the problem :)

Youtube api Where will write this code

Too bad my php knowledge.I'm using YouTube-api.Where will write this code: Retrieve Youtube Channel info for "Vanity" channel
If you are talking about this line :
GET https://www.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics&id=UC6ltI41W4P14NShIBHU8z1Q&key={YOUR_API_KEY}
You are simply making a get request, you can use file_get_contents to get the response for you :
$response = file_get_contents("https://www.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics&id=UC6ltI41W4P14NShIBHU8z1Q&key={YOUR_API_KEY}");
Two notes :
You have to replace {YOUR_API_KEY} with the developer key. You can easily request one from youtube: http://code.google.com/apis/youtube/dashboard/
This is just an example in one line of code, I suggest you use a better approach for making this request like the following :
// Encode the parameters of the link
function encode_param($params) {
foreach ($params as $field => $value){
$encoded_params[] = $field . '=' . urlencode($value);
}
return $encoded_params;
}
// Get the response
function get_response($url) {
$response = file_get_contents($url);
// If error, send message back to the client
if ($response === false) {
exit("Couldn't get response from the api");
}
return $response;
}
$params = array(
"part" => "snippet,contentDetails,statistics",
"id" => "UC6ltI41W4P14NShIBHU8",
"key" => "-----------", // Your API key
);
$encoded_params = encode_param($params);
$request_url = "https://www.googleapis.com/youtube/v3/channels?".implode('&', $encoded_params);
$response = get_response($request_url);
//............

CakePHP requestAction catch error when url not exists

I try to catch error when the requested url does not exist in my application when I use requestAction($url);
with try-catch block, requested wrongs url continue to be loaded;
Please tell me if there is an other solution to test if the url exist, or if the file exist in cakephp dir (my url can be composed of a plugin name).
my actual code:
$url = $appliUrl . $cron['Cron']['plugin'] . '/'. $cron['Cron']['controller'] . '/' . $cron['Cron']['action'];
if (!empty($cron['Cron']['params'])) {
$params = explode(',', $cron['Cron']['params']);
foreach ($params as $param)
$url .= '/' . $param;
}
try{
$output = $this->requestAction($url);
}catch (Exception $e){
$output = "error in the url : ".$url;
}
You can use a variety of methods to accomplish this. In this example, use cURL.
function url_exists($url) {
if (!$fp = curl_init($url)) return false;
return true;
}
Then you can wrap your $output in an if block checking for the url.
if (url_exists($url)) {
$output = $this->requestAction($url);
}
EDIT
Make sure you put the function in a lib or your bootstrap for access. Don't just stick it in your controller like that :)
ANOTHER EDIT
<?php
$ch = curl_init('http://yoururl/');
curl_setopt($ch, CURLOPT_NOBODY, 1);
$c = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE);

How do I get a LinkedIn request token?

Hey I'm trying to use LinkedIn's OAuth in PHP. I'm stuck at the first step of getting a request token. All I know is you post some values to their server and get your token back. So i post the documented args to 'https://api.linkedin.com/uas/oauth/requestToken' and I get slapped with a 400 error.
here's the request:
$postArr = array();
//$postArr["oauth_callback"] = ""; idk they said this was optional...
$postArr["oauth_consumer_key"] = "ForBritishEyesOnly"; //is this the application secret key or the api key?
$postArr["oauth_nonce"] = "UltraRandomNonceFTW";
$postArr["oauth_timestamp"] = time();
$postArr["oauth_signature_method"] = "HMAC-SHA1"; //lolwut
$postArr["oauth_version"] = "1.0";
$params = array('http'=>array('method'=>'post','content'=>http_build_query($postArr)));
$context = stream_context_create($params);
$stream = file_get_contents('https://api.linkedin.com/uas/oauth/requestToken', false, $context);
I don't think my POST args are correct but ANY help is very appreciated -- I just don't want resort to use someone else's library to solve this.
-------EDIT: ATTEMPT 2 per James' input ---------
ok so here im making a call to the test link you sent me. i'm actually able to get a response back, but it doesnt like my signature (big surprise, i know). So just how bad did I screw up the encryption?
//setup GET args
$url = "http://term.ie/oauth/example/request_token.php?";
$url .= "oauth_version=1.0&";
$url .= "oauth_nonce=" . rand(0, 100000) . "&";
$url .= "oauth_timestamp=" . time() . "&";
$url .= "oauth_consumer_key=key&";
$url .= "oauth_signature_method=HMAC-SHA1&";
//encrypt the request according to 'secret'
$sig = urlencode(base64_encode(hash_hmac("sha1", $url, "secret")));
//append the url encoded signature as the final GET arg
$url .= "oauth_signature=" . $sig;
//do it to it
echo file_get_contents($url);
EDIT by James
Try:
//setup GET args
$url = "http://term.ie/oauth/example/request_token.php?";
$url .= "oauth_consumer_key=key&";
$url .= "oauth_nonce=" . rand(0, 100000) . "&";
$url .= "oauth_signature_method=HMAC-SHA1&";
$url .= "oauth_timestamp=" . time() . "&";
$url .= "oauth_version=1.0&";
I'm on cloud nine. Decided to revisit this problem and got it to work. Here is some very bare bones PHP to build a token request for LinkedIn (it outputs an anchor tag)
<?php
$endpoint = "https://api.linkedin.com/uas/oauth/requestToken";
$key = "YourAPIKey";
$secret = "YourAPISecret";
$params = array(
"oauth_version" => "1.0",
"oauth_nonce" => time(),
"oauth_timestamp" => time(),
"oauth_consumer_key" => $key,
"oauth_signature_method" => "HMAC-SHA1"
);
function SortedArgumentString($inKV)
{
uksort($inKV, 'strcmp');
foreach ($inKV as $k => $v)
$argument[] = $k."=".$v;
return implode('&', $argument);
}
$baseString = "GET&" . urlencode($endpoint) . "&" . urlencode(SortedArgumentString($params));
$params['oauth_signature'] = urlencode(base64_encode(hash_hmac('sha1', $baseString, $secret."&", TRUE)));
echo "<a href=\"" . $endpoint . "?" . SortedArgumentString($params) . "\">Get Token<a/><br/>";
?>
oauth_consumer_key is a value that LinkedIn should have assigned to your app. Did you register with them?
oauth_nonce should be different for each request to prevent replay-attacks.
If you're using HMAC-SHA1 you'll need to add the oauth_signature field yourself. Creating the signature manually is a total PITA.
There's also a lot of Base64 encoding to do (with the added bonus of some special OAuth quirks). I suggest you read the spec.
There is a test server and client at this link. It's quite useful when you're struggling to get the protocol right.

How do you POST to a page using the PHP header() function?

I found the following code on here that I think does what I want, but it doesn't work:
$host = "www.example.com";
$path = "/path/to/script.php";
$data = "data1=value1&data2=value2";
$data = urlencode($data);
header("POST $path HTTP/1.1\r\n");
header("Host: $host\r\n");
header("Content-type: application/x-www-form-urlencoded\r\n");
header("Content-length: " . strlen($data) . "\r\n");
header("Connection: close\r\n\r\n");
header($data);
I'm looking to post form data without sending users to a middle page and then using JavaScript to redirect them. I also don't want to use GET so it isn't as easy to use the back button.
Is there something wrong with this code? Or is there a better method?
Edit I was thinking of what the header function would do. I was thinking I could get the browser to post back to the server with the data, but this isn't what it's meant to do. Instead, I found a way in my code to avoid the need for a post at all (not breaking and just continuing onto the next case within the switch).
The header function is used to send HTTP response headers back to the user (i.e. you cannot use it to create request headers.
May I ask why are you doing this? Why simulate a POST request when you can just right there and then act on the data someway? I'm assuming of course script.php resides on your server.
To create a POST request, open a up a TCP connection to the host using fsockopen(), then use fwrite() on the handler returned from fsockopen() with the same values you used in the header functions in the OP. Alternatively, you can use cURL.
The answer to this is very needed today because not everyone wants to use cURL to consume web services. Also PHP does allow for this using the following code
function get_info()
{
$post_data = array(
'test' => 'foobar',
'okay' => 'yes',
'number' => 2
);
// Send a request to example.com
$result = $this->post_request('http://www.example.com/', $post_data);
if ($result['status'] == 'ok'){
// Print headers
echo $result['header'];
echo '<hr />';
// print the result of the whole request:
echo $result['content'];
}
else {
echo 'A error occured: ' . $result['error'];
}
}
function post_request($url, $data, $referer='') {
// Convert the data array into URL Parameters like a=b&foo=bar etc.
$data = http_build_query($data);
// parse the given URL
$url = parse_url($url);
if ($url['scheme'] != 'http') {
die('Error: Only HTTP request are supported !');
}
// extract host and path:
$host = $url['host'];
$path = $url['path'];
// open a socket connection on port 80 - timeout: 30 sec
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if ($fp){
// send the request headers:
fputs($fp, "POST $path HTTP/1.1\r\n");
fputs($fp, "Host: $host\r\n");
if ($referer != '')
fputs($fp, "Referer: $referer\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: ". strlen($data) ."\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $data);
$result = '';
while(!feof($fp)) {
// receive the results of the request
$result .= fgets($fp, 128);
}
}
else {
return array(
'status' => 'err',
'error' => "$errstr ($errno)"
);
}
// close the socket connection:
fclose($fp);
// split the result header from the content
$result = explode("\r\n\r\n", $result, 2);
$header = isset($result[0]) ? $result[0] : '';
$content = isset($result[1]) ? $result[1] : '';
// return as structured array:
return array(
'status' => 'ok',
'header' => $header,
'content' => $content);
}
In addition to what Salaryman said, take a look at the classes in PEAR, there are HTTP request classes there that you can use even if you do not have the cURL extension installed in your PHP distribution.
There is a good class that does what you want. It can be downloaded at: http://sourceforge.net/projects/snoopy/
private function sendHttpRequest($host, $path, $query, $port=80){
header("POST $path HTTP/1.1\r\n" );
header("Host: $host\r\n" );
header("Content-type: application/x-www-form-urlencoded\r\n" );
header("Content-length: " . strlen($query) . "\r\n" );
header("Connection: close\r\n\r\n" );
header($query);
}
This will get you right away

Resources