diff --git a/file_entity.api.php b/file_entity.api.php
index ca6d0db..cfa7f9f 100644
--- a/file_entity.api.php
+++ b/file_entity.api.php
@@ -186,6 +186,18 @@ function hook_query_file_entity_access_alter(QueryAlterableInterface $query) {
 }
 
 /**
+ * Alter file download headers.
+ *
+ * @param $headers
+ *   Array of download headers.
+ */
+function hook_file_download_headers_alter(&$headers) {
+  // Instead of being powered by PHP, tell the world this resource was powered
+  // by your custom module!
+  $headers['X-Powered-By'] = 'My Module';
+}
+
+/**
  * Decides which file type (bundle) should be assigned to a file entity.
  *
  * @param $file
diff --git a/file_entity.module b/file_entity.module
index e674ae8..7e14f7a 100644
--- a/file_entity.module
+++ b/file_entity.module
@@ -171,6 +171,15 @@ function file_entity_menu() {
     'context' => MENU_CONTEXT_PAGE,
     'file' => 'file_entity.pages.inc',
   );
+  $items['file/%file/download'] = array(
+    'title' => 'Download',
+    'page callback' => 'file_entity_download_page',
+    'page arguments' => array(1),
+    'access callback' => 'file_entity_access',
+    'access arguments' => array('download', 1),
+    'file' => 'file_entity.pages.inc',
+    'type' => MENU_CALLBACK,
+  );
   $items['file/%file/edit'] = array(
     'title' => 'Edit',
     'page callback' => 'drupal_get_form',
@@ -356,6 +365,12 @@ function file_entity_permission() {
     'create files' => array(
       'title' => t('Add and upload files'),
     ),
+    'download own files' => array(
+      'title' => t('Download own files'),
+    ),
+    'download any files' => array(
+      'title' => t('Download any files'),
+    ),
     'edit own files' => array(
       'title' => t('Edit own files'),
     ),
@@ -1107,6 +1122,7 @@ function file_entity_ctools_plugin_api($owner, $api) {
  * @param $op
  *   The operation to be performed on the file. Possible values are:
  *   - "view"
+ *   - "download"
  *   - "update"
  *   - "delete"
  *   - "create"
@@ -1123,7 +1139,7 @@ function file_entity_ctools_plugin_api($owner, $api) {
 function file_entity_access($op, $file = NULL, $account = NULL) {
   $rights = &drupal_static(__FUNCTION__, array());
 
-  if (!$file && !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) {
+  if (!$file && !in_array($op, array('view', 'download', 'update', 'delete', 'create'), TRUE)) {
     // If there was no file to check against, or the $op was not one of the
     // supported ones, we return access denied.
     return FALSE;
@@ -1207,6 +1223,12 @@ function file_entity_file_entity_access($op, $file, $account) {
     }
   }
 
+  if ($op == 'download') {
+    if (user_access('download any files', $account) || (is_object($file) && user_access('download own files', $account) && ($account->uid == $file->uid))) {
+      return FILE_ENTITY_ACCESS_ALLOW;
+    }
+  }
+
   if ($op == 'update') {
     if (user_access('edit any files', $account) || (is_object($file) && user_access('edit own files', $account) && ($account->uid == $file->uid))) {
       return FILE_ENTITY_ACCESS_ALLOW;
diff --git a/file_entity.pages.inc b/file_entity.pages.inc
index b9afc7b..699a569 100644
--- a/file_entity.pages.inc
+++ b/file_entity.pages.inc
@@ -32,6 +32,27 @@ function file_entity_view_page($file) {
 }
 
 /**
+ * Menu callback; download a single file entity.
+ */
+function file_entity_download_page($file) {
+  $headers = array(
+    'Content-Type' => 'force-download',
+    'Content-Disposition' => 'attachment; filename="' . $file->filename . '"',
+    'Content-Length' => $file->filesize,
+    'Content-Transfer-Encoding' => 'binary',
+    'Pragma' => 'no-cache',
+    'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
+    'Expires' => '0',
+    'Accept-Ranges' => 'bytes',
+  );
+
+  // Let other modules alter the download headers.
+  drupal_alter('file_download_headers', $headers);
+
+  file_transfer($file->uri, $headers);
+}
+
+/**
  * Form callback for adding a file via an upload form.
  *
  * This is a multi step form which has 1-3 pages:
