How to force downloading a file with Drupal 7
New quick post to share a feature.
To force to download a file, as usual with Drupal, there's a module for that. In this case there are even two: Download file and File force. But you may be reticent to install a module for such a basic feature! So here is the solution in a few lines of code:
Here is a snippet based on File API. It is more secure than the second one but it will not work on files not handled by Drupal (whatever file download method you're using) : thus I recommend it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php /** * Implementation of hook_menu() */ function mymodule_menu() { $items['download/%file'] = array( 'page callback' => 'mymodule_download_file', 'access arguments' => array('administer site configuration'), 'type' => MENU_CALLBACK, ); return $items; } /** * Page callback for forcing a file to download */ function mymodule_download_file($file) { if($file) { file_transfer($file->uri, array('Content-disposition' => 'attachment; filename='.$file->filename)); } else { return drupal_access_denied(); } } |
You can download the file with a link like http://mysite.com/download/123 where 123 is the file id (fid) of the file being downloaded.
This second snippet may present a security issue! It is recommended to use it as a last resort when files are not handled by Drupal. To increase security, this feature will be restricted to administrators, and filtered on file extensions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?php /** * Implementation of hook_menu() */ function mymodule_menu() { $items['download'] = array( 'page callback' => 'mymodule_download_file', 'access arguments' => array('administer site configuration'), 'type' => MENU_CALLBACK, ); return $items; } /** * Page callback for forcing a file to download */ function mymodule_download_file() { if (!isset($_GET['file']) || !file_exists($_GET['file'])) { return drupal_not_found(); } $filepath = $_GET['file']; $realpath = realpath($path); $filename = basename($filepath); $extension = pathinfo($filepath, PATHINFO_EXTENSION); // Check extension and restrict to files in DRUPAL_ROOT if(in_array($extension, array('jpg', 'png', 'gif', 'mp4')) && substr($path, 0, strlen(DRUPAL_ROOT)) === DRUPAL_ROOT) { drupal_add_http_header('Content-disposition', 'attachment; filename=' . $filename); readfile($filepath); } else { return drupal_access_denied(); } } |
Then you can create your file link as in http://mysite.com/download?file=sites/default/files/myimage.png
It's only working for the public file system handling but it is the most famous one.