How do I force-download a dynamic image that requires arguments? - php

I have a dynamic image which uses GD to throw in some overlay images/text. This would be dynamicImage.php?firstName=Bob&lastName=Sacamano. I want to be prompted to download that file, so I created a download.php file to act as the middle-man:
//Get the Arguments
$file .= "firstName=".filter_var($_GET['firstName'], FILTER_SANITIZE_STRING);
$file .= "&lastName=".filter_var($_GET['lastName'], FILTER_SANITIZE_STRING);
//get The File Size
$size = intval(sprintf("%u", filesize($file)));
//Header Info to Prompt for Download and name it a .jpg
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-disposition: attachment; filename=dynamicImage.jpg");
header("Content-Length: ".$size);
readfile($file, true);//.$file);
There's 2 problems, first I get this error:
PHP Warning: filesize() [<a href='function.filesize'>function.filesize</a>]: stat failed for dynamicImage.php?firstName=bob&lastName=Sacamano in /www/download.php on line 19
PHP Warning: readfile(dynamicImage.php?firstName=bob&lastName=Sacamano) [<a href='function.readfile'>function.readfile</a>]: failed to open stream: No such file or directory in /www/download.php on line 25
See how it parses the & to & ? But not only that. If I take out the arguments and just leave dynamicImage.php it prompts me to download the raw php file. Is there a way I can make it Run the PHP and then download the generated image?
BTW My dynamicImage.php ends with:
header("Content-Type: image/JPEG");
ImageJpeg ($bg);
imagedestroy($bg);
Fixd. I altered my dynamicImage.php thusly:
if(isset($_GET['download'])){
header('Content-Description: File Transfer');
header('Content-Type: application/octet-image');
header("Content-disposition: attachment; filename=dynamicImage.jpg");
}else{
header("Content-Type: image/JPEG");
}
ImageJpeg ($bg);
imagedestroy($bg);

I'll try to add some meat to my comment in an initial answer.
Trying to download your file through a separate call to another script over http is backwards and over complicating a simple problem.
It would be easier to refactor your original code for dynamicImage.php into a function. Then include that file as a library in download.php and use the function from dynamicImage.php to return your image with Content-disposition headers set.
Or you could add download as a third argument to your dynamicImage.php script and just add the Content-Disposition header to output form dynamicImage.php when that argument is set.
Also see #Novikov's answer.

How is the file called on your hard disk? That's the value you should filesize() on.

You are trying to call a PHP script from within a PHP script through HTTP? Very Rube Goldbergy.
You'll probably want a full path such as http://yoursite/dynamicImage.php, otherwise the PHP file handling functions will treat it as a filesystem call not an HTTP wrapper call.
The PHP manual states that stat isn't supported on HTTP wrappers so you won't be able to do filesize("http://url").
I'd suggest refactoring the code in dynamicImage.php so that for example &action=download will let you download the image as a file rather than a jpeg.

I think you only got 2 solutions :
Generate the picture the first time with your dynamicImage.php and store it into your server as, for example, bob-Sacamano.jpg. But it'll be a hdd space eater if you got a lot of pictures to generate, still, it's a solution.
As Novikov and many other said, you may want to generate and forcing download in the same script. (Maybe with another argument in your get http call like suggested).

Related

Readfile is best solution to download external files?

I need to get a remote file and give it to user without saving it to my server disk (for hiding original URL) and found a lot of posts about download external files with various functions like file_get_contents or readfile. Already I'm using this one:
function startDownload($url){
if($this->url_exists($url))
{
//get filename from url
$name=$this->getFileName($url);
//first flush clear almost output
ob_end_flush();
//final clear
ob_clean();
//set headers
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . $name . "\"");
//send file to client;
readfile($url);
//exit command is important
exit;
}
else JFactory::getApplication()->enqueueMessage(JText::_('URL_NOT_FOUND'), 'error');
}
And that's working but there is a problem! For a file with 200 MB size it takes ~ 10 seconds to start download in client browser. I think it's because readfile first downloads whole file to my server buffer and then give it to user. Is that right?
And is it possible to make it faster? for example download be started before fetch ended or it isn't possible technically?
In fact I don't know that this method is optimised or not. Any technical advice would be appreciated.
Note :
I know that this function should be changed for big files and that's not my concern now.
I consider to buy the external server in the same datacenter to make this download faster.
Target is that [File server] be separate than the file [online shop].
I tested curl method that mentioned by #LawrenceCherone. It worked nicely but when moved it to my project the result was the same as readfile (white screen for a few seconds).
So suspect to readfile() function. Separate my previous code to a single PHP file and result was amazing! Download starts immediately.
So I think my guess wasn't right and problem was not related to readfile function.
After a little search found a minor modification. I added below line :
while (ob_get_level()) ob_end_clean();
before the :
readfile($url);
And now download starts before whole file fetched in my server.

