PHP force download of remote file without reading into memory - php

Is it possible to force a download of remote file in PHP without reading it into memory? I know fpassthru(), readfile(), file_get_contents() all reads the files into memory before outputting it into the browser.
Here's my code:
if($url = getRemoteFileURL($file_id))
{
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="abc.zip"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Pragma: no-cache');
readfile($url); // is there a better function ?
}
I don't want to do header("Location: ") because that would reveal the URL

If you do a header("Location: ..."); to a downloaded file, the URL isn't actually revealed much, if at all.
Anyway, readfile is probably your best option. I would assume, given that it writes straight to the output, that PHP proceeds by reading in a part of the file, then outputting it, then reading the next part, etc., overall using very little memory.

Related

Redirect to a file outside of the domain Root

I want to give a file to a person based on the users rank so I need to hide the files in a directory which is hidden.
I'm using Plesk and my structure looks like this:
api (reachable from https://api.pexlab.net)
cloud (reachable from https://cloud.pexlab.net)
default (reachable from https://pexlab.net)
error_docs
hidden (not reachable)
My PHP script is located in:
api/hub/Test.php (reachable from https://api.pexlab.net/hub/Test.php)
I have tried this:
# In Test.php
downloadFile("../../hidden/hub/download/assets/user/main.fxml");
# Function:
function downloadFile($file) {
if(file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
}
This method works but I want to redirect to this file (show it) and NOT download it. So I have tried using this:
header("Location: ../../hidden/hub/download/assets/user/main.fxml");
But this tried to redirect to https://api.pexlab.net/hidden/hub/download/assets/user/main.fxml which is invalid.
The only difference between "viewing" and "downloading" a file is what the browser does with the data. Ultimately, that's in the hands of the user, but the server can indicate what it would like to happen.
I suspect you have copied these lines without really understanding what they do:
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
These are all instructions to the browser telling it what to do with the data you send.
The Content-Disposition header is used to tell the browser "rather than trying to display this content straight away, suggest the user saves it in a file, with this name". To use the browser's default behaviour, you would simply leave off this header, or give it the value inline.
The Content-Type header tells the browser what type of file this is. The value application/octet-stream means "just a bunch of bytes, don't try to interpret them in any way". Obviously, that would be no good for viewing a file in the browser, so you should send an appropriate "MIME type", like text/html or image/jpeg, as appropriate for the file you're serving. I'm guessing "FXML" is an XML-based format, so text/xml might be appropriate; or if it's human readable and you just want it displayed without any formatting, use text/plain.

PHP force downloading

How do I make php force download a file. I have a file named song1, which is a song, in the file songs. so from the page I am at it is song/song1. How do I make php download the file as soon as the php is ran?
You have to send out some HTTP headers:
header('Content-disposition:attachment; filename=song.mp3;');
Then you have to pull the song data with for example file_get_contents(). And finally use a die() or exit() to avoid adding extra data.
Side note: The above code will not work if you've already sent out HTTP headers (wrote out some whitespace characters, etc), so put it directly after <?php if you can.
Try below code
$file='song1.mp3';
if (file_exists('song/'.$file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename('song/'.$file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize('song/'.$file));
ob_clean();
flush();
readfile('song/'.$file);
}
It will directly download file.

PDF rendered as text

I'm using PHP to generate a PDF via browser for my web-application. Recently, the client changed the webserver to Apache and now this feature is no longer working. Instead of generating the PDF, the browser is showing the PDF as text, just as it was ignoring Content-Type (that is set to "application/pdf"). In fact, I successfully simulated the problem by commenting the line setting the Content-Type in the source code.
I need ideas about where and what to look for, any help will be very welcome :)
Since you generate PDF files through PHP, you can try to add these headers:
$file_default = 'default filename you want to appear when the user downloads.pdf';
$file_location = '/path/to/file.pdf';
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.$file_default);
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($file_location));
ob_clean();
flush();
readfile($file_location);
I guess you'd have to force apache to download PDF content rather than showing:
check this: http://www.thingy-ma-jig.co.uk/blog/06-08-2007/force-a-pdf-to-download

Why is this PHP download script failing only on Android

I have a simple script that allows someone to download a movie file to their device. The code works well on everything I've tested except for Andriod. The Android device butchers the name and the file extension. It might call the file 2.qt or .bin. Why is this failing?
<?php
if(isset($_GET['filename'])) {
header('Content-Description: File Transfer');
header('Content-Type: movie/quicktime');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize("$file"));
ob_clean();
flush();
readfile("$file");
} else {
echo "Link: <a href='test.php?filename=test.mov'>Download Video</a>";
}
?>
Because Android doesn't natively support Quicktime, an Apple technology. It's also very possible that the client used to download this app isn't respecting the filename set on the http envelope as the name it uses to write the file to the filesystem, as there is nothing forcing it to.
The problem was that the filenames had spaces, and Android doesn't like that. I removed the spaces and everything is fine.

php image file download by code

This concerns downloading files with PHP
My php version is 5.3.5 and my apache is 2.2.17
I am trying to dowload files (pdf,jpg,tiff) that I have uploaded in my server, and they download with the same size and type but I can not see them. I am guessing they are not copied right. But when I open the original uploaded ones they work just fine.
I have seen almost all the questions that appeared as suggested and none of them answered the question, te only similar one is this, but still doesnt answer my question.
to download I am using this code
header("Content-type: application/force-download");
header('Content-Disposition: inline; filename="' . $dir . '"');
header("Content-Transfer-Encoding: Binary");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Content-length: ".filesize($dir));
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $file . '"');
readfile("$dir");
where $dir="62756d616769636e63/646973736572746174/ehddggh/1.JPG"
and $file="1.JPG"
can anyone give me a hint on what I am doing wrong, or give me a better solution to download files?
This smells like you are getting extra (spurious) content in your downloaded files.
Make sure you have no BOM headers, spaces, or anything else before your PHP open tags in your files; also, that you have no trailing whitespace or any other data after the closing PHP tags (if you close your PHP tags).
Also, clean up your code a bit: why multiple Content-Type headers? Why multiple Content-Disposition headers?
readfile($dir); // without the quotes?
Also, make sure that $dir actually exists
is_file($dir) or file_exists($dir)
Thank you all for the answers. I was calling the download as a function in a file with other functions in it so in the end I had to write a script alone, apart from other files. My problem was that I needed it to be safe and to only download a file if it belonged to the user and the user was logged in, so I send all the data I need, ciphered and inside the script I use a series of things to see if the owner is really the logged user. So if anyone wants to know this is the code I used and works perfectly.
<?php
session_start();
$a=$_GET['a'];
$parts=explode("-",$a);
$tres=$parts[0];
$nombre=$partes[1];
$dbcodletra=substr($tres,2);
if($dbcod!=$_SESSION["username"])$boolt=0;
if($ext==1) $extl=".jpg";
if($ext==2) $extl=".jpeg";
if($ext==3) $extl=".tif";
if($ext==4) $extl=".tiff";
if($ext==5) $extl=".pdf";
if($ext==6) $extl=".doc";
if($ext==7) $extl=".docx";
if($tipoproy==1) $dir="rute/".$dbcodletra."/".$nombre.$extl;
if($tipoproy==2) $dir="rute/".$dbcodletra."/".$nombre.$extl;
if($tipoproy==3) $dir="rute/".$dbcodletra."/".$nombre.$extl;
if($tipoproy==4) $dir="rute/".$dbcodletra."/".$nombre.$extl;
if (file_exists($dir) && $boolt) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($dir));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($dir));
ob_clean();
flush();
readfile($dir);
exit;
}else echo "<meta http-equiv=\"Refresh\" content=\"0;url=misdocumentos.php\">";
?>
Paying it forward on a two-year old question...
I had a similar issue with corrupt downloads that could not be opened (when right-click & save-as worked perfectly). After reading #Jon's answer, I figured he was on to something. If you look at the docs for readfile (linked below), you will see an ob_clean(), a flush(), and an exit in their example. All of those will minimize leakage of extra character data in the response. I just copied their Example #1 and my problem was solved.
http://php.net/readfile
Your headers look messy, try just doing this:
header('Pragma: public');
header('Cache-Control: public, no-cache');
header('Content-Type: application/octet-stream');
header('Content-Length: ' . filesize($dir));
header('Content-Disposition: attachment; filename="' . basename($dir) . '"');
header('Content-Transfer-Encoding: binary');
readfile($dir);

Categories