Stream protected media (located outside of httpdocs) with jPlayer - php

I have uploaded some sample mp3 files to a directory outside of httpdocs, I have ensured that this is accessible to PHP by configuring open_basedir correctly and tested that this directory is working.
What I would like to do is stream these files via a PHP file as non-authenticated users should never have access to these files. I am currently using jPlayer and expect the setMedia function should look similar to this:
$("#jquery_jplayer").jPlayer("setMedia", { mp3: "stream.php?track=" + id + ".mp3" });
I have tried setting content headers etc in stream.php and it currently looks like this:
$filePath = "../song_files/mp3/";
$fileName = "$_GET[track].mp3";
header("Content-Type: audio/mpeg");
header('Content-Disposition: attachment; filename="'.$fileName.'"');
getFile($filePath + $fileName);
If I load this page directly, the mp3 file downloads and plays fine, but when I use the above javascript, jPlayer doesn't play the track.
I have had a look at this post ( Streaming an MP3 on stdout to Jplayer using PHP ) and it appears the user was trying to achieve exactly what I want, but upon testing the solution I keep running into a problem, all I get is "CURL Failed".
Are there any different methods I can use to achieve this. Pointing me in the right direction would be greatly appreciated.

After searching around some more I have found a solution that is working fine. I used the code from a similar topic ( PHP to protect PDF and DOC )
I will place the code I used here to help answer the question correctly:
//check users is loged in and valid for download if not redirect them out
// YOU NEED TO ADD CODE HERE FOR THAT CHECK
// array of support file types for download script and there mimetype
$mimeTypes = array(
'doc' => 'application/msword',
'pdf' => 'application/pdf',
);
// set the file here (best of using a $_GET[])
$file = "../documents/file.doc";
// gets the extension of the file to be loaded for searching array above
$ext = explode('.', $file);
$ext = end($ext);
// gets the file name to send to the browser to force download of file
$fileName = explode("/", $file);
$fileName = end($fileName);
// opens the file for reading and sends headers to browser
$fp = fopen($file,"r") ;
header("Content-Type: ".$mimeTypes[$ext]);
header('Content-Disposition: attachment; filename="'.$fileName.'"');
// reads file and send the raw code to browser
while (! feof($fp)) {
$buff = fread($fp,4096);
echo $buff;
}
// closes file after whe have finished reading it
fclose($fp);
</code></pre>

Related

Php header() User Agent Change

