Running Drupal 7. I have two file fields set up: One is public, the other private. I need to be able to take a file which has been saved as a public file, and switch it to the private file field. And, vice-versa. Can someone provide some some advice on this? Thanks!


I've written an update script to switch a file field from private to public:

function MYMODULE_update_7100(&$sandbox) {
  $field_name = 'MY_FIELD_NAME';

  $fids_data = db_select('field_data_' . $field_name, 'dat')
    ->fields('dat', array($field_name . '_fid'))

  $fids_revision = db_select('field_revision_' . $field_name, 'rev')
    ->fields('rev', array($field_name . '_fid'))

  $fids = array_unique(array_merge($fids_data, $fids_revision));

  // Move files using Drupal's File API
  $files = file_load_multiple($fids);
  foreach ($files as $file) {
    if ($file && file_exists($file->uri)) {
      $destination = str_replace('private:', 'public:', $file->uri);
      $directory = drupal_dirname($destination);
      if (file_prepare_directory($directory, FILE_CREATE_DIRECTORY|FILE_MODIFY_PERMISSIONS)) {
        file_move($file, $destination, FILE_EXISTS_RENAME);

  // Update field configuration
  $field = field_info_field($field_name);
  $field['settings']['uri_scheme'] = 'public';

What this function simply does:

  1. Gets the fid of all files from field_data and field_revision tables.
  2. Moves files using File API
  3. Updates field configuration
| improve this answer | |
  • this works but to get the default file system setting of the field switched for future file uploads you still need to temporary empty the field data and field data revision tables in the database. As for this you have to go in the database directly anyway I do not see much advantage of using an update script as you can do this replacing a lot easier in the database. – Hans Rossel Jun 27 '15 at 6:32
  • @HansRossel If you don't use any version control system like git, then you don't need an update script. However if there are more environments and/or more developers working on the same project, then an update script is required. – Елин Й. Jun 28 '15 at 20:51
  • @HansRossel I've further updated the answer. You need neither to edit the database, nor to move the files manually. The update script takes care of both. – Елин Й. Sep 15 '15 at 11:56
  • I noticed a problem where for each file a directory was created with the name of the file (e.g. public://foo/image.png/image.png). Solved by adding $destination = dirname($destination); – Herman van Rink May 18 '17 at 7:30
  • Thanks @HermanvanRink, I've updated the answer. I've used drupal_dirname and also added a file_exists check before trying to move the file. – Елин Й. May 18 '17 at 8:41

I'm assuming that your "fields" are fields on a content type, but I think this advice is helpful regardless.

  1. Use the File API (also, here) to store your files;
  2. Store the row id from the managed_files table ($file->fid) in your fields.

When you need to switch a file from one to the other, just set the values of those fields (for example, load the content using entity_load and set the field's value with something like $my_content->public_file_id = NULL and $my_content->private_file_id = 3.

...AND when referring to the file, use the public:// or private:// notation. You'll see that there's methods for actually moving the file, too, if you need to do so. I've only scratched the surface, but the File API has been very useful.

| improve this answer | |
  • OK, it took me a while to comprehend your response, but I think I've got the gist. Just a couple of clarifications. When I view the node_load hook, I see this in the public and private file attachments: – user1866032 Dec 31 '13 at 21:39
  • OK, thanks! Just a couple of clarifications. When I view the node_load hook, I see the public and private file attachments: !drive.google.com/file/d/0B9Ep0Y1FILulejhsWTJJRTM1ZWc/… entity_load() on the files gives this: !drive.google.com/file/d/0B9Ep0Y1FILulQ2xzbG9DUHRUYlk/… Do I simply load the file object and execute a file_move() changing the uri to private:// or public:// as approprite?; or, Do I modify the file attachment arrays in the node itself, moving the public elements to the private array or vice-versa as appropriate? – user1866032 Dec 31 '13 at 21:47

I finally got this to work. Essentially, Micheal's response (first response to original question) was a good guideline for beginning. I'll add some notes here in case someone else is looking for this solution.

First, you need to be able to modify the file attachment widget in order to put in a checkbox or button or whatever you want to use to indicate that an existing file needs to be switched from one file_attachment field to another. I used this very excellent tutorial as my guide to modify the widget: Drupal7 tip: Alter a field widget by
Dominique De Cooman (google this).

Next, you need to process the results of this form -- that is, create code which will actually do the physical switching. I tried using hook_node_update(), but found out it would not save my changes. I ended up putting my processing code into hook_node_presave();

My processing consisted of:

  1. Moving the file's file_attachments field elements in the $node from public table to private (or vice-versa) as suggested by Michael.
  2. Copying (or moving) the physical file using the file api. I discovered that you can use file_move() to move from from private to public, but you must use file_copy() to move file from public to private. This is because when you remove the public file_attachment's field info in the $node, that ends up deleting the physical file (or, at least removing it from the file_managed table).

Also don't forget to use the public:// and/or private:// uris as appropriate when copying/moving files.

Of course, there are a lot of little details in between, but the gist of it is that it can be done, and Michael's outline is a great roadmap for at least one way to do it.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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