SHOPPING CART: 0 items £0.00 Checkout

Forcing PDFs to download in WordPress

What a browser does with different files is very much up to the browser, and for the most part that’s exactly the way it should be. It’s up to the user how they deal with downloads, and you should really have users who know to right-click and choose “save file as…” or similar when they’re confronted with a download link.

It’s not always that simple, though. Sometimes your client (or you) is going to want users to download a PDF rather than open it in a browser window (which is the default position for most browsers) – especially if it’s a big PDF. Really big files are going to take an age to open in the browser and may well stop you doing anything else while they load.

Of course, the best way to get around this is, again, simple and gives the user choice: tell the user how big the file is (you can use something along the lines of filesize( get_attached_file( $post->ID ) ); in the appropriate place) and let them pick what to do.

But, again, you might not want to do this. For any number of reasons. So what do you do?

The easiest option is to use a .htaccess file in your uploads folder with rules like these. But from time to time this won’t work: IIS servers, some other kind of server settings. I recently came across this issue on WP Engine hosting, but neither they nor I know completely why just yet. So what’s the other solution?

First up, you’ll need to create a template or loop for the attachment itself, and link to this using get_attachment_link($attachment->ID); (possibly with some kind of checking so this only happens for PDF files) – you can put your loop into your normal template structure using:

if(is_attachment()):
get_template_part('loop', 'download');
endif;

In your normal single.php file, of course.

Now, in your loop-download.php file, you can do a normal loop, but this time include a hidden iFrame where the attachment itself will go. As the source of the iFrame, you’ll want to link back to the attachment itself, but this time add a query string saying something like "forcedownload=true" to the address (we’ll get to that in a second):

<iframe src="<?php the_permalink(); ?>?forcedownload=true" width="1" height="1"></iframe>

(You’ll need to hide this iFrame with your CSS, or you may end up with a little square on the page).

Right – you’re done with the attachment pages now. Let’s move on to header.php.

At the very top of this file (any spaces may well cause a “headers already sent” error) you’ll need to put the following:

<?php
if(is_attachment() && $_GET['forcedownload'] == 'true'):
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="download.pdf"');
readfile(get_post_meta( get_the_ID(), '_wp_attached_file', true ));
endif;
?>

What we’re doing here is first checking that we’re on an attachment page and then checking that the query string is set… if we don’t do this, the download will start when we click the download link, and we won’t get the “if your download doesn’t start…” bit. By loading the attachment template first, then loading it into itself and forcing this version to download, we get to see whatever you’ve put in the attachment template as well as the download dialogue. This could be anything in addition to the “if your download doesn’t start…” text – perhaps a link to related downloads or something like that.

You might need to add some extra checks for different file types if you want to do them in different ways, and always be careful of your images linking to attachment pages… if you do this (and WordPress will do this by default) you may well run into some odd behaviours unless you check the attachment file type before sending those headers.

Let me know if you use this code by leaving me a comment!

Leave a comment

*

From Twitter...

 

Also, Timelords: I have too much to fit in to my life and no time. Help. How do I make an extra week?

Wednesday 22 February