Home » Wordpress » Protecting / serving a file via readfile(), force download?

Protecting / serving a file via readfile(), force download?

Posted by: admin November 30, 2017 Leave a comment

Questions:

I’m trying to make a simple script that does two things:

  1. Serves up a file and hide’s it’s destination
  2. Has a download counter

Now, I’m doing this in the wordpress environment, but this question isn’t completely wordpress-related so I figured I would ask here.

Basically, the way I have it set up, currently, is I have a link that when you click it sets a $_['GET'] which is then checked if is set. If it is set, the download file is served.

the link: <a href="http://localhost:8888/fresh/?dl_id=01">Click here!</a>

the $_['GET'] code: http://pastebin.com/93nD43gA

There is a bit of wordpress jargon in the code, but basically it’s checking a download count user_meta and if it’s > 0, serveFile() is called.

The main problem I’m having here is, if I click the link, readfile() loads the actual file contents INTO the window (garbled text). If I add a target=_blank to the <a> it opens a new browser window and loads the contents INTO the window.

This approach seemed to work perfectly fine when I was doing it as stand-alone php files. My main issue is that I need to keep the wordpress space so I can call functions, etc. associated with it.

I have tried using the $_['GET'] on both the self page, another page with a custom template (the code in the pastebin above), and as a stand-alone php file. Both the first two options load the file INTO the window. The third doesn’t preserve wordpress functions, even if I include blog-header.php.

Can anyone point me in to the right direction of how to get the file to force download and not load INTO the window?

Answers:

You need to set the appropriate header for whatever the file type is. For example, if readfile always serves, PDFs, it should be done like this:

// disable browser caching -- the server may be doing this on its own
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

header('Content-Type: application/pdf');
//forces a download
header("Content-Type: application/force-download");
header('Content-Disposition: attachment; filename=filename.pdf');
readfile($file);

Keep in mind that header only works if you have not sent any data in the request at all including whitespace.

Questions:
Answers:

The ‘garbled’ text is what you want however besides that you have to set a mime. This can be accomplished by simply setting a header, e.g. header("Content-Type: image/png");

If the file mimes will vary (e.g. pdf, doc, png, etc) you should look into finfo extension. With it you can get the full and correct mime of the file

<?php
$finfo = new \finfo(FILEINFO_MIME);
$mime = $finfo->file('path/to/file', FILEINFO_MIME_TYPE);
header("Content-Type: $mime");

As noted – headers can be set only if no write to output has been done (no echo’s, print, etc. Output buffering could help you here).