Multiple files upload using managed_file in drupal

There is already a multiple file management module in drupal: plupload, but no luck when I tried to use it following their instructions.

<?php
$form['my_element'] = array(
  '#type' => 'plupload',
  '#title' => t('Upload files'),
  '#description' => t('This multi-upload widget uses Plupload library.'),
  '#upload_validators' => array(
    'file_validate_extensions' => array('jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'),
    'my_custom_file_validator' => array('some validation criteria'),
  ),
  '#plupload_settings' => array(
    'runtimes' => 'html5',
    'chunk_size' => '1mb',
  ),
);
?>

The above element only gave me a few lines of text on the form, nothing else.

While in image example, #type managed_file is used to upload files, one at a time, I am just wondering if the same control can be used for multiple file upload.

Good news is managed_file is already ajax based, and can be created multiple instances on same form.

The screen shots of form that has multiple manage_file instances created

multiple files upload with managed_file

multiple files upload with managed_file

So you can see that you can add and remove multiple files at the same time

Here is the code, the module name is multifiles_example

/**
 * Implements hook_menu().
 *
 * Provide a menu item and a page to demonstrate features of this example
 * module.
 */
function multifiles_example_menu() {
  $items = array();
  $items['multifiles_example'] = array(
    'title' => 'Multifiles Example',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('multifiles_example_form'),
    'access arguments' => array('access content'),
  );

  return $items;
}

/**
 *
 * @param unknown_type $form
 * @param unknown_type $form_state
 * @return multitype:string NULL
 */
function multifiles_example_form($form, &$form_state) {

  // in case of ajax, form_state is already populated, as drupal_process_form already run before this.

  $form['allfiles'] = array(
      '#type' => 'container',
      '#tree' => true,
  );

  $filecount =0;

  // first loading
  if (!$fileslist = variable_get('multifiles_example_files', FALSE))
  {
    $fileslist =array('files'=>array(),'count'=>0);
  }

  // load existing list
  foreach($fileslist['files'] as $key=>$value)
  {
    // Use the #managed_file FAPI element to upload an image file.
    $form['allfiles']['file_'.$filecount] = array(
        '#title' => t('Image '.$filecount),
        '#type' => 'managed_file',
        '#upload_location' => 'public://multifiles_example_images/',
        '#default_value' =>$value,
        '#upload_validators' => array(
            'file_validate_extensions' => array('gif png jpg jpeg'),
            // Pass the maximum file size in bytes
            'file_validate_size' => array(1000000),
        ),
    );

    $filecount++;

  }

 // always attach a new upload control

    // Use the #managed_file FAPI element to upload an image file.
    $form['allfiles']['file_'.$filecount] = array(
        '#title' => t('Image '.$filecount),
        '#type' => 'managed_file',
        '#upload_location' => 'public://multifiles_example_images/',
        '#upload_validators' => array(
            'file_validate_extensions' => array('gif png jpg jpeg'),
            // Pass the maximum file size in bytes
            'file_validate_size' => array(1000000),
        ),

    );

  // Submit Button.
  $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Save'),
  );

  return $form;
}

/**
 *
 * @param unknown_type $form
 * @param unknown_type $form_state
 */
