/    Sign up×
Community /Pin to ProfileBookmark

[RESOLVED] Trouble with Headers: Forcing download of images.

Hi guys,

I’m having some difficulties that hopefully some of you would be able to help with.

I’m currently building an extranet for our company so that our retailers can log in to download images, adverts, prices, etc..

Since most of them are, well, stupid, I want to force them to have to download each image with a download box.

Please note that I am running PHP 5 on Apache, on Windows (as a test environment, production server is Linux)

Now, the first thing I implemented was a download basket so that everything could be added to it to be downloaded in bulk at a later date. This is done by zipping the file and then forcing a download using this code.

[CODE]
if (file_exists(“downloads/” . $_GET[‘package’] . “.zip”)) {
header(‘Content-type: application/zip’);
header(‘Content-Disposition: attachment; filename=”‘ . $_GET[‘package’] . ‘.zip”‘);
readfile(“downloads/” . $_GET[‘package’] . “.zip”);
}[/CODE]

There is some checking before this code to make sure that the person has permission to download the file which in turn prevents exploits to download files they shouldn’t be (for example, their user level, if they’re logged in, if the file is in a valid download directory, etc)

This works absolutely perfectly, and it downloads the zip file with everything inside in tact.

Now, using this as a template, I then went on to do individual files. The issue is that the file type would change depending on the file, so using this one as a template I wrote this (and for testing, I made sure it was a gif file):

$g[‘filename’] would be as an example, “images/imagefile.gif” which is a valid file, just as “downloads/” . $_GET[‘package’] . “.zip” was a valid file.

[CODE]
if (file_exists($g[‘filename’])) {
header(“Content-Type: image/gif”);
header(“Content-Disposition: attachment; filename=”” . $g[‘filename’] . “””);
readfile($g[‘filename’]);

}
[/CODE]

This brings up the download dialog box, but the image doesn’t work.
In fact, none of the test images that I tried work, but the filename is valid since it “exists”.

I then looked around at 100s of examples people have done, but I couldn’t find a solution.

Taking some of the code people had done with vaguely similar problems, I added more crap to my code to see if it would work.

[CODE]
if (file_exists($g[‘filename’])) {

$fp=fopen($g[‘filename’], “r”);
if ($fp) {
header(“Cache-Control: public, must-revalidate”);
header(“Content-Type: application/force-download”);
header(“Content-Disposition: attachment; filename=”” . $g[‘filename’] . “””);
header(“Content-Transfer-Encoding: binary”);
header(“Content-Length: ” . (filesize($g[‘filename’])) );
header(“Pragma: no-cache”);
header(“Expires: 0”);
fpassthru($fp);
}
#readfile($g[‘filename’]); // I used the fpassthru method above
}
[/CODE]

Sadly, it didn’t…

So I’ve exhausted most of my options and I can’t find out what is wrong. My original code was pretty much the same as the one I used for the zip download which was successful, except this one isn’t. No headers are sent prior to the download, the dialog box shows up, but the image downloaded from it just plainly doesn’t work.

Help would be greatly appreciated.

to post a comment
PHP

19 Comments(s)

Copy linkTweet thisAlerts:
@SheldonJul 31.2008 — I have always just used some simple headers;
[code=php]
<?php

$filename = "image.jpg";
$path = "./downloads/";
if(is_file($path.$filename)){

header("Content-disposition: 'attachment'; filename='{$path}{$filename}'");
readfile("{$path}{$filename}");

}else{
die("No File");
}

?>[/code]
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — Just tried that approach, it thought that $g['filename'] was a .htm file, and tried to open in explorer unsuccessfully.

[CODE]
$filename = 'common/images/wait.gif';

if (file_exists($filename)) {
header("Content-Disposition: attachment; filename="" . $filename . """);
readfile($filename);
}
[/CODE]


$filename does physically exist, but it tries to save as wait.htm
Copy linkTweet thisAlerts:
@MrCoderJul 31.2008 — "common/images/wait.gif" is not a valid filename.

Try..
[code=php]
header("Content-Disposition: attachment; filename="wait.gif"");
[/code]
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — [code=php]
if (file_exists($filename)) {
header("Content-Disposition: attachment; filename="wait.gif"");
readfile($filename);
}
[/code]


That gives me wait.htm still, despite wait.gif being the filename.

[code=php]
if (file_exists($filename)) {
header('Content-type: image/gif');
header("Content-Disposition: attachment; filename="wait.gif"");
readfile($filename);
}
[/code]


That gives me wait.gif, but the file still doesn't work (original problem).

Any other ideas?
Copy linkTweet thisAlerts:
@MrCoderJul 31.2008 — Try a jpeg image using "image/jpeg" as the content type, also what browser?
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — I've tried it in Opera, IE, Firefox. All the same result...

Just trying jpeg now...

Same with Jpeg.

[code=php]
$filename = 'common/images/review.jpg';

if (file_exists($filename)) {
header('Content-type: image/jpeg');
header("Content-Disposition: attachment; filename="review.jpg"");
readfile($filename);
}
[/code]


Just a dead image...
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — It's a very strange problem, isn't it? The Zip file works not a problem, but this one is just very odd.

They both don't start with / and they both start in the same folder and go from there.

I've tried using document root, and this didn't give me any successes.
Copy linkTweet thisAlerts:
@MrCoderJul 31.2008 — Remove this line and try again.

[code=php]
header("Content-Disposition: attachment; filename="review.jpg"");
[/code]


