0

I stored the names of some users in one table and they should be able to download certain files from private folder(not other users). As i see i have to use hook_file_download

In the page of that hook it is said:

If the user does not have permission to access the file, return -1. If the user has permission, return an array with the appropriate headers.

I don't understand which array should be returned exactly! my file in private folder is mp4 file and user has to see save page.
some users file_create_url() but i don't know how to use it inside hook_file_download. can u give me one example code?

thanks in advanced for your help

edit: I found this code here:

  if ($can_access_file) {
    $headers = file_get_content_headers($file);
    return $headers;
  }  

I don't know exactly how to provide with $file! here is file_get_content_headers help

4 Answers 4

1

What field type serve the link for the download ? Maybe the module behind this type already implement this hook ?
For example, File module or Image module already implement this hook_file_download.

In case of File module, it allow you to alter the permission to access to the file.

// Allow other modules to alter the returned grants/denies.
    drupal_alter('file_download_access', $grants, $field_item, $entity_type, $entity);

You just have to implement the hook_file_download_access_alter. By the way, there is this other hook (hook_file_download_acces) you can may be try to hook.

Finally, if no one of this hooks can help you, so may be you have to use this hook_file_download. But I don't really know what is expecting as headers, but you can look at the hook in the Image module.

There, it get headers from others modules (may be you can just use this trick) :

// Check the permissions of the original to grant access to this image.
  $headers = module_invoke_all('file_download', $original_uri);

And just after that, it make its own headers :

return array(

      // Send headers describing the image's size, and MIME-type...
      'Content-Type' => $info['mime_type'],
      'Content-Length' => $info['file_size'],
      // By not explicitly setting them here, this uses normal Drupal // Expires, Cache-Control and ETag headers to prevent proxy or // browser caching of private images.
    );

Hoping that help :)

1
  • thanks man. i edited my first post. can u take a look at that and answer me?
    – PersianMan
    Mar 31, 2014 at 8:33
1

All is in the code you have found :) Just before the :

if ($can_access_file) { }

You have this code :

$files = file_load_multiple(array(), array('uri' => $uri));
if (count($files)) {
  foreach ($files as $item) {
    // Since some database servers sometimes use a case-insensitive comparison
    // by default, double check that the filename is an exact match.

    if ($item->uri === $uri) {
      $file = $item;
      break;
    }
  }
}

if (!isset($file)) {
  return;
}

You see the $file ? $file is returned in the first line :

$files = file_load_multiple(array(), array('uri' => $uri));

In fact, the file_load_multiple can return you more than one file. But not really used like that (with just one URI). So, the $files contain just your $file.

So, if Drupal don't find your file, count($files) will return 0 and you don't go through the if(). But normally, count($files) will return 1, for the file matching with the URI :

if (count($files)) {
  foreach ($files as $item) {
    // Since some database servers sometimes use a case-insensitive comparison
    // by default, double check that the filename is an exact match.

    if ($item->uri === $uri) {
      $file = $item;
      break;
    }
  }
}

The first time when Drupal will enter in the foreach, the $item->uri will be the same as $uri. So it's your file, and you stock all informations in $file :

$file = $item;

And it's like that you have provide your $file. By the way, the code above come from the implementation of hook_file_download in the File module.

2
  • thank u for following my issue. but count($files) is 0 am i am sure the file exists! i dont know why he used file_load_multiple bcuz it is for multiple files and each time we have only one file in the link!
    – PersianMan
    Mar 31, 2014 at 13:27
  • Yes, file_load_multiple() is for load multiple files. For load just one file, you can use file_load(). But with file_load() you need to have the File Id. So if you have the fid, the easiest way is file_load, else it's load_file_multiple, which can be use with an array of file ids or just an array of URI. Finally, if load_file_multiple return 0, may be do you have an issue with permission on this file. Bye bye Mar 31, 2014 at 14:28
0

You seems to have most of it in place already. To get the $file object, you call file_load:

$file = file_load($fid);

The argument ($fid) is the file id that is stored in the table {file_managed}.

Note that the file will only appear in the table {file_managed} if the file is uploaded to the site with the use of Drupal. If you've put the file in the private folder by some other means (e.g. you've used ftp or just copied the file into that folder), then the file is not managed by Drupal and can not be accessed through the Drupal API.

3
  • thank u very very much! but another question: this is what $uri passes: for example = "private://2.gif". so how should i extract file name which 2.gif? is there any function or i have to use normal string functions?
    – PersianMan
    Mar 31, 2014 at 10:30
  • sorry for that! i thought it might be better to be here to complete the main question! by thy way i found the drupal function for that which is: drupal_set_message(drupal_basename($uri));
    – PersianMan
    Mar 31, 2014 at 11:09
  • private files are there in private folder! but they are not necessarily resisted in file_managed table! what should i do?
    – PersianMan
    Mar 31, 2014 at 11:14
0

my problem solved by the help of friends here
this is the my final code:

function my_module_file_download($uri) {

  // Get the file record based on the URI. If not in the database just return.
  $files = file_load_multiple(array(), array('uri' => $uri));
  if (count($files)) {
    foreach ($files as $item) {
      // Since some database servers sometimes use a case-insensitive comparison
      // by default, double check that the filename is an exact match.
      if ($item->uri === $uri) {
        $file = $item;
        break;
      }
    }
  }

  if (!isset($file)) {
    return;
  }

  // Put your permission check code here.

  // When done perform the following code    
  if ($can_access_file) {
    $headers = file_get_content_headers($file);
    return $headers;
  }  
  return -1; 
}

my mistake was copying files to private folder directly! that was why the above code did not work! I created a custom content with to fields: file and taxonomy! it uploads the files to private folder and at the same time adds file name to "file_managed" table! Thank you all for helping me understand my mistakes :) i appreciate that

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.