diff --git a/includes/file.inc b/includes/file.inc
index 0ec69b7..41ed6da 100644
--- a/includes/file.inc
+++ b/includes/file.inc
@@ -1952,6 +1952,8 @@ function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EX
  *   An array of HTTP headers to send along with file.
  */
 function file_transfer($uri, $headers) {
+  module_invoke_all('file_transfer', $uri, $headers);
+
   if (ob_get_level()) {
     ob_end_clean();
   }
diff --git a/modules/simpletest/tests/file.test b/modules/simpletest/tests/file.test
index 20dd273..0afdc5e 100644
--- a/modules/simpletest/tests/file.test
+++ b/modules/simpletest/tests/file.test
@@ -2332,7 +2332,7 @@ class FileSaveDataTest extends FileHookTestCase {
 /**
  * Tests for download/file transfer functions.
  */
-class FileDownloadTest extends FileTestCase {
+class FileDownloadTest extends FileHookTestCase {
   public static function getInfo() {
     return array(
       'name' => 'File download',
@@ -2388,8 +2388,9 @@ class FileDownloadTest extends FileTestCase {
     $this->assertEqual($headers['x-foo'], 'Bar', 'Found header set by file_test module on private download.');
     $this->assertResponse(200, 'Correctly allowed access to a file when file_test provides headers.');
 
-    // Test that the file transferred correctly.
+    // Test that the file transferred correctly and hooks were invoked.
     $this->assertEqual($contents, $this->content, 'Contents of the file are correct.');
+    $this->assertFileHooksCalled(array('load', 'download', 'transfer'));
 
     // Deny access to all downloads via a -1 header.
     file_test_set_return('download', -1);
diff --git a/modules/simpletest/tests/file_test.module b/modules/simpletest/tests/file_test.module
index 1b11316..ed7acda 100644
--- a/modules/simpletest/tests/file_test.module
+++ b/modules/simpletest/tests/file_test.module
@@ -152,6 +152,7 @@ function file_test_reset() {
     'copy' => array(),
     'move' => array(),
     'delete' => array(),
+    'transfer' => array(),
   );
   variable_set('file_test_results', $results);
 
@@ -169,7 +170,7 @@ function file_test_reset() {
  *
  * @param $op
  *   One of the hook_file_* operations: 'load', 'validate', 'download',
- *   'insert', 'update', 'copy', 'move', 'delete'.
+ *   'insert', 'update', 'copy', 'move', 'delete', 'transfer'.
  *
  * @return
  *   Array of the parameters passed to each call.
@@ -187,8 +188,8 @@ function file_test_get_calls($op) {
  *
  * @return
  *   An array keyed by hook name ('load', 'validate', 'download', 'insert',
- *   'update', 'copy', 'move', 'delete') with values being arrays of parameters
- *   passed to each call.
+ *   'update', 'copy', 'move', 'delete', 'transfer') with values being arrays
+ *   of parameters passed to each call.
  */
 function file_test_get_all_calls() {
   return variable_get('file_test_results', array());
@@ -199,7 +200,7 @@ function file_test_get_all_calls() {
  *
  * @param $op
  *   One of the hook_file_* operations: 'load', 'validate', 'download',
- *   'insert', 'update', 'copy', 'move', 'delete'.
+ *   'insert', 'update', 'copy', 'move', 'delete', 'transfer'.
  * @param $args
  *   Values passed to hook.
  *
@@ -310,6 +311,13 @@ function file_test_file_delete($file) {
 }
 
 /**
+ * Implements hook_file_transfer().
+ */
+function file_test_file_transfer($uri, array $headers) {
+  _file_test_log_call('transfer', array($uri, $headers));
+}
+
+/**
  * Implements hook_file_url_alter().
  */
 function file_test_file_url_alter(&$uri) {
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index f54078b..337af81 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -2901,6 +2901,21 @@ function hook_file_delete($file) {
 }
 
 /**
+ * Respond to a file being transferred.
+ *
+ * @param $uri
+ *   The URI of the file being sent.
+ * @param $headers
+ *   An array of HTTP headers being sent along with file.
+ */
+function hook_file_transfer($uri, array $headers) {
+  // Check to see if this is a download and not being displayed inline.
+  if (isset($headers['Content-Disposition']) && strpos($headers['Content-Disposition'], 'attachment') === 0) {
+    watchdog('file', 'The file %uri is being downloaded.', array('%uri' => $uri));
+  }
+}
+
+/**
  * Control access to private file downloads and specify HTTP headers.
  *
  * This hook allows modules enforce permissions on file downloads when the