Also make sure there is no whitespaces being output after the closing PHP tag that would invalidate the image.
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — I'm pretty sure there's no whitespace after the closing tag, but just to be sure I put die(); at the end which would terminate anything.

Same result: Image doesn't download properly.

[code=php]
$filename = 'common/images/review.jpg';

if (file_exists($filename)) {
header('Content-type: image/jpeg');
header('Content-Disposition: attachment; filename="review.jpg"');
readfile($filename);
die();
}
else {
echo "<div class='error'>File could not be found</div>";
}[/code]


Even without die();

Do you want the full page code for both the zip and the image? That way, you can see the differences and any potential flaw that occurs before or after.
Copy linkTweet thisAlerts:
@MrCoderJul 31.2008 — Remove this line and try again.

[code=php]
header("Content-Disposition: attachment; filename="review.jpg"");
[/code]


Also make sure there is no whitespaces being output after the closing PHP tag that would invalidate the image.[/QUOTE]


Did you remove the above line?
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — You mean so it's just

[code=php]
header('Content-type: image/jpeg');
readfile($filename);
[/code]


?

Yes it doesn't output an image.. Only a box saying "Image" inside (in opera) and red x for IE.

However, the file does exist since I can output...

echo "<img src='" . $filename . "' />";

die();

...before the header and view the image perfectly.
Copy linkTweet thisAlerts:
@MrCoderJul 31.2008 — [code=php]
<?php
header("Content-Type: image/jpeg");
readfile("test.jpg");
die();
?>
[/code]


Create a blank file with the above code in it, that works in IE and FireFox as long as test.jpg exists in the same folder as the PHP file and is a valid image.

Not sure if this matters, but in your "Content-Type" you used a lower-case "T" in "type"
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — I don't think it does matter.. I toyed with upper and lower case.

Just done a test. That works pefectly in the root.

I move it up one folder (test/email.jpg) and it works

I move it up two folders (test/test/email.jpg) and it works.

So, in theory this should work on my current file providing there is no output prior...

So, I commented out the whole page, and I put
[code=php]

$filename = 'common/images/review.jpg';
header("Content-type: image/jpeg");
readfile($filename);
die();

[/code]


That returned a dead image, unlike the other one that returned a working image.

So I moved review.jpg into root.
[code=php]

$filename = 'review.jpg';
header("Content-type: image/jpeg");
readfile($filename);
die();

[/code]


Dead.

I moved my other test into the same root directory, and the image still works.

The setup of my system is fairly complicated. Basically index.php in the root checks $_GET['option'] and checks that option with the database to pull out the component file, which calls index.php in another folder. To call other folders, I have to use that same folder ($componentFile) to call this index.php

Between the download page and this one, the code is identical, so there shouldn't be any reason why it would work for one but not the other.

If something has changed, I had better make sure the zip still works. If it doesn't that's probably the reason why.
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — Oh, and index.php suppresses all output with ob_start();

header gets priority doesn't it?
Copy linkTweet thisAlerts:
@MrCoderJul 31.2008 — [code=php]
// $filename = 'review.jpg';
// header("Content-type: image/jpeg");
// readfile($filename);
echo "<pre>".var_export(ob_get_contents(), true)."</pre>";
die();
[/code]


That should give you an empty string?
Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — I removed the header so it was just readfile();

I got this:

<i>
</i>&lt;table class='container'&gt;
&lt;tr&gt;
&lt;td class='header'&gt;&lt;a href='index.php'&gt;&lt;img src='themes/extranet/images/logo.gif' width='900' height='120' border='0' alt='Maver UK' title='Maver UK' /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class='content'&gt;

<i> </i>&lt;!-- Main Content --&gt;

And then random characters which is the image it's opening, then the footer (although footer code doesn't appear on the source, but it displays on the page)


======================

Update


Just did that, I got

<i>
</i>
&lt;pre&gt;'&lt;table class='container'&gt;
&lt;tr&gt;
&lt;td class='header'&gt;&lt;a href='index.php'&gt;&lt;img src='themes/extranet/images/logo.gif' width='900' height='120' border='0' alt='Maver UK' title='Maver UK' /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class='content'&gt;

<i> </i>&lt;!-- Main Content --&gt;

'&lt;/pre&gt;

Copy linkTweet thisAlerts:
@maverukauthorJul 31.2008 — I think this is only going to work for zip files, isn't it?

Because I just tried a zip file instead of jpeg or gif, and it works fine.

I'm sure a gif or jpeg in place on the zip page would throw out that same error.

Any way to suppress all output before readfile()?

Keep in mind that the header.php file is called before this file is loaded, but after the first ob_start (in index.php)
Copy linkTweet thisAlerts:
@MrCoderJul 31.2008 — Throw a ob_clean() before the readfile() and make sure your ob_start() is executed before anything else is outputted.

[code=php]
<?php
ob_start();

echo "THIS WILL NEVER SHOW UP";

ob_clean();

echo "But this will..";
?>
[/code]
Copy linkTweet thisAlerts:
@maverukauthorAug 01.2008 — Thank you so much. That works excellently.

It's all working now with ob_clean(); I guess the header makes no difference to zip files, but it does to everything else.


Now I have to work out the mime type to load the correct Content-Type.. Any suggestions?

I tried mime_content_type but it returns blank.
×

Success!

Help @maveruk spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.28,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @AriseFacilitySolutions09,
tipped: article
amount: 1000 SATS,

tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,
)...