download large mkv files with php script - php

After some testing with the code below I've come to this conclusion:
The script works fine for AVI files in both firefox and chrome (if I change the content type to video/avi of course)
The script works fine for MKV files in Chrome. Firefox throws an error message in my face though (see below)
I've downloaded a 20 mb big test file (test7.mkv) from http://matroska.org/downloads/test_w1.html to test with a smaller file. Strangely Firefox can download that file and it seems to work. However if I try it on a 6 gb big file I get the firefox error you see below
Edit: Downloaded a 700 mb file, that didnt play at all in SM player (exitcode 1) but played fine in VLC player. I'm currently looking for a way to find how and where the file might be damaged.
My obvious question is: what am I doing wrong? How to properly download a mkv file in firefox or better in any common browser for that matter. I cant find anything helpful on google but maybe I'm just looking in the wrong places.
Firefox Error when trying to download a mkv file:
Corrupted Content Error
The page you are trying to view cannot be shown because an error in the data transmission was detected.
The page you are trying to view cannot be shown because an error in the data transmission was detected.Please contact the website
owners to inform them of this problem.
function download($file)
{
$path = $_SERVER['DOCUMENT_ROOT']; //<-- added the relative part after that
$fullPath = $path.$file;
set_time_limit(0);
if ($fd = fopen ($fullPath, "r"))
{
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: video/x-matroska");
header("Content-Disposition: attachment; filename=\"$file\"");
header("Content-Transfer-Encoding: binary");
header("Content-length: $fsize");
$file = #fopen($fullPath,"rb");
if ($file)
{
while(!feof($file))
{
print(fread($file, 1024*8));
flush();
if (connection_status()!=0)
{
#fclose($file);
die();
}
}
#fclose($file);
}
exit;
}
}

the absolute path to an mkv file on the server
Firefox may be choking on the absolute path in the filename header value.
Try specifying mere a file name instead:
$filename = pathinfo($filePath, PATHINFO_BASENAME);
header("Content-Disposition: attachment; filename=\"$filename\"");

Solution:
I should've checked if the variables I define are set to the expected values. Turns out filesize() returns a signed int with 32 bit.
For now I just took the function that's shown here: https://stackoverflow.com/a/5502328/1232791
An other possible solution would be to not define the content length header. With this solution the client wont know how long his download is going to take though.

Related

Browser incompatibility when serving a PDF using PHP fopen or readfile

I've been going around in circles trying to serve a PDF securely using variations of fopen() and readfile(). Most methods work fine in Firefox and Chrome whether I'm on Windows/OSX/iOS but no method will work with Internet Explorer or Safari. I've tried all sorts of header variations based on what others have suggested but no luck.
Here is my code:
<?php
session_start();
if ($_SESSION['user_is_logged_in'] === true || $_SESSION['admin_user_is_logged_in'] === true) {
$name = $_GET['name'];
$path = "www.mypath.com/$name";
$fp = fopen($path, 'rb');
header("Content-Type: application/pdf");
header("Content-Length: " . filesize($path));
//Experimented with this but all it does is force a download instead of opening in browser.
//header("Content-Disposition: attachment; filename=".$name);
readfile($path);
//alternately tried
//passthru($fp);
}
else {
echo "You do not have permission to view this file. Please <a href='../../index.php'>log in</a>.";
}
exit;
?>
If I use Content-Disposition in the header the result is any PDF downloaded in Safari or IE is 0KB and not readable. Without content-disposition IE and Safari just hang on a blank page.
These are some advices I would like to share:
Try to use localpaths ie. windows (c:\folder\resource.pdf) or linux (/var/www/project/resouce.pdf).
Print your $path and check that is a file generated correctly.
Then try this piece of code, that is which I use:
<?php
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename={$this->filename}.pdf");
readfile($path);
unlink($path);

Writing into txt file return empty on IE

I have a code that save the data of a form into a TXT file and it seems like Internet Explorer can't read the file once it is closed.
The file is successfully saved in my folder and when I load it from the FTP I can see my values, but when I do readfile() on the "submit", the donwloadable file is empty from data.
Here is my code :
$fp = fopen("plan_de_concepts/". $nom_du_fichier, "w");
$savestring = "$sujet=*|*=$concept1=*|*=$concept2=*|*=$concept3=*|*=$c1mc1=*|*=$c1mc2=*|*=$c1mc3=*|*=$c1mc4=*|*=$c1mc5=*|*=$c2mc1=*|*=$c2mc2=*|*=$c2mc3=*|*=$c2mc4=*|*=$c2mc5=*|*=$c3mc1=*|*=$c3mc2=*|*=$c3mc4=*|*=$c3mc5=*|*=$c4mc1=*|*=$c4mc2=*|*=$c4mc3=*|*=$c4mc5=*|*=$c5mc1=*|*=$c5mc2=*|*=$c5mc3=*|*=$c5mc3=*|*=$c5mc4=*|*=$c5mc5=*|*=$c6mc1=*|*=$c6mc2=*|*=$c6mc3=*|*=$c6mc4=*|*=$c6mc5";
fwrite($fp, $savestring);
fclose($fp);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Length: ". filesize($nom_du_fichier));
header("Content-Disposition: attachment; filename=". $nom_du_fichier);
header("Content-Type: application/octet-stream; ");
header("Content-Transfer-Encoding: binary");
readfile('plan_de_concepts/'.$nom_du_fichier);
Works great on Firefox and Chrome but IE return empty.. Is there any ways I could force to donwload the "uploaded" version of the file?
You use filesize($nom_du_fichier) and readfile('plan_de_concepts/'.$nom_du_fichier) (note the folder-prefix). I expect only one of the files exists.
It looks like IE is actually using the Content-Length-header where FF is silently ignoring it.
My Content-length was different of the file exact path. Internet Explorer could not find the file to read it.
It should be noted that in the example:
header('Content-Length: ' . filesize($file));
$file should really be the full path to the file. Otherwise content
length will not always be set, often resulting in the dreaded "0 byte
file" problem.
Source : PHP readfile returns zero length file

Download files php always damaged