$file_name = $_GET['title'];
$file_url = $_GET['url'] . $file_name;
header('Content-Type: video/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"".$file_name."\"");
readfile($file_url);
exit;
I'm using this code to download files in my site fetching from another websites.
It works if my url looks like:-
https://www.example.com/video_download.php?title=video.mp4&url=http://googlevideo.com/video/download/223689/289048
(example)
So, it starts downloading by fetching the video file from http://www.googlevideo.com/video/play/221589 to my site.
But my problem is that the file can be accessed if the person uses a PC.
Can I change the User Agent by using header()?
Is it possible?
So if I change the user agent into a PC user agent, so it can be downloaded from a mobile!
I'm sorry, but the User Agent has nothing to do with readfile() function. Readfile() will just throw the raw file input into your browser. Useful for e.g. rendering images through PHP to the client without having to expose the real file name.
Indeed, it is possible to render video to the client with readfile(), but using a HTML5 video tag will dramatically improve performance. This will also provide better mobile support.
Hope this helps you,
You can use stream_compy_to_stream
$video = fopen($file_url);
$file = fopen('videos/' . $title . '.mp4', 'w');
stream_copy_to_stream($video, $file); //copy it to the file
fclose($video);
fclose($file);
I wrote a class for downloading youtube video files. you can find it here.

Download a remote file and rename the file- & filetype on client-side

I am working on a project where on the client-side it should start a download from a remote URL.
I currently got it to work with cURL, but the problem is that it downloads to the server, and not on the client's browser (asking where to save it).
The URL looks like this:
http://r13---sn-5hn7snee.googlevideo.com/videoplayback?expire=138181&mv=u&ipbits=0&clen=7511717&fexp=929447,913430,930802,916612,902546,937417,913434,936916,934022,936921,936923&ms=au&mt=1396164&itag=247&key=yt5&source=youtube&ip=2a00:7143:100:1:225:90ff:fe52:b3ad&sparams=clen,dur,gir,id,ip,ipbits,itag,lmt,source,upn,expire&signature=DBDCE9EFFC094C0FC653610C8FAAF367EA942CC4C6EF3971A186C&upn=0JQJ6yqyYPo&sver=3&gir=yes&lmt=13960601932&dur=130.133&id=17f0fd8b&size=1280x720,type=video/mp4
When downloading this just from the URL the filename will be 'videoplayback' without any file extension. So I need to have a custom file name and .mp4 as file type. (Which works on the cURL code below), but it writes to server instead of client-side download.
function download($file_source, $file_target) {
$rh = fopen($file_source, 'rb');
$wh = fopen($file_target, 'w+b');
if (!$rh || !$wh) {
return false;
}
while (!feof($rh)) {
if (fwrite($wh, fread($rh, 4096)) === FALSE) {
return false;
}
echo ' ';
flush();
}
fclose($rh);
fclose($wh);
return true;
}
$result = download('http://r13---sn-5hn7snee.googlevideo.com/videoplayback?expire=138181&mv=u&ipbits=0&clen=7511717&fexp=929447,913430,930802,916612,902546,937417,913434,936916,934022,936921,936923&ms=au&mt=1396164&itag=247&key=yt5&source=youtube&ip=2a00:7143:100:1:225:90ff:fe52:b3ad&sparams=clen,dur,gir,id,ip,ipbits,itag,lmt,source,upn,expire&signature=DBDCE9EFFC094C0FC653610C8FAAF367EA942CC4C6EF3971A186C&upn=0JQJ6yqyYPo&sver=3&gir=yes&lmt=13960601932&dur=130.133&id=17f0fd8b&size=1280x720,type=video/mp4');
if (!$result)
throw new Exception('Download error...');
So my question is how I can turn this into so it renames the file and changes the extension to mp4, and then downloads the file on client-side.
Thanks
First output the correct headers to the client so it knows how to handle a file and then echo the file contents. So try this way:
<?php
// ... fetch the file using CURL and store it (in memory or on file system) ...
// Set the content type
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
// ... echo/output the file contents to the browser...
?>
Check out "Example #1 Download dialog" in the PHP documentation.
Also, you mention you are using CURL, I don't see that anywhere in your code.

Zip files generated by PHP file

I am needing to parse a series of php files to output .PDFs and .PNGs files before zipping them using zipArchive. What I would like to do is something like
$zip = new ZipArchive();
$zip->open($file, ZipArchive::OVERWRITE);
//If you access qr_gen.php on a browser it creates a QR PNG file.
$zip->addFile('qr_gen.php?criteria=1', 'alpha.png');
$zip->addFile('qr_gen.php?criteria=2', 'beta.png');
//If you access pdf_gen.php on a browser it creates a PDF file.
$zip->addFile('pdf_gen.php?criteria=A', 'instructions.pdf');
$zip->close();
header('Content-Type: application/zip');
header('Content-Length: ' . filesize($file));
header('Content-Disposition: attachment; filename="file.zip"');
readfile($file);
unlink($file);
This obviously does not work. How can I accomplish my goal?
The following line will not work as you provide and url as filename:
$zip->addFile('qr_gen.php?criteria=1', 'alpha.png');
Instead you'll have to download the pngs first and store them locally. Then add them to the zip archive. Like this:
file_put_contents('alpha.png',
file_get_contents('http://yourserver.com/qr_gen.php?criteria=1');
$zip->addFile('alpha.png');
You'll find more information at the documentation page of ZipArchive::addFile()
What you will need to do is to get the files locally first. This can be (trivially) achieved using file_get_contents if you have URL fopen mappers set up, or failing that, cURL calls.
This is a sample way to do it:
$zip = new ZipArchive();
$zip->open("zipfile.zip",ZipArchive::OVERWRITE);
$URLs = array(
"alpha.png" => "http://my.url/qr_gen.php?criteria=1",
"beta.png" => "http://my.url/qr_gen.php?criteria=2",
"instructions.pdf" => "http://my.url/pdf_gen.php?criteria=A");
foreach ($URLs as $file => $URL) {
$f = #file_get_contents($URL);
if (empty($f)) throw new Exception("File not found: ".$URL);
$zip->addFromString($file, $f);
}
Your zip is then available as $zip for further processing.
First of all, execute all files into browser and put that content (png.pdf) into one folder and then create zip of it by fetching one by one.
hope it helps

Correctly setting headers so a file can be downloaded via a proxy using PHP

I'm finding it difficult to phrase this question correctly, let me try to explain our problem...
We have an intranet running on Ubunutu box with Apache2/PHP 5.2.4. We have a bit of PHP code that reads a file from a directory that is not publically accessible and output it to the screen (code below):
$file_path = '/home/path/to/filename.gif';
if(file_exists($file_path)){
$output = FALSE;
//File Information
$path_parts = pathinfo($file_path);
$file_size = filesize($file_path);
$file_ext = (isset($path_parts['extension'])) ? strtolower($path_parts['extension']) : null;
$file_name = $path_parts['basename'];
//Sets up the headers
if($file_size > 0){
header('Content-Length: ' .$file_size);
}
header('Content-Disposition: attachment; filename="'.$file_name.'"');
header('Content-Type: application/octet-stream');
//Reads the File
if($file_size > 0){
$handle = fopen($file_path, "r");
$output = fread($handle, $file_size);
fclose($handle);
}
//Outputs the File
echo $output;
}
Inside our network when, browsing to the page that uses this code, the file is downloaded perfectly and quickly...
However, when accessing this page via our Cisco ASA/Proxy/VPN (not sure what to call it) this code locks up the browser, but does eventually download the file...
After a bit of experimenting, after taking out the headers and just echoing the contents of the file to the browser, it prints no problem. However as soon as I add the lines with the headers back into the code it causes the hanging again, but only when accessed via this box..
Anybody come across this problem before or have any idea what we can try to move forward?
Thanks for any advice...
Have you tried eliminating the content-size header entirely? The proxy may be taking that as a firm promise and if the data you're sending ends up being a different size, the proxy may wait for those last few "missing" bytes to show up.
Just as an aside, you should use [readfile()][1] instead of the fopen()/fread()/echo construct you have now.
As it stands now, you're slurping the contents of the entire file into memory and then echoing out. For large files and multiple requests, you'll kill the server with memory starvation. readfile will automatically stream the file in smaller chunks so that memory usage is minimal.
Your proxy obviously have problems with the Content-Type: application/octet-stream. Try setting it to the real MIME-type of each file. You can use the Fileinfo module to find out which MIME-type a certain file is, like this:
//You may need to specify the location of your system's magic file
//See http://php.net/finfo_open for more info
$finfo = new finfo(FILEINFO_MIME);
$mimetype = $finfo->file($file_path);

PHP ZIP files on the fly

What's the easiest way to zip, say 2 files, from a folder on the server and force download? Without saving the "zip" to the server.
$zip = new ZipArchive();
//the string "file1" is the name we're assigning the file in the archive
$zip->addFile(file_get_contents($filepath1), 'file1'); //file 1 that you want compressed
$zip->addFile(file_get_contents($filepath2), 'file2'); //file 2 that you want compressed
$zip->addFile(file_get_contents($filepath3), 'file3'); //file 3 that you want compressed
echo $zip->file(); //this sends the compressed archive to the output buffer instead of writing it to a file.
Can someone verify:
I have a folder with test1.doc, test2.doc, and test3.doc
with the above example - file1 (file2 and file3) might just be test1.doc, etc.
do I have to do anything with "$filepath1"? Is that the folder directory that holds the 3 docs?
Sorry for my basic question..
Unfortunately w/ PHP 5.3.4-dev and Zend Engine v2.3.0 on CentOS 5.x I couldn't get the code above to work. An "Invalid or unitialized Zip object" error message was all I could get. So, in order to make it work, I had to use following snippet (taken from the example by Jonathan Baltazar on PHP.net manual, at the ZipArchive::open page):
// Prepare File
$file = tempnam("tmp", "zip");
$zip = new ZipArchive();
$zip->open($file, ZipArchive::OVERWRITE);
// Stuff with content
$zip->addFromString('file_name_within_archive.ext', $your_string_data);
$zip->addFile('file_on_server.ext', 'second_file_name_within_archive.ext');
// Close and send to users
$zip->close();
header('Content-Type: application/zip');
header('Content-Length: ' . filesize($file));
header('Content-Disposition: attachment; filename="file.zip"');
readfile($file);
unlink($file);
I know this is different than working w/ memory only - unless you have your tmp area in ram ;-) - but maybe this can help out someone else, who's struggling with the solution above, like I was; and for which performance penalty is not an issue.
Your code is very close. You need to use the file name instead of the file contents.
$zip->addFile(file_get_contents($filepath1), 'file1');
should be
$zip->addFile($filepath1, 'file1');
http://us3.php.net/manual/en/function.ziparchive-addfile.php
If you need to add files from a variable instead of a file you can use the addFromString function.
$zip->addFromString( 'file1', $data );
This works for me (nothing is written to disk)
$tmp_file = tmpfile(); //temp file in memory
$tmp_location = stream_get_meta_data($tmp_file)['uri']; //"location" of temp file
$zip = new ZipArchive;
$res = $zip->open($tmp_location, ZipArchive::CREATE);
$zip->addFile($filepath1, 'file1');
$zip->close();
header('Content-type: application/zip');
header('Content-Disposition: attachment; filename="download.zip"');
echo(file_get_contents($tmp_location));
If you have access to the zip commandline utility you can try
<?php
$zipped_data = `zip -q - files`;
header('Content-type: application/zip');
header('Content-Disposition: attachment; filename="download.zip"');
echo $zipped_data;
?>
where files is the things you want to zip and zip the location to the zip executable.
This assumes Linux or similar, of course. In Windows you might be able to do similar with another compression tool, I guess.
There's also a zip extension, usage shown here.
maraspin's Answer is what I tried. Strangely, I got it working by removing the header line that references the file size:
header('Content-Length: ' . filesize($file));
With the above change, the code works like a breeze!
Without this change, I used to get the following error message:
End-of-central-directory signature not found. Either this file is not a zipfile, or it constitutes one disk of a multi-part archive. In the latter case the central directory and zipfile comment will be found on the last disk(s) of this archive.
Test environment:
OS: Ubuntu 10.10
Browser: Firefox
And the default LAMP stack.
To create ZIP files on the fly (in memory), you can use ZipFile class from phpMyAdmin:
PMA\libraries\ZipFile.php
An example of how to use it in your own application:
https://stackoverflow.com/a/9648562/767871
Note: Your ZIP files will be limited by PHP's memory limit, resulting in corrupted archive if you exceed the limit.
itsols
If you want to insert the 'Content-Length' do it like this:
$length = filesize($file);
header('Content-Length: ' . $length);
I don't know why, but it crashes if you put it in the same line.
On Unix systems (and maybe others),
you can apply a simple trick to #maraspin's answer by deleting the file entry for the file ("unlinking" it from its inode), and send its data using a handle opened previously. This is basically the same thing tmpfile() does; this way you can "temporarify" any file.
The code is the same as maraspin's up to the very last lines:
// Prepare File
$file = tempnam("tmp", "zip");
$zip = new ZipArchive();
$zip->open($file, ZipArchive::OVERWRITE);
// Stuff with content
$zip->addFromString('file_name_within_archive.ext', $your_string_data);
$zip->addFile('file_on_server.ext', 'second_file_name_within_archive.ext');
// Close and send to users
$zip->close();
header('Content-Type: application/zip');
header('Content-Length: ' . filesize($file));
header('Content-Disposition: attachment; filename="file.zip"');
// open a handle to the zip file.
$fp = fopen($file, 'rb');
// unlink the file. The handle will stay valid, and the disk space will remain occupied, until the script ends or all file readers have terminated and closed.
unlink($file);
// Pass the file descriptor to the Web server.
fpassthru($fp);
As soon as the script finishes, or terminates abnormally, or the application pool is cycled, or the Apache child gets recycled -- the "disappearance" of the file will be formalized and its disk space released.

Categories