PDF Download gives corrupt PDF php

I am saving a pdf file, and then attempting to download it using php.
The script seemed to work fine, but all of the sudden not anymore.
Can anybody see what I am missing?
PS: the file I am downloading is only 4.3kb big, so I assume that would be because it is not downloading at all. The actual file size should be bigger than this.
$pdf->output(ROOTDIR.'/modules/addons/statement_generator/reports/statement.pdf');
if($action=='print'){
$file_name = 'statement.pdf';
$file_url = "http://".$_SERVER['SERVER_NAME']."/modules/addons/statement_generator/reports/" . $file_name;
header('Content-Type: application/pdf');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"".$file_name."\"");
readfile($file_url);
exit;
}
The $pdf->output() call will already send the PDF to the client. The file will not be saved to your local folder (Didn't you checked at least this?) because you have to pass "F" as the snd parameter.
After that you try to read from an URL (!!!!) that does not exists and which maybe return a nicely styled 404 html response. Two issues here:
Why are you using http when you have the local path used some lines above? Use the local path only!
The content returned by the URL is append to the already send PDF which ends in a document mixed of PDF and HTML (the 404 response) -> corrupted PDF
Conclusion: Use "F" as the 2nd parameter and use the same path for both writing and reading and not a mix of local path and URL.

How to download a file without an extension in PHP

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)));

PHP Download not working with path coming from DB

I have a simple form that, when posted back, calls a function to initiate a download. The path and file name are pulled from the database then I'm using headers to start the download. My code for the download is:
//START DOWNLOAD
header('Content-type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="'.$FILE_PATH.$FILE_NAME.'"');
header("Content-Transfer-Encoding: binary");
header("Connection: close")
In the example above, the $FILE_PATH variable is /downloads/software/ and the $FILE_NAME variable is client-installer.exe. So, what I would expect is a file called client-installer.exe (approximately 70MB) to be downloaded to the client. Instead, I get a file called _downloads_software_client-installer.exe and approximately 10KB.
I thought maybe I needed to urlencode the file path/name but that didn't fix the issue either. So I'm left thinking perhaps I have something wrong with the header but can't seem to find it.
Thank you!
The filename header just denotes what the file should be called. It must contain only a filename, not a path. The internal path on the server's hard disk is irrelevant and of no interest to the client. Your server will have to output the actual file data in the response, the client can't take it from the server given the path.
See readfile.

Using php to force download a pdf

Im trying to get a website to have a button that forces a download of a pdf.
Heres the html of the button:
<a href=scripts/download.php>
<input type="image" src="images/download.gif" alt="Submit button"/>
</a>
And the php script so far:
<?php
header('Content-Type: application/pdf');
header('Content-disposition: attachment;filename=documents/ECM_IT_ResumeDownload.pdf');
readfile('documents/ECM_IT_ResumeDownload.pdf');
?>
This seems to download the file fine but when I go to open it i get this error:
"Adobe Reader could not open 'documents_ECM_IT_ResumeDownload.pdf' because it is either not a supported file type or because the file has been damaged (for example, it was sent as an email attachment and wasn't correctly decoded)."
Any help would be greatly appreciated.
EDIT
Opened the pdf in a text editor and got this message:
"
Warning: readfile(documents/ECM_IT_ResumeDownload.pdf) [function.readfile]: failed to open stream: No such file or directory in html/scripts/download.php on line 4
"
The document is definitely there though. in html/documents/ECM_IT_ResumeDownload.pdf
$file_url = www.example.com/pdffolder/$pdfname;
header('Content-Type: application/pdf');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=".$pdfname);
readfile($file_url);
Try removing the path to the file and just leave the file name in the content:
header('Content-Type: application/pdf');
header('Content-disposition: attachment; filename=ECM_IT_ResumeDownload.pdf');
Have you tried getting rid of the closing PHP tag (the ?>) at the end? It will treat the page as a pure PHP page, removing any possible new lines that might accidentally get appended to the end of the output. This helped me when I was dynamically creating excel files for download, and they were downloading as corrupted. Check out this page for more information:
http://www.php.net/manual/en/language.basic-syntax.phptags.php
From your edited question, it seems like PHP is unable to find the file. Try using an absolute path to the file like so: "c:\blah\de\blah\bloo.pdf" or "c:/blah/de/blah/bloo.pdf". If one of those paths works and downloads correctly, your relative path is incorrect in some way.
I always use Gowon Patterson's download script, it also has hotlink protection:
http://by.gowondesigns.com/getfile/
By the way, a bit late, but to identify the problem properly here:
Your download script is at scripts/download.php and the file you want to download is at documents/[...].pdf.
Therefore, your readfile() function should be traversing to the parent directory (outside of scripts/), e.g. readfile('../documents/[...].pdf');.

Categories