function multifiles_example_form_submit($form, &$form_state) {

  // to get the current list

  if ($fileslist = variable_get('multifiles_example_files', FALSE))
  {

    //have to find out deleted ones
    $i=0;
    foreach($fileslist['files'] as $fileindex=>$fileidvalue)
    {
      if ($form_state['values']['allfiles']['file_'.$i] ==0)
      {
        // deleted
        $file =  file_load($fileidvalue) ;
        if ($file) {
          // When a module is managing a file, it must manage the usage count.
          // Here we decrement the usage count with file_usage_delete().
          file_usage_delete($file, 'multifiles_example', 'sample_image', 1);

          // The file_delete() function takes a file object and checks to see if
          // the file is being used by any other modules. If it is the delete
          // operation is cancelled, otherwise the file is deleted.
          file_delete($file);
        }
      }
      $i++;
    }

  }
  // now save files

  $newlist= array('files'=>array(),'count'=>0);
  foreach ($form_state['values']['allfiles'] as $key =>$value)
  {
    if ($value!=0)
    {
      // file exists
      $file = file_load($value);
      if ($file->status==0)
      {
        $file->status = FILE_STATUS_PERMANENT;
        file_save($file);

        // When a module is managing a file, it must manage the usage count.
        // Here we increment the usage count with file_usage_add().
        file_usage_add($file, 'multifiles_example', 'sample_image', 1);
      }

      $newlist['files'][$file->fid]= $file->fid;
      $newlist['count'] = $newlist['count']+1;

    }

  }

  if($newlist['count']>0)
  {
    variable_set('multifiles_example_files', $newlist);
  }
  else
  {
    variable_set('multifiles_example_files', false);
  }

  $form_state['redirect']='multifiles_example';

}

A few points

1. It is using managed_file type, no other libraries or modules needed

2. Files are saved in a variable, and rendered with managed_file, which means you can remove and edit those files

3. Then always a file upload is attached

4. #tree property of container must be turned on, to accommodate multiple managed_file instances underneath.

5. #upload_validators, first item is for file extensions, space delimited, the second item is file size limit for managed_file control.

    '#upload_validators' => array(
            'file_validate_extensions' => array('gif png jpg jpeg'),
            // Pass the maximum file size in bytes
            'file_validate_size' => array(1000000),
        ),

6. Files and file list (a list of file ids) are actually persisted in save submit

7. The only regret is when you had file in the last file upload, you need press save button for another file upload control to be available. Ideally when you fill up the last file upload, there should be another one to be attached automatically, in theory it should be easy, as managed_file control itself is ajax control, but being ajax style is also the cause of problem, as manage_file ajax response only cause the re-rendering of control itself, not on the form level.

This is not an issue at all if you have fixed number of files to manage, for example you allows 5 photos maximum per user, you can then always render five manage_file instances.

The code example does not have any limit on number of files.

8. Styling : if you do not like default styling, you can either re implement theme functions in your theme, or use hook_theme_registry_alter to redirect the theme function to implementations in module.

Theme functions are :
theme_file_managed_file, theme_file_link, theme_file_icon

Have fun

Download the source from multifiles_example

Tags:

This entry was posted on Wednesday, January 30th, 2013 at 3:22 am and is filed under PHP. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

5 Responses to “Multiple files upload using managed_file in drupal”

  1. Andres Santos says:

    Hi, I’m trying to create a custom module to upload multiple files, I appreciate your tutorial, can you share me the source files. Thanks

  2. pavel says:

    Helo. and how to preserve the value of the text field of this structure. My code:

    foreach($fileslist['files'] as $key=>$value){

    // file image
    $form['allfiles']['file_'.$filecount] = array(
    '#title' => t('Image '.$filecount),
    '#type' => 'managed_file',
    '#upload_location' => 'public://slider/',
    '#default_value' =>$value,
    '#upload_validators' => array(
    'file_validate_extensions' => array('gif png jpg jpeg'),
    'file_validate_size' => array(1000000),
    ),
    );

    //text description
    $form['allfiles']['desc_'.$filecount] = array(
    '#title' => t('Description image '.$filecount),
    '#description' => t('Input description for this image '.$filecount),
    '#type' => 'textfield',
    '#default_value' => $value,
    );

    $filecount++;

    }

    here is not stored ‘#default_value’

  3. Chad Peppers says:

    This works great but using variable_set is a global variable and will not fully function if multiple users are using the form at the same time. Try appending a unique string to the variable set, but that gets messy.

    • CleanCodeNZ says:

      Thanks Chad, you are spot on, in production, I guess you will have to store files list somewhere that is unique per user/per customer.

Leave a Reply

*