I am trying to build an application in which i have to stream the media files (audio and video) to the browser. I am reading the file through php and send the data to browser. I am using the following code.
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: {$file->getMimetype()}");
header("Content-Disposition: inline; filename=".$filename.";");
header("Content-Length: ".strlen($file_content));
echo $file_content;
Every thing is working fine, except when i try to forward the video or audio, (I mean suppose current play location is 0:15 and it directly go to 1:25), media stops and when i press the play button again, it starts from the beginning.
I think the problem is with the buffering, but can't figure it out. Am i doing something wrong in header or something else is required.
Thanks.
I think you need to implement the Range header, so that the client can skip to a specific position in the file. You can probably find out what goes wrong by sniffing the request the player sends.
What you want is called "Content-Range requests"
Have a look here Resumable downloads when using PHP to send the file?
I came across this recently which may help you:
http://www.jasny.net/articles/how-i-php-x-sendfile/
Rather than passing the whole file through PHP (which eats up memory), you can use x-sendfile. This is an Apache module which allows you to run a PHP program, but pass control back to the web server to handle the actual file download once your code has done what it needs to do (authentication, etc).
It means that your PHP code doesn't have to worry about how the file is served; let the web server do what it's designed for.
Hope that helps.
Here is a good tutorial for it, you only want the PHP section but still:
http://www.devshed.com/c/a/PHP/Video-Streaming-PHP-Script-Tutorial/3/
Related
I'm in the process of developing a PHP webpage that constructs a .SVG file from a SQL database on the fly, embeds it in the page, and enables a user to interact with it. These temporary files take on the form SVG[RandomNumber].svg and the unlink function successfully deletes the files with no error messages.
Here's the problem: I assumed that if I invoked the unlink function after the SVG file had loaded for the user, the webpage would be unaffected since the user's browser would have cached the file or whatnot. Everything works perfectly when no unlink command is present in the code; however, 'unlinking' anywhere -- even at the end of the webpage, causes no object to show at all. In Firefox there's no trace of the object, and in IE I receive the error "The webpage cannot be found."
So have I deleted the file before the browser uploads it? What's the best way to deal with the general situation?
Thank you.
It might be useful to change workflow and don't create temporaries. When image is used only once or it's generation is not a big deal you can try to generate it on-the-fly in following fashion
<?php
// We'll be outputting a SVG
header('Content-type: Content-Type: image/svg+xml');
// It will be called image.svg
header('Content-Disposition: attachment; filename="image.svg"');
// Don't cache
header("Cache-Control: no-cache, must-revalidate");
header("Expires: " . date("D, j M Y H:i:s"));
// The PDF source is in original.pdf
generate_svg_from_db('image.svg');
?>
I want to download a file with android and so i stream the file with php with the following function
function stream($fileName,$fileSize,$fName){
//header("Pragma:public");
//header("Expires:0");
//header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
//header("Cache-Control:private",false);
header("Content-Disposition:attachment;filename=\"".$fName."\";");
header("Content-Type:application/octet-stream");
header("Content-Transfer-Encoding:binary");
header("Content-Length:".$fileSize);
readfile($fileName);
}
But the download does not work on an android device. On my computer, it isn't a problem. I already found a solution on http://www.digiblog.de/2011/04/android-and-the-download-file-headers/ but this didn't helped me here.
As mentioned in my blog posting that you cite, the success of your download on Android devices depends on multiple factors, which cannot be told by reading your code:
Your $fileName must contain an uppercase extension.
The pure URL that you call to download must contain all information needed for the download to start. No POST parameters may be involved, they will not be forwarded to the Android Download Manager and thus the download will fail. GET parameters (query string) are ok.
Also you did not obey the blog posting in all details:There is a semicolon after your Content-Disposition header which is fatal.
I also recommend to start with the Content-Type and Content-Disposition headers ONLY. When you got it working on Android, you can still add any other headers that you want to send. But do not forget to re-test on Android (multiple versions if possible) when you are complete. Android is really picky about some of those headers (or should I say "bitchy"?).
If all of this does not help you will have to provide some more information about your case. What exactly happens when you try it? What URL do you call. What are the parameters passed to your function?
I've coded an option called 'devmode' in my web app, which basically means 'no caching'. The app normally outputs an automatically minified (and aggregated) version of the Javascript and CSS, but the devmode option overrides this.
However, we still have the browser cache. So, without further ado, how can I disable caching of ALL components on a page if a certain PHP boolean is true?
Cheers
Edit: might interest you to know that I'm running Apache, and one idea I had was to force .js and .css to be parsed as PHP (which is straightforward), and somehow 'inject' a little piece of PHP code at the start of each.
A "quick and dirty" approach for debugging/developing, you could call all components in your HTML with a random (or time-based) query-string. For instance:
<img src="logo.png?uniqecall=20111026035500" />
, which would look like this in your PHP code:
print '<img src="logo.png?uniqecall=' . date("YmdHis") . '" />';
etc...
.htaccess
RewriteRule ^no-cache/(.*?)$ no-cache.php?file=$1 [QSA,L]
no-cache.php
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
readfile('static/'.$_GET['file']);
Assuming you won't hack yourself :)
THE EXAMPLE
1) User enters in a playlist in a <textarea>
C:/music/foo.mp3
C:/music/bar.mp3
C:/music/hello.mp3
2) They click a save button. I send the user's playlist to the server with AJAX.
3) The server formats the text with PHP in this fashion:
<playlist>
<item>C:/music/foo.mp3</item>
<item>C:/music/bar.mp3</item>
<item>C:/music/hello.mp3</item>
</playlist>
4) A file save dialog pops up asking the user to save this formatted text as playlist.m3u on their own harddrive.
QUESTIONS
A) Is it possible to not write a file to the harddrive on the server when generating this m3u file? I don't want millions of files clogging up my server. I suppose PHP can echo out the formatted text and set headers to masquerade as a file.
B) How do I get the file save dialog to pop up for this on-the-fly file? If it were a real file, I would just have the PHP respond back with the location of the file. Then I would have JS insert a new iFrame with that location. But I don't want to write a file on the server, so I can't do this.
new Ajax.Request(
'generateM3u.php',
onSuccess: function(transport) {
$$('body').first().appendChild(
new Element(
'iframe', {
src: transport.responseText
}
)
);
}
);
You should take a look at http://php.net/manual/en/function.header.php from the PHP manual. There are a lot of user contributions at the bottom of the page regarding forcing the browser to show a download prompt rather than printing to screen.
Here is one from that page (By phpnet at holodyn dot com 31-Jan-2011 09:01) which I have edited slightly. I think it answers both questions A and B. Just send the textbox's contents to the PHP file through an iframe, allow it to format the text appropriately and send it back to the browser with the following headers.
$contents = '<playlist>etc....</playlist>';
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false); // required for certain browsers
header("Content-Type: audio/x-mpegurl");
header("Content-Disposition: attachment; filename=\"playlist.m3u\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($contents));
ob_clean();
flush();
echo $contents;
Edit: If what you want is an all Javascript solution, then I personally don't know, and after a little google-ing, it looks like others don't either. Most seem to solve this with an invisible iframe that directs to a server-side file.
Edit 2: I've changed the content type so that it matches the m3u file type.
How about creating a form on your parent DOM, and post it to the IFRAME/pop-up that you created?
The POST action URL will be your generateMu3.php
To answer your questions,
A & B) I assume so... as long as generateM3u.php sets the correct MIMEType for the .m3u file...
I'm not familiar with syntax in PHP, but in both Java & .NET, you can set the response's MIMEType in the header to, say, a Word document, and the browser will read the header, and if it's a file that is "Save-able", it'll prompt the client to save the page as a file.
If I read this correctly there's a machine creating the .m3u files. In that case, perhaps just write the files to a temporary directory, /tmp on unix machines andC:\Windows\Temp on Windows machines. Those files are cleared on boot, which should allow you to handle B) without all the A).
The Situation
I am creating a video training site for a client on the RackSpace Cloud using the traditional LAMP stack (RackSpace's cloud has both Windows and LAMP stacks). The videos and other media files I'm serving on this site need to be protected as my client charges money for access to them. There is no DRM or funny business like that, essentially we store the files outside of the web root and use PHP to authenticate user's before they are able to access the files by using mod_rewrite to run the request through PHP.
So let's say the user requests a file at this URL:
http://www.example.com/uploads/preview_image/29.jpg
I am using mod_rewrite to rewrite that url to:
http://www.example.com/files.php?path=%2Fuploads%2Fpreview_image%2F29.jpg
Here is a simplified version of the files.php script:
<?php
// Setups the environment and sets $logged_in
// This part requires $_SESSION
require_once('../../includes/user_config.php');
if (!$logged_in) {
// Redirect non-authenticated users
header('Location: login.php');
}
// This user is authenticated, continue
$content_type = "image/jpeg";
// getAbsolutePathForRequestedResource() takes
// a Query Parameter called path and uses DB
// lookups and some string manipulation to get
// an absolute path. This part doesn't have
// any bearing on the problem at hand
$file_path = getAbsolutePathForRequestedResource($_GET['path']);
// At this point $file_path looks something like
// this: "/path/to/a/place/outside/the/webroot"
if (file_exists($file_path) && !is_dir($file_path)) {
header("Content-Type: $content_type");
header('Content-Length: ' . filesize($file_path));
echo file_get_contents($file_path);
} else {
header('HTTP/1.0 404 Not Found');
header('Status: 404 Not Found');
echo '404 Not Found';
}
exit();
?>
The Problem
Let me start by saying this works perfectly for me. On local test machines it works like a charm. However once deployed to the cloud it stops working. After some debugging it turns out that if a request to the cloud has certain file extensions like .JPG, .PNG, or .SWF (i.e. extensions of typically static media files.) the request is routed to a cache system called Varnish. The end result of this routing is that by the time this whole process makes it to my PHP script the session is not present.
If I change the extension in the URL to .PHP or if I even add a query parameter Varnish is bypassed and the PHP script can get the session. No problem right? I'll just add a meaningless query parameter to my requests!
Here is the rub: The media files I am serving through this system are being requested through compiled SWF files that I have zero control over. They are generated by third-party software and I have no hope of adding or changing the URLs that they request.
Are there any other options I have on this?
Update: I should note that I have verified this behavior with RackSpace support and they have said there is nothing they can do about it.
If the requesting flash app is following redirects, I would try to answer with a redirect on the first request and rewrite the second one, e.g.
GET .../29.jpg
to
header("Status: 302 Moved temporarily");
header("Location: .../r.php?i=29.jpg&random=872938729348");
Then your r.php delivers the file on the second request.
If not (btw. always), I would explicitly send headers along with delivering the static files that Varnish accepts and acts accordingly, something like
header("Cache-Control: no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
And:
I would place the exit(); command after your first header() statement to be sure the rest of the script is not executed. header() sends just headers.
I find it also more reliable to use ob_start() as whitespace in your PHP file may lead to annoying errors when adding headers.
I have the same situation, and I've contacted Rackspace hoping for a better answer.
I got one! They've put together a FAQ outlining half a dozen ways to bypass/modify the caching:
http://cloudsites.rackspacecloud.com/index.php/How_can_I_bypass_the_cache%3F