27

Is there a way to make an image a download once you click on it (without right-click save image as)?

I'm using a small Javascript function to call the download page:

<a href="#" 
   onclick="window.open('download.php?file=test.jpg', 'download', 'status=0');"
>Click to download</a>

In the download.php page I have something like:

$file = $_GET['file'];
header('Content-Description: File Transfer');
header("Content-type: image/jpg");
header("Content-disposition: attachment; filename= ".$file."");
readfile($file);

But it doesn't work. What am I doing wrong?
Thanks in advance!

hakre
  • 193,403
  • 52
  • 435
  • 836
Jay Wit
  • 2,987
  • 7
  • 32
  • 34
  • 14
    Note that, with this code, anyone can download any possible file from your server : just specific the path to any file in the URL, and voila, you get its content... – Pascal MARTIN Apr 13 '11 at 12:13
  • 2
    @Pascal; Don't worry, it's secured, this is just a small part of the code – Jay Wit Apr 13 '11 at 12:16
  • Maybe there are PHP error in your download.php but In the above code is correct and it is working. I have tested :) You can test by writing exit before header. – mahadeb Apr 13 '11 at 12:21

11 Answers11

23

Use application/octet-stream instead of image/jpg:

If [the Content-Disposition] header is used in a response with the application/octet-stream content-type, the implied suggestion is that the user agent should not display the response, but directly enter a `save response as...' dialog.
RFC 2616 – 19.5.1 Content-Disposition

Community
  • 1
  • 1
Gumbo
  • 643,351
  • 109
  • 780
  • 844
  • Its saved as a jpg, and the filename is correct. But when I open the file it's damaged? So it seems like it isn't actualy a jpeg – Jay Wit Apr 13 '11 at 12:22
  • @Jay Wit: You probably send more data than just the file’s contents (like some whitespace etc.). – Gumbo Apr 13 '11 at 12:24
  • @Gumbo; Some filenames/directories contain whitespaces yes, but is that a problem? – Jay Wit Apr 13 '11 at 12:26
  • @Jay Wit: I meant that you probably not just send the file’s contents but also some other data that was put out earlier (like whitespace, HTML markup, etc.). – Gumbo Apr 13 '11 at 12:28
  • @Gumbo; Oh no, even if I change `$file = "test.jpg"` it still downloads a damaged jpeg – Jay Wit Apr 13 '11 at 12:32
  • 1
    @Jay Wit: It’s `readfile` that prints the file’s contents. Just use a hex editor to see the actual data that has been sent. – Gumbo Apr 13 '11 at 12:34
  • @Gumbo; Hex editor? How does it work? And any other idea on what might went wrong? – Jay Wit Apr 13 '11 at 12:49
  • @Jay Wit: A hex editor lets you see and edit the value of each byte in hexadecimal notation. Then you can see what actually has been sent. – Gumbo Apr 13 '11 at 12:56
  • @Gumbo; Yup, now I see what goes wrong. The imagefile contains the text of the download.php page. So it downloads the page, and converts it to a jpeg with the filename. Any idea on how to fix this? – Jay Wit Apr 13 '11 at 13:01
  • 1
    @Jay Wit: Just don’t do any output except with `readfile` or buffer it so that it doesn’t get sent to the client. – Gumbo Apr 13 '11 at 13:08
  • @Gumbo; I removed al the text of the page, but the image file still contain's one whitespace(enter), if I remove that one it works perfect. But I don't know what generates the white space... – Jay Wit Apr 13 '11 at 13:10
  • @Gumbo; Nevermind, the whitespace was created by the ``. Removed the tab and it worked perfect, thanks! – Jay Wit Apr 13 '11 at 13:12
19

I think you forgot to add Path on the header

if(isset($_GET['file'])){
    //Please give the Path like this
    $file = 'images/'.$_GET['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;
    }
}
Kaartikeyan R
  • 259
  • 2
  • 2
7

Or you can use .htaccess file for all your image files. In case you want to force the browser to download all your images (f.e. from a table list):

RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} ^download$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .(jpe?g|gif|png)$ index.php?file=noFoundFilePage [L,NC]
RewriteCond %{QUERY_STRING} ^download$
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .(jpe?g|gif|png)$ - [L,NC,T=application/octet-stream] 

This looks for image files a tries to force download them into the browser. The -f RewriteConds also checks that the file exsist.. The last rule ensures that download is used only for certain file types.

Bery
  • 1,094
  • 1
  • 10
  • 23
  • Thanks :-) I like it the most too ;-) It is clean and without using PHP as a stream.. Just for chosen dir. Alas it does not suit every use case.. – Bery Dec 05 '11 at 14:33
  • To get this to work for JPGs in IE 9, I had to add `Header set Content-Disposition attachment` to the Apache configuration as well. – Richard Ayotte Mar 01 '12 at 14:55
4

This worked for me

header("Pragma: public");
header('Content-disposition: attachment; filename='.$title);
header("Content-type: ".mime_content_type($sample_file));
header('Content-Transfer-Encoding: binary');
ob_clean(); 
flush(); 
readfile($sample_file);

I also added the following to .htaccess

SetEnvIf Request_URI "\.jpg$" requested_jpg=jpg
Header add Content-Disposition "attachment" env=requested_jpg

Not sure if that helps?

Michael Stott
  • 43
  • 1
  • 6
2

If you're on an apache server this is really simple.

  1. Create a file called .htaccess in the directory in which your files exist. For me it was assets/.htaccess.
  2. Add the following to the file AddType application/octet-stream .jpg. You can add a new line for each file extension that you need. E.g. AddType application/octet-stream .pdf
  3. Save your file
  4. Clear your browser cache
  5. Refresh your browser and enjoy!
kdev
  • 2,687
  • 1
  • 18
  • 17
1

This is a button you can click on in internet explorer to download pic

<html>
<head>
<title>Your title</title>
</head>
<body>
<form><input type="button" value="Click Me" onClick="window.location.href='image.jpg'"></form>
</body>
</html>
Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
anonomus
  • 19
  • 1
1

Once you’ve added the Content-Disposition: attachment header, you should be able to use a normal link:

<a href="download.php?file=test.jpg">Click to download</a>

What browsers have you tried this with?

Martijn
  • 13,225
  • 3
  • 48
  • 58
  • It keeps downloading the page itself, am I doing something wrong? I've tried this in Chrome and Firefox. – Jay Wit Apr 13 '11 at 12:20
  • @JayWit: Does it download the PHP code? If so, your web server is configured to _not_ run PHP scripts in that directory. What server are you using? If it doesn’t download the PHP code, then what _does_ it download? – Martijn Apr 13 '11 at 12:50
  • It downloads a damaged jpeg file, I think an empty file with just the filetype jpg. Not sure though. Any ideas? – Jay Wit Apr 13 '11 at 12:57
  • Open that downloaded .jpg file using Notepad.exe; what do you see then? – Martijn Apr 13 '11 at 13:01
0

Browsers recognize jpg URL's and hand them over just like a .htm, so I don't think you can force it on the user-end (not positive on that, though).

Read this

http://apptools.com/phptools/force-download.php

xkeshav
  • 53,360
  • 44
  • 177
  • 245
0

Try to change this:

header("Content-disposition: attachment; filename= ".$file."");

To:

header("Content-disposition: attachment; filename= ".$file);

If the above doesn't work to you, here a function to force a file to be downloadable:

    <?php
function downloadFile($file, $type)
{
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=$file");
header("Content-Type: Content-type: $type");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($file));
readfile($file);
}

    downloadFile("sd.jpg", "image/jpg");
    ?>
SIFE
  • 5,567
  • 7
  • 32
  • 46
  • 1
    I updated my code, its work for me now, just make sure you are pointing to existing file. – SIFE Apr 13 '11 at 12:39
  • Still doesn't work, I have an image `sd.jpg` right next to my download.php file and it's still a damaged jpeg. Could this problem be because I'm on my localhost? – Jay Wit Apr 13 '11 at 12:55
  • I am also in localhost, try to clear browser cache, also try another image. – SIFE Apr 13 '11 at 13:02
0

See if this helps:

Webkit and Excel file(PHPexcel)

Community
  • 1
  • 1
Nathan Loding
  • 3,185
  • 2
  • 37
  • 43
0

Here's a way to force all files in a certain directory to be prompted for a download.

Requirement

  1. Apache
  2. mod_headers enabled

In your .htaccess or Apache configuration put the following.

<Directory /path/to/downloadable/files>
    Header set Content-Disposition attachment
    Header set Content-Type application/octet-stream
</Directory>
Richard Ayotte
  • 5,021
  • 1
  • 36
  • 34