Im trying to make a script to download images from my localhost. Just for a schoolproject.
I get the filename via the url ("$_GET['file']"). Then i run this script. Every time the file is damaged and can't be viewed. I want to download images, but when i tried a word document it also was damaged. This is my code:
<?php
//get file
$file = $_GET['file'];
//set path of file
$path = $_SERVER['DOCUMENT_ROOT']."/blackbox/mediafiles/";
$fullPath = $path.$file;
if ($fd = fopen ($fullPath, "r")) {
$path_parts = pathinfo($fullPath);
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // fore a download
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
}
header("Cache-control: private"); // open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
fclose ($fd);
exit;
?>
Anybody has and idea what goes wrong?
Thanks in advance!!
Try opening the file explicitly in binary mode:
if ($fd = fopen ($fullPath, "rb")) {
As the documentation on fopen states:
Windows offers a text-mode translation flag ('t') which will
transparently translate \n to \r\n when working with the file. In
contrast, you can also use 'b' to force binary mode, which will not
translate your data. To use these flags, specify either 'b' or 't' as
the last character of the mode parameter.
The default translation mode depends on the SAPI and version of PHP
that you are using, so you are encouraged to always specify the
appropriate flag for portability reasons. You should use the 't' mode
if you are working with plain-text files and you use \n to delimit
your line endings in your script, but expect your files to be readable
with applications such as notepad. You should use the 'b' in all other
cases.
If you do not specify the 'b' flag when working with binary files, you
may experience strange problems with your data, including broken image
files and strange problems with \r\n characters.
You can use readfile instead of manually reading file and outputting.
Also, please note, that $_GET['file'] can contain '../' and open any file, which is a security risk. Use the basename function (if all files are in the same directory) or restrict access to files outside the mediafiles directory
<?php
//get file
$file = $_GET['file'];
//set path of file
$path = $_SERVER['DOCUMENT_ROOT']."/blackbox/mediafiles/";
$fullPath = $path. basename($file);
if (is_readable($fullPath) {
$path_parts = pathinfo($fullPath);
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // fore a download
header("Content-type: application/octet-stream");
header("Content-length: " . filesize($fullPath));
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
header("Cache-control: private"); // open files directly
readfile($fullPath);
}
exit;
?>
At first its not your answer. But you should really look at Security in PHP. On Your script you can access all files over the GET Parameter and DOCUMENT_ROOT.
Don't trust a user.
You should filter your variables at first or map them with a known list of files before deliver them to a user.
PHPSec
Either something is throwing an error from PHP, or you have leading whitespace before the opening tag in the file (<?php).
Do two things:
Make sure the < of <?php is the first character in the file. Make sure your script doesn't have a byte order marker in it.
Add these lines to the top of the script: error_reporting(0); ini_set('display_errors', 0);
Note that if disabling errors fixes your problem it means that there is an error which needs to be fixed, it is not a final solution to the problem!
Using readfile() is also shorter, safer and more efficient than opening a file pointer and looping it.

Force file download with PHP: corrupted files, have tried several solutions

This has been posted, but I've tried lot of solutions found on SO and more (like this: http://davidwalsh.name/php-force-download)
I basically have this:
{
$filePath = '../public/myfile.png';
$fileName = basename($filePath);
$fileSize = filesize($filePath);
if (!is_file) {
die("File not found");
} else {
header("Content-Description: File Transfer");
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename= " . $fileName);
header("Content-Transfer-Encoding: binary");
readfile($filePath);
}
}
Files are recognized and downloaded, but .PNGs are empty and .DOCs are corrupted (and Word asks me to fix the file, then it's ok). I have tried also PDFs, and no problem with that.
I trues to put all sort of options (Pragma, Cache-Control, Expires, Content-Length, etc.), still downloaded files but corrupted in some way...
Did you ever had my problem? Please consider I'm on IIS 7.5
Thanks in advance
Open the downloaded files with a plain text editor like Notepad++. At the top you will find a PHP Error notice, it will tell you what's going wrong.
The error is probably "session already send". Then add ob_start(); at the beginning of your script.

Zip file downloads, but is invalid?

I use this code to enable users to download a zip file:
if(file_exists($filename)){
header("Content-Disposition: attachment; filename=".basename(str_replace(' ', '_', $filename)));
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-Length: " . filesize($filename));
flush();
$fp = fopen($filename, "r");
while (!feof($fp))
{
echo fread($fp, 65536);
flush();
}
fclose($fp);
exit;
}
When the file is downloaded, it only downloads 25,632 kilobytes of data. However the zip file is 26,252 kilobytes ...
Why does the browser get all 25MB but then stop?
I checked the Content-Length header to make sure it was correct and it is...
edit
In firefox, when i download the file, it says 'of 25mb' SO the browser thinks that 25mb is the COMPLETE amount... however, the content-length when echo'd is 26252904?
add this before your code
ob_clean();
ob_end_flush();
Your header('Content-Type ...) calls are useless as only the last one will be sent to the browser.
Downloads are triggered by Content-Disposition: attachment. You should send the actual Content-Type: application/zip if you are sending a zip file.
Finally, your read loop is unnecessary.
Putting it all together, your code should look like this:
if (file_exists($filename)) {
$quoted_filename = basename(addcslashes($filename, "\0..\37\"\177"));
header("Content-Disposition: attachment; filename=\"{$quoted_filename}\"");
header('Content-Type: application/zip');
header('Content-Length: '.filesize($filename));
readfile($filename);
}
Use a single MIME type to represent the data.
In this case using application/octet-stream will do just fine. This is when you dont know the MIME before hand. When you know it, you must put it. Do not use multiple content-type headers.
Usually, when the browser doesn't know how to handle a particular MIME, it will trigger the download process. Further, using Content-disposition: Attachment; .. ensures it.
There exists a simple readfile($filename) which will send out the bytes of the file to the requesting process like below:
header("Content-disposition: attachment;filename=" . basename($filename);
readfile($filename);
I had similar problem. The file downloaded fine in Firefox but not in IE. It appeared that Apache was gzipping the files and IE was not able to ungzip so the files were corrupted. The solution was to disable gzipping in Apache. You can also check if PHP is not gzipping on the fly and disable it too.
For Apache you can try:
SetEnv no-gzip 1
And for PHP, in .htaccess:
php_flag zlib.output_compression on
This answer is by No means a REAL ANSWER.
However i did get it to work... I just set the Content-Length to 30000000. Therefor it thinks the file is bigger than it actually is, and then it downloads it all.
Ugly hack i know, but i couldn't find ANY other way

Categories