rockstardev
rockstardev

Reputation: 13527

Programmatically insert a file in Drupal's file system?

I am grabbing a file using CURL, and want to save it locally, but so it plugs into Drupal's file system. Would I have to do a manual insert or is there a better way? The closest I could find was:

function image_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items);

But I don't know how to use this. Any better suggestions?

Upvotes: 3

Views: 5325

Answers (3)

thirdender
thirdender

Reputation: 3951

I was able to do the following to add a file from the public:// Drupal folder (typically sites/default/files or sites/DOMAIN/files) using file_save.

$uri = 'public://filename.pdf';
$file = file_save((object) array(
  'filename' => basename($uri),
  'uri' => $uri,
  'status' => FILE_STATUS_PERMANENT,
  'filemime' => file_get_mimetype($uri),
));

This adds the appropriate entry into the Drupal core file_managed table. If you have the File Entity module installed, an entry is created there as well.

The returned file object will include the database ID as $file->fid.

Upvotes: 0

Kai Qing
Kai Qing

Reputation: 18833

This just about drove me out of my mind and in trying to figure out a simple way to circumvent the form API expectations I came across this question. Clive's answer was the beginning of how I figured it out because it looks like you can provide a source as the first arg for file_save_upload() but as it turns out, you cannot. At least not in the current drupal 7.30.

I checked the link he provided and deconstructed the function. Basically, even if you pass a full source path it still looks for the first arg in the $_FILES array way down the path. Who knows why, but what a dumb idea. So I did this:

//after verifying all post fields are set...
$node = new stdClass();
$node->type = 'user_files';
node_object_prepare($node);

$node->title    = $_POST['title'];
$node->author = 2; //hardcoded because only admin uses this form
$node->uid = 2;
$node->language = LANGUAGE_NONE;
$node->body[$node->language][0]['value']   = $_POST['body'];
$node->body[$node->language][0]['summary'] = text_summary($_POST['body']);
$node->body[$node->language][0]['format']  = 'filtered_html';

$node->field_first_name[$node->language][0]['value'] = $_POST['first_name'];
$node->field_last_name[$node->language][0]['value'] = $_POST['last_name'];

node_save($node);

if(isset($_FILES['file']['tmp_name']) && $_FILES['file']['name'] != '')
{
  //upload file

  $file = new stdClass();
  $file->uid = 2;
  $file->status = 0;
  $file->filename = trim(drupal_basename($_FILES['file']['name']), '.');
  $file->uri = $_FILES['file']['name'];
  $file->filemime = file_get_mimetype($file->filename);
  $file->filesize = $_FILES['file']['size'];
  $file->filename = file_munge_filename($file->filename, 'jpg jpeg gif png doc docx pdf');
  $file->destination = file_destination('private://' . $file->filename, FILE_EXISTS_RENAME);
  $file->uri = $file->destination;
  if (!drupal_move_uploaded_file($_FILES['file']['tmp_name'], $file->uri)) {

    return false; //set errors or do whatever you want on error
  }
  else
  {
    drupal_chmod($file->uri);
    $existing_files = file_load_multiple(array(), array('uri' => $file->uri));
    if (count($existing_files)) {
      $existing = reset($existing_files);
      $file->fid = $existing->fid;
    } 

    if ($file = file_save($file)) {
      // Add file to the cache.

      $node->field_file[$node->language][0]['uri'] = 'private://'.$file->filename;
      $node->field_file[$node->language][0]['fid'] = $file->fid;
      $node->field_file[$node->language][0]['display'] = 1;
      $node->field_file[$node->language][0]['description'] = 'User uploaded file';
      node_save($node);

      //do we notify someone?
    }    
  }
}

What this does is creates a node of a specified type. In this case, user_files, then if a file is uploaded, add the file to the media table, or file table or whatever it's called. Then adds the association to the newly created node.

Is it ugly? Yeah. Why didn't I use the internal drupal form API? Shut up, that's why. We don't always have a choice, so when a client asks for a basic form that just emails them, it is quick and easy to make a simple field that sends through a mail chimp API or something. Then later they add that they want files. Then later they want it to add to the drupal back end and suddenly you have a snowball of horror that would have been a lot easier if it was done with the form API to begin with. I don't know who started it, but I had to end it and I did it with this ghastly hack. Hope it helps you or whoever else is currently spinning in the drupal vortex of horror.

Upvotes: 1

Clive
Clive

Reputation: 36956

There are a couple of ways but the easiest would be to use file_save_upload():

$source = '/path/to/file.ext';
$dest = 'public://uploads/'; // Or whatever
$file = file_save_upload($source, array(), $dest, FILE_EXISTS_RENAME);

if ($file) {
  // file_save_upload marks the file as temporary, we need to mark as permanent or it will be cleared from the database in a few hours
  $file->status = FILE_STATUS_PERMANENT;
  file_save($file);
}

Upvotes: 3

Related Questions