When attempting to view a *.wav URL on our webserver, the data doesn't transfer properly.
We are using apache2 with php5.10. Among the apache rewrite rules is:
RewriteRule ^(.+)\.(wav)$ /wav.php?wav=$1.$2 [L,NC]
And the relevant code from wav.php is:
<?php
$image = getPassed("wav");
header( 'Content-Type: audio/wav');
set_include_path("/");
readfile($image, true);
exit;
?>
This is supposed to return any .wav file on the server when server.company.com/filepath is accessed via a web browser.
When attempting to listen to any *.wav file in firefox or chrome in ubuntu (haven't tested other OSes yet), the plugin errors: "Location not found." However, right-clicking and choosing "save link as" allows the user to download the .wav file.
Any thoughts?
Edit:
getPassed is a function to return variables from $_GET or $_POST
I think that you need a couple more headers for this functionality to work, not 100% sure because I have never tried to stream a file.
header( 'Content-Type: audio/wav');
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($image) ."; ");
header('filename="'.$image . '"; ');
It is worth a shot to try it with those either way. You may also want to try a content-type of Content-Type: application/octet-stream;, you may also want to try "chunking" the file using readfile_chunked() (a user contributed function on php.net) and see if that possibly helps as well.
Hope it helps.
Related
I'd like to embed a PDF file in a webpage. I need to dynamically produce the PDF so I can authenticate the user first, so I'm using XSendFile on Apache. The PHP file I have works fine when I visit a browser with the PDF file being immediately offered for download. Here is the code I'm using (courtesy of http://www.brighterlamp.com/2010/10/send-files-faster-better-with-php-mod_xsendfile/)
// Get a list of loaded Apache modules
$modules = apache_get_modules();
if (in_array('mod_xsendfile', $modules)) {
// Use XSendFile if possible
header ('X-Sendfile: ' . $pathToFile);
header ('Content-Type: ' . $documentMIME);
header ('Content-Disposition: attachment; filename="' . $actualFilename . '"');
exit;
} else {
// Otherwise, use the traditional PHP way..
header ('Content-Type: ' . $documentMIME);
header ('Content-Disposition: attachment; filename="' . $actualFilename . '"');
#ob_end_clean();
#ob_end_flush();
readfile($pathToFile);
exit;
}
So far so good. Now I want to embed this PDF in a webpage using an object tag e.g.:
<object data="dynamicpdf.php" type="application/pdf">
<p>PDF embed failed</a></p>
</object>
But this fails. If I switch the data attribute to a static PDF file then it works fine.
Any ideas what is going wrong?
Is iframing the PDF an option for you?
Like <iframe src="dynamicpdf.php">
The Content-Disposition header forces the download. Remove it.
General Advise:
I would not use functions like apache_get_modules that asume a specific webserver environment.
What if you switch away from mod_php or apache in future? Your code will break.
Instead I would do the delivery in a streamed php response that is more memory efficient than output buffering the whole PDF into RAM and then send it.
By streaming the PDF out with PHP you would also have only one implementation and it would be same speed as x-sendfile is:
Streaming a large file using PHP
When generating a PDF in the browser programmatically (via PHP) the rendered PDF displays fine in both Firefox and Safari, but Chrome returns an ERR_INVALID_RESPONSE. It is a valid PDF - can be opened locally with Adobe Reader/Preview once saved from the working browsers, and will even open in Chrome once the PDF is saved from a different browser.
The PDF file is being read through file_get_contents(), is given a current timestamp and then passed to the browser. A workaround would involve saving the file to a temporary spot and redirecting the user (for Chrome, at least) but this is not ideal.
I've researched it and only been able to find bug reports dating from 2008.
I have an inkling it's a header error. After the PDF is generated, the following headers are sent to the browser (again working fine in FF, Safari and IE):
header('Content-type:application/pdf');
header("HTTP/1.1 200 OK");
I've also tried adding the following headers after searching on Stack Overflow, but to no avail:
header("Content-Transfer-Encoding: binary");
header('Accept-Ranges: bytes');
Are there missing headers that Chrome requires? Does anyone have experience with getting dynamically generated PDFs to display in Chrome?
EDIT: One of my more salient questions is what could be causing this to work fine locally in Chrome, but wouldn't work on a server environment.
In my case I had to add these 2 parameters to headers because wordpress was sending 404 code as it didn't recognize the url of my php function:
header("Content-type: application/pdf",true,200);
as stated in this answer on wordpress.stackexchange.
This forces the headers to replace (2nd param true) the 404 status code generated by wordpress as it does not recognize the custom url, and sets 200 OK (3rd param 200).
So it ended being something like this:
$pdf_name = "test.pdf";
$pdf_file = "/absolute/path/to/my/pdfs/on/my/server/{$pdf_name}";
header('Content-type: application/pdf',true,200);
header("Content-Disposition: attachment; filename={$pdf_name}");
header('Cache-Control: public');
readfile($pdf_file);
exit();
Try this
<?php
$filename = 'Physical Path to PDf file.pdf';
$content = file_get_contents($filename);
header("Content-type:application/pdf");
// It will be called downloaded.pdf
header("Content-Disposition:inline;filename='".basename($filename)."'");
header('Content-Length: '.strlen( $content ));
// The PDF source is in original.pdf
readfile($filename);
?>
<html>
<body>
...
...
...
Make sure that above header code is called before output of PHP script is
sent to browser.
I want to thank everyone for their answers.
It turns out this was not related to the headers. After attempting to change/remove headers in various ways (detecting encoding, trying with and without content-length, etc.) we decided to dig into the deeper httpd logs to see if anything was resolving differently for Chrome.
It turns out that mod_sec on our server was flagging the request (only from Chrome for some reason) as an attempt at a file injection attack and was returning a 403 forbidden response. Chrome displayed this as the ERR_INVALID_RESPONSE rather than a 403.
The hostname of the CDN was present in the request (we had ample checking at the endpoint to ensure that the file was indeed an allowed resource), and instead are building the URL out on the server instead.
I have a file with no extension on it, but I know it's a tiff. I want to be able to download this file via PHP.
I created a page with a link to another php page, which has the following content:
<?php
$imgPath = 'http://server/23700-b074137f-eb5c-45d6-87c2-13c96812345b';
header("Content-disposition: attachment; filename=invoice.tiff");
header("Content-type: image/tiff");
readfile($imgPath);
?>
When I click the link, I get a prompt to download invoice.tiff, but it's 0 bytes.
However, if I rename the file on the server to 23700-b074137f-eb5c-45d6-87c2-13c96812345b.tiff (and change the $imgPath), it works.
How do I accomplish this without renaming the file to include the extension?
It's possible the 'tiff' extension is registered as a known file type on the server, so when you rename and request the tiff it's permissions will allow you to open it. However, with no extension, the security is probably stopping you from reading it, as mentioned by 'Mike B' above. To check this try just entering the file name in your browser address bar and see if it opens, both with and without the 'tiff' extension. There is no workaround for getting past the security issue, short of changing the severs security which would be very bad.
You are retrieving the file from a URL, therefore activating the 'fopen wrappers' in readfile. In general, you should not do this, especially when working locally since it invokes a lot of unnecessary overhead and (in this case) unwanted 'magic' behaviour.
Just use readfile on the local path to the file, and it'll be fine, or use die(file_get_contents($imgPath)) instead of the last line to circumvent PHP's native behaviour.
It works for me:
$imgPath = 'http://server/23700-b074137f-eb5c-45d6-87c2-13c96812345b';
$f = fopen($imgPath, "r");
header("Content-disposition: attachment; filename=invoice.tiff");
header("Content-type: image/tiff");
fpassthru($f);
You should also add the content-length header like so:
// untested code
header('Content-Length: '.strlen(stream_get_contents($imgPath)));
I'm sure this is a simple task, but on my wordpress site I want to create a download button that forces an .mp3 download, without opening a player (when left clicked), or the user having to right-click 'save target as'. I just need a straight forward button, that when left-clicked causes a file to be downloaded (as well as being easily trackable by Google Analytics).
Is a .php script required for this? You'd think this would be a very common function, and easy to solve....but I have spent hours on this and have been unable to get anything to work.
*if it's not obvious my coding skills are nearly non-existent.
I really appreciate anybody's time who can help me figure this out. Thanks!
***EDIT
Just found this on another post, but no comments if it would work or not. It was for a .pdf file though...
<?php
if (isset($_GET['file'])) {
$file = $_GET['file'] ;
if (file_exists($file) && is_readable($file) && preg_match('/\.pdf$/',$file)) {
header('Content-type: application/pdf');
header("Content-Disposition: attachment; filename=\"$file\"");
readfile($file);
}
} else {
header("HTTP/1.0 404 Not Found");
echo "<h1>Error 404: File Not Found: <br /><em>$file</em></h1>";
}
?>
Save the above as download.php
Save this little snippet as a PHP file somewhere on your server and you can use it to make a file download in the browser, rather than display directly. If you want to serve files other than PDF, remove or edit line 5.
You can use it like so:
Add the following link to your HTML file.
Download the cool PDF.
Well, this is possible, but you need to write a script to do it. This is a pretty poor (security and basic coding wise) from http://youngdigitalgroup.com.au/tutorial-force-download-mp3-file-streaming/
file: downloadit.php
<?php
header ("Content-type: octet/stream");
header ("Content-disposition: attachment; filename=".$file.";");
header ("Content-Length: ".filesize($file));
readfile($file);
exit;
?>
you would then place it into a publicly accessible folder and build your links as such:
http://www.yoursite.com/downloadit.php?file=/uploads/dir/file.mp3
what this does is tells the browser to treat the file as a stream of bytes, rather than a particular MIME type which the browser would ordinarily do based on the file extension.
I am trying to send files by using X-Sendfile directive in lighttpd.
My php code is;
header("Content-Type: application/force-download");
header( "Content-Disposition: attachment; filename=" . 's.php');
header("Content-Length: ". filesize("/home/web/domain/htdocs/download.php"));
header( "X-Sendfile: /home/web/domain/htdocs/download.php");
(I am sending download.php file just for testing purpose)
However, I get blank file no matter what I try. If I change the filename, I get;
2010-08-30 18:01:14: (mod_fastcgi.c.2587) send-file error: couldn't get stat_cache entry for: /home/web/domain/htdocs/downloa1d.php
So, it is working, but when I send the correct file it does not give any error in the logs and the browser downloads an empty file.
What could be wrong? What should I do?
It is likely that you don't have the right line in your lighttpd.conf file.
Add this in your fastcgi.server between the options with => arrows:
"allow-x-send-file" => "enable",
All but the last option end with commas.
I assume that you have your fastcgi already configured. If you are using a different framework for connecting PHP to your server, I can't help you.
If you have more than one PHP worker process, you must add that option to all of them.
I should add that .php files are small in size and outputting them with readfile() won't make a dent.
And, as I see it, you are using some non-critical headers wrongly. There is no such thing as force-download, it will just behave like an unknown file type. Use application/octet-stream. And your Content-Disposition is also shaky. Not only in that weird string enclosing. What the client should get is:
Content-Disposition: attachment; filename="My file ♥.txt"
The file name is surrounded by double quotes and it is NOT escaped; instead, it is either in UTF-8 or in the encoding of the calling page (this is a dirty grey area where every browser acts differently). You don't even have to include the file name to be on the safe side if you are satisfied with the downloading path. Then, it will look like this:
Content-Disposition: attachment
You can then use a pseudo-directory trick to supply a less buggy file name:
www.example.com/download.php/Custom%20Name.txt
This will work more reliably than that filename= parameter, but takes more planning in the link system. It may require you to do some redirecting.
OK this is an old post, but I'm going to reply anyway 'cause I was stuck on this too for way too long and I didn't find my solution anywhere so here I go:
Yeah I edited lighttpd.conf and added
fastcgi.server += ( ".php" => (( "allow-x-send-file" => "enable" )) )
and used
Header("X-LIGHTTPD-send-file: /path/filename");
in the PHP test page. But it didn't work. It took me a while to figure out I also had a PHP definition in
etc/lighttpd/conf-enabled/15-fastcgi-php.conf
After I added the option in there it worked :)