Last updated September 5, 2011. Created on November 5, 2008.
Edited by gagarine, joelstein, dkinzer, Kasper Souren. Log in to edit this page.

I've had to bump my head into batch uploading files to a drupal installation.

Update (sep 2011) according to http://drupal.org/node/330421#comment-2806336

<?php
// Set up any other fields in the node
$form_state['values']['title'] = 'My node';
$form_state['values']['body'] = 'This is the body text!';
$form_state['values']['op'] = t('Save');

$i = 0;
$file_drupal_path "path/under/files/directory/file.mp3";
global
$user;

// Create file object and update files table
$file            = new stdClass();
$file->filename  = basename($file_drupal_path);
$file->filepath  = $file_drupal_path;
$file->filemime  = mime_content_type($file_drupal_path);
$file->filesize  = filesize($file_drupal_path);
$file->uid       = $user->uid;
$file->timestamp = time();
drupal_write_record('files', $file);

// Create $form_state array from the returned $file object
$files[$i]            = (array) $file;
$files[$i]['status']  = 0;
$files[$i]['_weight'] = $i;

$form_state['values']['YOUR_FILEFIELD_NAME'] = $files;

// Create a new node
module_load_include('inc', 'node', 'node.pages');
$node = new stdClass();
$node->type = 'YOUR_NODE_TYPE';
drupal_execute('YOUR_NODE_TYPE_node_form', $form_state, $node);
?>

Other solutions for lazy coders/non-coders

A more elaborate way of importing large amounts of files using batch API can be found in #292904: Mass import/upload?.

FeedAPI

FeedAPI is first and foremost intended to import feeds from external sites, but it can just as well be used to import a static XML file of your choosing from a local server.

By exteding FeedAPI with Feed Element Mapper and using one of the mappers in one of these issues:

#319538: Mapper for FileField / ImageField
#535970: Mapper for imagefield
#224235: Mapper for CCK Image Field

you might be able to have it create nodes from your custom xml.

FeedAPI is supposed to be replaced by Feeds.

Feeds

If you have tried out the Feeds module, then chip in with your 2 cents here. Feeds is supposed to replace FeedAPI, but I don't know how well it is working, nor if it has the same number of modules extending its functionality as FeedAPI has.

Node Import

Node Import can import nodes from CSV/TSV files. It says that CCK filefield and imagefield content may be supported.

I haven't tried it since before I started this book page, because at that time it didn't fullfill my needs.

Further solutions

I haven't browsed the Import/Export projects in depth, you might find a better fit for your needs there.

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

upupax’s picture

This is exactly what I was looking for! Worked perfectly!
I need it to import an image folder and fill each node with an image!
Just in case: using imagefield you could even set image alt and image title in this way

$node->field_img_tel = array(
    array(
'fid' => $file->fid,
'title' => basename($file->filename),
'filename' => $file->filename,
'filepath' => $file->filepath,
'filesize' => $file->filesize,
'mimetype' => $mime,
'description' => basename($file->filename),
'list' => 1,
'data'=>array('alt' => 'image alt',
'title' => 'image title',
),
),
);

Thanks a lot!

pkej’s picture

I even came across this #201983: How To: Migrate from Image.module to ImageField Documentation Project for anyone needing to migrate from image to image field based nodes (as I am needing just this second).

Paul K Egell-Johnsen

Paul K Egell-Johnsen

Royce’s picture

I have been looking around for 2 days for a way to do this! I was missing the "drupal_write_record" though I had the rest correct. Like the original, this code should work for any filefield. I used it for imagefield.

I modified the above to work with drupal_execute rather than node_save because I have read it is the better practice.

Prior to this you need to have your file on the server. Note the variables at the top need to be changed.

Here is the code that is working for me in Drupal 6:

<?php
  $mime
'image/jpeg';
 
$your_node_type = 'custom';
 
$file_drupal_path "sites/default/files/YOUR_FILE.jpeg";

 
$file = new stdClass();
 
$file->filename = basename($file_drupal_path);
 
$file->filepath = $file_drupal_path;
 
$file->filemime = $mime;
 
$file->filesize = filesize($file_drupal_path);

 
$file->uid = $uid;
 
$file->status = FILE_STATUS_PERMANENT;
 
$file->timestamp = time();
 
drupal_write_record('files', $file);
 
$file->fid = db_result(db_query("SELECT fid FROM {files} WHERE filepath = '%s'", $file->filepath));

// Create a new node
$form_state = array(); module_load_include('inc', 'node', 'node.pages');
$node = array('type' => $your_node_type);
$form_state['values']['type'] = $your_node_type; // the node's type
$form_state['values']['title'] = 'Your node title';
$form_state['values']['body'] = 'This is the body text!';

// add CCK fields
$form_state['values']['field_price'][0]['value'] = 14;
$form_state['values']['field_rid'][0]['value'] =13;

//more node values
$form_state['values']['name'] = 'yourusername';
$form_state['values']['op'] = t('Save');

//add image field
$form_state['values']['field_pic'] = array(
    array(
     
'fid' => $file->fid,
     
'title' => basename($file->filename),
     
'filename' => $file->filename,
     
'filepath' => $file->filepath,
     
'filesize' => $file->filesize,
     
'mimetype' => $mime,
     
'description' => basename($file->filename),
     
'list' => 1,
    ),
  );

$errs= drupal_execute($your_node_type.'_node_form', $form_state, (object)$node);
if (
count($errs)) {
echo
$errs;
}
?>
mghatiya’s picture

Hi,

I tried to use your code, but I get error in following line:
$errs= drupal_execute($your_node_type.'_node_form', $form_state, (object)$node);

It says: warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, 'testing1_node_form' was given in C:\wamp\www\bentexdp\includes\form.inc on line 378.

Here "testing1" is my $node_type

Evidently, I have not explicitly defined, testing1_node_form() function. Do I need to do that? I didn't see that in your code.
What do I need to write in that function if I create one?

Thanks,
Mukesh

Ira Rabinowitz’s picture

I'm new to Drupal. This is exactly what I need.

Where exactly do I put this code?

pkej’s picture

Sorry to answer so late; you put it in your import code, which would be a specifically tailored php page.

You could also look into FeedAPI if your data is in XML.

Paul K Egell-Johnsen

ncameron’s picture

The above code worked like a charm for us. We have a load of existing nodes all with the file specified in a text field and the file manually uploaded via FTP. We wanted to do a bulk update to include all those files in the drupal file system. The code below is a modification which opens up a node, adds a specified field, then saves it. The code is designed to be placed in a free standing php file in the root directory and then executed (make sure you do this securely).

<?php
   
require_once './includes/bootstrap.inc';
   
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

 

$mime = 'audio/mpeg'; // I was importing mp3 files

 

$file_drupal_path "sites/default/files/audio_files/file_name.mp3";

 

$file = new stdClass();
 
$file->filename = basename($file_drupal_path);
 
$file->filepath = $file_drupal_path;
 
$file->filemime = $mime;
 
$file->filesize = filesize($file_drupal_path);

 

$file->uid = '1';
 
$file->status = FILE_STATUS_PERMANENT;
 
$file->timestamp = time();
 
drupal_write_record('files', $file);
 
$file->fid = db_result(db_query("SELECT fid FROM {files} WHERE filepath = '%s'", $file->filepath));
   
   
   
$nid = '123' // your NID here
   
 
$node = node_load($nid);

 

$node->field_audio_file = array(
    array(
     
'fid' => $file->fid,
     
'title' => basename($file->filename),
     
'filename' => $file->filename,
     
'filepath' => $file->filepath,
     
'filesize' => $file->filesize,
     
'mimetype' => $mime,
     
'description' => basename($file->filename),
     
'list' => 1,
    ),
  );

 

node_save($node);
?>

--
Open Source Web Solutions
http://cameronandwilding.com

ball.in.th’s picture

To build file information, you can also use field_file_load() or field_file_save_file(). See file filefield/field_file.inc for details.

Example:

<?php
$node
->field_name[] = field_file_load('sites/default/files/filename.gif');
?>

--
วิเคราะห์บอล - ชุมชนคอบอลพันธ์แท้, ผลบอล

czeky’s picture

is it working this way as well? was working for me just ones.. other attempts to load image failed..

<?php
$node
->field_name[0] = field_file_load('sites/default/files/filename.gif');
?>
zigazou’s picture

Very helpful post !

Here's my contribution.

The following function has been designed for Drupal 6.

<?php
function drupal_add_existing_file($file_drupal_path,$uid=1,$status=FILE_STATUS_PERMANENT) {
 
$file=(object)array(
   
'filename' =>basename($file_drupal_path),
   
'filepath' =>$file_drupal_path,
   
'filemime' =>file_get_mimetype($file_drupal_path),
   
'filesize' =>filesize($file_drupal_path),
   
'uid'      =>$uid,
   
'status'   =>$status,
   
'timestamp'=>time()
  );

 
drupal_write_record('files',$file);

  return
field_file_load($file_drupal_path);
}
?>

Improvements include :

  • use of file_get_mimetype to guess the mime type
  • use of field_file_load (thanks to ball.in.th) to retrieve an already usable array for node_save

When using it, the original code of this post becomes :

<?php
function drupal_add_existing_file($file_drupal_path,$uid=1,$status=FILE_STATUS_PERMANENT) {
 
$file=(object)array(
   
'filename' =>basename($file_drupal_path),
   
'filepath' =>$file_drupal_path,
   
'filemime' =>file_get_mimetype($file_drupal_path),
   
'filesize' =>filesize($file_drupal_path),
   
'uid'      =>$uid,
   
'status'   =>$status,
   
'timestamp'=>time()
  );

 
drupal_write_record('files',$file);

  return
field_file_load($file_drupal_path);
}

$file_drupal_path "path/under/files/directory/file.mp3";

$node = new StdClass();
$node->type = 'word';
$node->body = $body;
$node->title = $word;
$node->field_pronounciation = array(drupal_add_existing_file($file_drupal_path));
$node->uid = 1;
$node->status = 1;
$node->active = 1;
$node->promote = 1;
node_save($node);
?>
czeky’s picture

Hi, this seems working, MANY thanx for this..

Is it possible to import more images? Now, my one filefield has 1-9 images in array

field_image[0] - field_image[8]

is it possible to store them all as well? script below was working, but now it is not, dunno why.. so I'd like to configure your script to import

<?php
$node
->field_image[0] = field_file_load('sites/default/files/pic-0.jpg');
$node->field_image[1] = field_file_load('sites/default/files/pic-1.jpg');
node_save($node);
?>

many thanx again

czeky’s picture

<?php
$filename
= array("sites/default/files/0.jpg","sites/default/files/1.jpg","sites/default/files/2.jpg");
$file_drupal_path = $filename;
foreach(
$file_drupal_path as $key => $value) {
echo
$value."<br>";
}
?>

This stores array of images into $file_drupal_path, so I need just a way how to save multiple files from this array in cck filefield - in

<?php
 $node
->field_pronounciation = array(drupal_add_existing_file($file_drupal_path)); 
?>

Many thanx

czeky’s picture

<?php
$file_drupal_path
= array("sites/default/files/0.jpg","sites/default/files/1.jpg","sites/default/files/2.jpg");
...
...
...
foreach(
$file_drupal_path as $key => $value) {
   
$node->field_picture_test[$key]= array(drupal_add_existing_file($file_drupal_path[$key]));
    print
$file_drupal_path[$key];
}
?>

this code doesn't work, don't know why.. ;-(

<?php
$file_drupal_path
= array("sites/default/files/0.jpg","sites/default/files/1.jpg","sites/default/files/2.jpg");
...
...
...
foreach(
$file_drupal_path as $key => $value) {
   
$node->field_picture_test = array(drupal_add_existing_file($file_drupal_path[$key]));
    print
$file_drupal_path[$key];
}
?>

this works ... print $file_drupal_path[$key]; prints all the values in array, but only last one is recorded into $node->field_picture_test[0], the other two $node->field_picture_test[1] and $node->field_picture_test[2] are empty

help please..

zigazou’s picture

I admit I never tried multiple files...

Your code is :

<?php
foreach($file_drupal_path as $key => $value) {
   
$node->field_picture_test = array(drupal_add_existing_file($file_drupal_path[$key]));
    print
$file_drupal_path[$key];
}
?>

I think it should be something like :

<?php
$node
->field_picture_test=array();
foreach(
$file_drupal_path as $value) {
   
$node->field_picture_test[] = drupal_add_existing_file($value);
    print
$file_drupal_path[$key];
}
?>

It's not tested but worth a try ;-)

czeky’s picture

Well... thanx!

Your code was almost there! thank You

here is a working code

<?php
$node
->field_picture_test=array();
foreach(
$file_drupal_path as $key => $value) {
$node->field_picture_test[] = drupal_add_existing_file($value);
//print $file_drupal_path[$key]; // this actually was only for my test print out, doesn't do anything
}
?>

Now, let's see what imagecache will do with this, I'm using imagecache presets with imported images.. will keep posting here

Thank You Zigazou!

jaybee1001’s picture

Hello all who have contributed to this code/thread.

This looks to be exactly what I need, if only I could understand it!! Here is what I need to do:

1. Attach CCK File Fields to newly created custom content type nodes, which are mp3 files unique to each node. Some nodes have 2 CCK file fields.
2. The mp3 files are already present on the host server

Could someone help me with the following:

1. How to associate each newly created node with the required mp3 file?
2. How to loop through all the nodes of a specified content type, attaching the relevent mp3 file(s).

It would be great if you could indicate which code fragments in this thread I should try to understand and use.

Thanks in advance :-)

zigazou’s picture

If you create a CCK field named "mp3", it will be included in the $node object as $node->field_mp3.

$node->field_mp3 always contains an array indexed by an integer, whether you have enabled multiple values or not. For single values, you will access $node->field_mp3[0].

You may look the second block code of my previous comment http://drupal.org/node/330421#comment-2480042 for a start.

If I understand what you want to do, you already have created the nodes and now you want to add mp3s to them.

In fact, it is a little easier than creating a node.

If you have 2 separate mp3 fields named mp3_first and mp3_second, the code will be (NOT tested, use at your own risk) :

<?php
// Load Drupal environment
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

// No time limit
set_time_limit(0);

// Insert here the drupal_add_existing_file function definition

$node=node_load($nid);
$node->field_mp3_first =array(drupal_add_existing_file("path/under/files/directory/file1.mp3"));
$node->field_mp3_second=array(drupal_add_existing_file("path/under/files/directory/file2.mp3"));
node_save($node);
?>

node_load will populate a $node object that you can modify and save via node_save.

You need to save this code into a PHP file at the root of your Drupal site and to call it with your web browser.

Note : Remember that drupal_add_existing_file cannot be used twice on the same file or you will get error messages.

Hope it helps you !

jaybee1001’s picture

Thank you Zigazou - I will give that a try.

I appreciate your help.

MauMau’s picture

Ok, so here I am.

I've got a picture at

$usersubmittedpic[filepath] = /sites/default/files/webform/nice_doggy.jpg

and I want to add a node over at

node/add/doggingstory/submission/789

The doggingstory content type has an imagefield (CCK) called field_doggypic.

function dogging_form_alter($form_id, &$form) {
if ($form_id == 'doggingstory_node_form' && arg(3)=='submission' && is_numeric(arg(4))) {
$form[field_doggypic][field_doggypic_upload]['#description'] = '<img width = "224" src = "/' . $usersubmittedpic['filepath'] . '">";
}

Woof! Woof! I can see the picture. I\m almost there. But what do I do to
1. Have Drupal copy the pic to the file table and /sites/default/files/doggypics
2. Have the picture show up on the new node

Just setting

$form[field_doggypic][field_doggypic_upload]['#description']

to the filepath didnt help one bit.

This is Drupal 5.

chandantyagi’s picture

hi , this is working fine, but if i want to add list and description also...
Please help how to do so

Thanks in advance

giorgio79’s picture

Fantastic stuff so far! Would anyone know how to make this work with Filefield Paths, so that the path is assigned as per the pattern?
http://drupal.org/project/filefield_paths
Also posted as a support req: #717856: Programmatic creation of CCK Filefield - Make it work with Filefield Paths

Karlheinz’s picture

A small note:

drupal_write_record() passes the node (here, $file) as a reference, so after you call this function, $file->fid will already be set.

In other words, this line is totally unnecessary:

<?php
$file
->fid = db_result(db_query("SELECT fid FROM {files} WHERE filepath = '%s'", $file->filepath));
?>

-Karlheinz

Karlheinz’s picture

One more thing:

I belive it is considered best practice to create nodes using drupal_execute() rather than node_save(). That way, it goes through the validation process.

The idea is that you call a custom function that inserts data into $form_state['values'] to mirror what a user would have entered, then pass that to drupal_execute().

In that case, the code would be something like this:

<?php
// Set up any other fields in the node
$form_state['values']['title'] = 'My node';
$form_state['values']['body'] = 'This is the body text!';
$form_state['values']['op'] = t('Save');

$i = 0;
$file_drupal_path "path/under/files/directory/file.mp3";
global
$user;

// Create file object and update files table
$file            = new stdClass();
$file->filename  = basename($file_drupal_path);
$file->filepath  = $file_drupal_path;
$file->filemime  = mime_content_type($file_drupal_path);
$file->filesize  = filesize($file_drupal_path);
$file->uid       = $user->uid;
$file->timestamp = time();
drupal_write_record('files', $file);

// Create $form_state array from the returned $file object
$files[$i]            = (array) $file;
$files[$i]['status']  = 0;
$files[$i]['_weight'] = $i;

$form_state['values']['YOUR_FILEFIELD_NAME'] = $files;

// Create a new node
module_load_include('inc', 'node', 'node.pages');
$node = new stdClass();
$node->type = 'YOUR_NODE_TYPE';
drupal_execute('YOUR_NODE_TYPE_node_form', $form_state, $node);
?>

Obviously you would substitute YOUR_NODE_TYPE for whatever type of node you're creating, and YOUR_FILEFIELD_NAME for whatever the name of the filefield is.

I set the variable $i in this code, in case you can upload multiple files per node. In this case, you would iterate over each uploaded file you wanted to attach to the node.

Also, note that $files[$i]['status'] has to be set to 0. If it is set to anything else, validation will fail with a message about not allowing references to a file. (The exact message escapes me, and I don't want to break my code just to get it...)

If you're programmatically generating nodes according to a custom form, then you would put this code in that form's submit handler. An example of this would be searching a REST application for user-input data, then importing what is returned.

-Karlheinz

markusbroman’s picture

Great help!

I'm trying to automatically create a node when a file is placed in a specific folder and then attach the file in a filefield, this is done by a rule. I manage to get a node created with the file in a filefield. When the node is created I want the folder to be emptied, so that more nodes wont be created from the same file, but I cant get this working...

I have tried file_delete($file_drupal_path) and unlink($file_drupal_path) with no success (the file still remains in the folder).

Anyone have any ideas?

Thanks!

Markus

hunt3r’s picture

Please excuse the quick and dirty nature of this script, but it's what I used to migrate a large amount of photos from a joomla site that had the Community Builder - Profile_gallery plugin (which happened to be heavily used). I wanted to preserve over 1000 photos that were uploaded to the site and stored in this little plugin. Please excuse the SQL, I copied it out of a view created by the TW/Migrate plugins, which in the end wouldn't work for me, since Migrate only allows for One-Node:One-CCK-Value, I wanted one node to hold all the photos for each user, One-Node:Many-CCK-Values.

I used this article to get the basis of the script, but ended up using node_save over $form_state. Although I could create the node using $form_state and drupal_execute, I couldn't get the $form_state['values']['name'] value to take, so my nodes were always created with uid=0, I tried everything, and in the end creating nodes by constructing the $node object, and passing it to node_save() was the only thing that worked for me.

Anyways, This should give people an idea of how this Can be done, I'm not saying it's an end all / be all solution.

Also note: I exported relevant tables to the database, and I use the "Joomla" module to migrate users over, so it has a bridge table to match Joomla UID and Drupal UID, that was very important so I could apply ownership to the nodes.

<?php
// Bootstrap Drupal
require 'includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

//include node libs
module_load_include('inc', 'node', 'node.pages');

//Where the photos are located, I used a linux find command like this to get them into one folder:
/*
find /var/www/yyy/images/comprofiler/ -iname "pg_*" -exec cp '{}' /var/www/xxx/sites/default/files/migratedphotos/ ';'
*/
$filePath="sites/default/files/migratedphotos/";

// No time limit
set_time_limit(0);

//Distinct list of drupal and Joomla uids and some info about them needed for the node creation
$SQLouter='
            SELECT distinct joomla_users.juid AS joomla_users_juid,
                cms_users.id AS id,
                cms_users.name AS cms_users_name,
                cms_users.username AS cms_users_username,
                cms_comprofiler_plug_profilegallery.pgitemdate AS pgitemdate,
                joomla_users.uid AS joomla_users_uid
            FROM cms_users cms_users
            LEFT JOIN cms_comprofiler_plug_profilegallery cms_comprofiler_plug_profilegallery ON cms_users.id = cms_comprofiler_plug_profilegallery.userid
            LEFT JOIN joomla_users joomla_users ON cms_users.id = joomla_users.juid
            WHERE cms_comprofiler_plug_profilegallery.id IS NOT NULL
            group by joomla_users.juid'
;
   
//The actual photo content to be looped over
$SQLinner = '
            SELECT cms_users.id AS id,
                cms_users.name AS cms_users_name,
                cms_users.username AS cms_users_username,
                cms_comprofiler_plug_profilegallery.userid AS cms_comprofiler_plug_profilegallery_userid,
                cms_comprofiler_plug_profilegallery.id AS cms_comprofiler_plug_profilegallery_id,
                cms_comprofiler_plug_profilegallery.pgitemdate AS pgitemdate,
                cms_comprofiler_plug_profilegallery.pgitemdescription AS pgitemdescription,
                cms_comprofiler_plug_profilegallery.pgitemfilename AS pgitemfilename,
                cms_comprofiler_plug_profilegallery.pgitemtitle AS pgitemtitle,
                cms_comprofiler_plug_profilegallery.pgitemtype AS pgitemtype,
            joomla_users.juid AS joomla_users_juid,
            joomla_users.uid AS joomla_users_uid
            FROM cms_users cms_users
            LEFT JOIN cms_comprofiler_plug_profilegallery cms_comprofiler_plug_profilegallery ON cms_users.id = cms_comprofiler_plug_profilegallery.userid
            LEFT JOIN joomla_users joomla_users ON cms_users.id = joomla_users.juid
            WHERE cms_comprofiler_plug_profilegallery.id IS NOT NULL and joomla_users.juid = {1}'
;

//Used to count the number of users processed in the output
$userCount=1;

//Run the outer query
$result1 = db_query($SQLouter);
while (
$id = db_fetch_object($result1)) {

   

//Output something for me to see its working :)
   
print "\n[".$count++."] Migrating: ". $id->cms_users_name;

   

//Create a new node class for each user
   
$node = new stdClass();
   
   
//reset some variables for each node
   
$i=0;
   
$files = array();
   
$lastItemAdded="";

   

//loop through this users photo data set
   
$result2 = db_query(str_replace('{1}', $id->joomla_users_juid, $SQLinner));
    while (
$pg = db_fetch_object($result2)) {

       

//Create this files path (not stored in database)
       
$file_drupal_path = $filePath.$pg->pgitemfilename;
       
       
//Cobble the title and description into one field
       
$description ="";
        if(
strlen($pg->pgitemtitle) > 0)
        {
           
$description = $pg->pgitemtitle;
        }
        if(
strlen($pg->pgitemdescription) > 0)
        {
           
$description .= " - " . $pg->pgitemdescription;
        }
       
       
$file            = new stdClass();
       
$file->data      = array('description' => $description, 'title' => $pg->pgitemtitle);
       
$file->filename  = basename($file_drupal_path);
       
$file->filepath  = $file_drupal_path;
       
$file->filemime  = mime_content_type($file_drupal_path);
       
$file->filesize  = filesize($file_drupal_path);
       
$file->uid       = $id->joomla_users_uid;
       
$file->timestamp = strtotime($pg->pgitemdate);
       
drupal_write_record('files', $file);
       
       
       
$files[$i]            = (array) $file;
       
$files[$i]['status']  = 0;
       
$files[$i]['_weight'] = $i;
   

       

//Increment counter
       
$i++;
               
//Used as the node created time
       
$lastItemAdded=strtotime($pg->pgitemdate);
    }
   
   
// Set up any other fields in the node
   
$node->title=    $id->cms_users_username."'s Migrated Profile Gallery";
   
$node->body=    "<p>This gallery was migrated from the profile gallery system on behalf of: <b>". $id->cms_users_username."</b></p>";
   
$node->type=    'photo_gallery';
   
$node->uid=    $id->joomla_users_uid;//"$id->cms_users_username";
   
$node->created=    $lastItemAdded;
   
$node->changed=$lastItemAdded;
   
$node->status=    1;
   
$node->promote=0;
   
$node->sticky=     0;
   
$node->format=     1;
   
$node->language='en';
   
$node->taxonomy=array(48);
   
   
//Add CCK field arrays
   
$node->field_photo=$files;
   
   
//Save the Node
   
$errs=node_save($node);
   
   
printErr($errs);
}

function

printErr($e)
{
    if (
count($e)) {
        echo
$e;
    }
}

?>
aznboy’s picture

Well, I started trying to create a node which works out fine except for the file field. Perhaps someone can give me some help on this.

$node = new stdClass();
$node->uid = $user->uid;
$node->status = 1; 
$node->promote = 0; 
$node->sticky = 0; 
$node->title = $title1;
$node->body = $body1;
$node->comment= 2;
$node->type = 'review';
$node->created = time();
$node->format = 1;
$node->uid = 1;
$node->field_label['0']['value']=$label1;

The variable for the file is $getfile which just reads the name of the file from a directory which is uploaded by ftp and already existing so just trying to fill in the data. The files are mp3 for podcasting. So in the example provided show path as:

$file_drupal_path =  "path/under/files/directory/file.mp3";

and I used

$file_drupal_path =  "files/private/" . $getfile;

The page gets created and all other fields gets created plus ones that I created in CCK to see if it would fill but the file field does not get data. I can get a regular text field filled with with a link to the file but I'd like to track how often users download which is why I'd like to use the drupal file system.

So, what am I doing wrong?

So the whole snippet is:

.....

$mime = 'audio/mpeg'; // I was importing mp3 files
$file_drupal_path =  "files/private/" . $getfile;
$file = new stdClass();
$file->filename = basename($file_drupal_path);
$file->filepath = $file_drupal_path;
$file->filemime = $mime;
$file->filesize = filesize($file_drupal_path);
$file->uid = $uid;
$file->status = FILE_STATUS_PERMANENT;
$file->timestamp = time();
drupal_write_record('files', $file);
$file->fid = db_result(db_query("SELECT fid FROM {files} WHERE filepath = '%s'", $file->filepath));

//create node
$node = new stdClass();
$node->uid = $user->uid;
$node->status = 1; 
$node->promote = 0; 
$node->sticky = 0; 
$node->title = $title1;
$node->body = $body1;
$node->comment= 2;
$node->type = 'review';
$node->created = time();
$node->format = 1;
$node->uid = 1;
$node->field_label['0']['value']=$label1;
  $node->field_review = array(
    array(
      'fid' => $file->fid,
      'title' => basename($file->filename),
      'filename' => $file->filename,
      'filepath' => $file->filepath,
      'filesize' => $file->filesize,
      'mimetype' => $mime,
      'description' => basename($file->filename),
      'list' => 1,
    ),
  );

....

Thanks for any help.

aznboy’s picture

Hey, got it working. For some reason I had modules that were spitting back errors and I wasn't seeing the errors in the broswer. Also the post above actually was what I used which help me out greatly.

asiby’s picture

I am seeing this all over the place.

<?php
 drupal_write_record
('files', $file);
$file->fid = db_result(db_query("SELECT fid FROM {files} WHERE filepath = '%s'", $file->filepath));
?>

You must remember that the drupal_write_record('files', $file) function will pass the $file parameter by reference. But what does that mean? Well, simply that the function will be able to modify this upload depending on the outcome of the work to be done. Upon success, the property $file->nid will be set to the id of the new record that has been created. So you can save some server resources and node run yet another query. Read what the Drupal's api says at http://api.drupal.org/api/function/drupal_write_record/6 ...

halcyonCorsair’s picture

The api has changed slightly, and if you wish to add a description to a file, you now need to do the following:

$node->field_pronounciation = array(
    array(
      'fid' => $file->fid,
      'title' => basename($file->filename),
      'filename' => $file->filename,
      'filepath' => $file->filepath,
      'filesize' => $file->filesize,
      'mimetype' => $mime,
      'data' => array(
          'description' => basename($file->filename),
      ),
      'list' => 1,
    ),
  );
shriji’s picture

Hi, I am a non programmer but some how managed to run the script for importing just one file. However I dont know how to loop through a director using drupal API. I have over 20,000 files to import. Also how can we add taxonomy term in code?

Thanks

Regards
Shriji

xurizaemon’s picture

If you look at the docs for Drupal's File Interface, you'll see some functions which will help you.

file_scan_directory() is likely to be useful, as well as your more standard PHP file tools.

<?php
  $files
= file_scan_directory('/tmp', '.*');
  foreach (
$files as $file ) {
   
/* better (you'll need devel.module installed): */
    // dpm($file);
    /* failing that: */
    // drupal_set_message(print_r($file,1)); // show the file object
 
}
 
?>
dkinzer’s picture

There's a mistake in this example:

$file->fid = db_result(db_query("SELECT fid FROM {files} WHERE filepath = '%s'", $file->filepath));

Is not necessary since the fid is already added via reference in a previous statement:

drupal_write_record('files', $file);

akaserer’s picture

you can also use imagefield_import

Austria

xurizaemon’s picture

People interested in a module/config -based solution for bulk file addition might also like to take a look at Media Mover.

nikitas’s picture

Hi.First of all i would like to thank you for making this so easy :) . . . but
although i've managed to import my data from a custom cms into drupal i have this issue now . . .
the nodes need to be translated and the data from the csv file are already there ! I've searched and found the tnid variable inside the node table after enabling the i18n module . . . does anyone knows if i just set this to the 'translated' nid is OK? thanx!I mean i've tested this and its ok . . .but what if i stumble into some feature issue because some variable should be written somewhere else during the importing process etc . . .

icosa’s picture

This works well, thanks, except that I think filesize needs the full path so...

$file->filesize = filesize($file_drupal_path);

should be something like:

$file->filesize = filesize($_SERVER['DOCUMENT_ROOT'] . $file_drupal_path);

I'm surprised nobody else has mentioned this so perhaps others have something set so that paths are relative to the document root.

ContentBloom’s picture

Hi All,

I have been banging my head against the wall trying to get some code based on the above example to work. Here is what i'm doing:

1) I have a webform that a user enters data and an image (form field type 'file')
2) On submission I have a custom module that get the form data and creates a node based on a content type i have defined.
3) It works great but the image is not set in the created node.
4) The file is uploaded into the files /system/files/webform/test.png directory (I can view the file over http fine)

Here is a snippet of my code:

$file_drupal_path = "system/files/webform/test.png";

$newcandidate->field_candidate_picture = array(drupal_add_existing_file($file_drupal_path, $newcandidate->uid, FILE_STATUS_PERMANENT));

function drupal_add_existing_file($uploadfilepath,$uid=1,$status=FILE_STATUS_PERMANENT) {
$file=(object)array(
'filename' =>basename($file_drupal_path),
'filepath' =>$file_drupal_path,
'filemime' =>file_get_mimetype($file_drupal_path),
'filesize' =>filesize($file_drupal_path),
'uid' =>$uid,
'status' =>$status,
'timestamp'=>time()
);

drupal_write_record('files',$file);

return field_file_load($file_drupal_path, TRUE);
}

I'd really appreciate it if someone can spot the mistake i'm making.

Huge thanks in advance!

newnewuser’s picture

thanks,,

jibran’s picture

Just change my_form_file_filed and use it :)

<?php
 
$file
=file_save_upload('my_form_file_filed',array(),file_directory_path());//drupal way to upload file
 
if(file_set_status($file, FILE_STATUS_PERMANENT)){
   
$node->filefield[0]=(array)$file;
 }
?>
feedbackloop’s picture

This is what I got for Drupal 7, after much tinkering:

<?php
  $filepath
= drupal_realpath("sites/default/files/tmp_osp/$filename");
 
$file = (object) array(
   
'uid' => 1,
   
'uri' => $filepath,
   
'filemime' => file_get_mimetype($filepath),
   
'status' => 1,
   
'display' => 1,
  );
 
$file = file_copy($file, file_build_uri("OSP/"), FILE_EXISTS_REPLACE);
 
$node->field_doc_file[LANGUAGE_NONE][0] = (array)$file;
?>

I was attaching files that I'd already copied to the filesystem to new nodes I was creating.

(Thanks jibran for leading me on the right path.)

mellenger’s picture

I can get the above working if check the node right away using dsm($node), but if i actully browse to it and edit it, the file field isn't showing anything and if i check it with devel the field is empty. what am i doing wrong? I'm trying to make a VBO action out of this code:

<?php
function one_voice_node_download_video_action($node, $context = array()) {
 
   if(
$node->type = "one_voice" && count($node->field_video,1)){
    
drupal_set_message("process it!");
    
    
//if the vuid is set
    
$vuid = $node->field_vuid[$node->language][0]['value'];
     if(
$vuid){
      
//get the remote file and save it
       //build the URL
      
$url = "http://api.nimbb.com/Video/Download.aspx";
      
$url.= "?key=" . variable_get('nimbb_public_key');
      
$url.= "&code=" . variable_get('nimbb_private_key');
      
$url.= "&guid=" . $vuid . "&format=flv";
      
//download the file
      
$vfile = system_retrieve_file($url, "public://" . $vuid . ".flv", TRUE, FILE_EXISTS_REPLACE);
      
//if the file has been created
      
if($vfile){
        
$file = (object) array(
          
'fid' => $vfile->fid,
          
'display' => 1,
          
'description' => "",
          
'uid' => 1,
          
'filename' => $vfile->filename,
          
'uri' => $vfile->uri,
          
'filemime' => $vfile->filemime,
          
'filesize' => $vfile->filesize,
          
'status' => 1,
          
'timestamp' => $vfile->timestamp,
          
'uuid' => $vfile->uuid,
          
'rdf_mapping' => array()
         );
        
$file = file_move($file, file_build_uri("videos/"), FILE_EXISTS_REPLACE);
        
        
$node->field_video[$node->language][0] = (array)$file;
        
dsm($node, "the new node"); // this shows the node with the file added properly
      
}else{
        
drupal_set_message("the video must already exist");
       }
      
     }else{
      
drupal_set_message("no nimbb video ID, moving on..");
     }
       
  }

}
?>

Andrew Mellenger
http://mellenger.com

mellenger’s picture

Im an idiot. I figured out the problem:

<?php
 
if($node->type = "one_voice" && count($node->field_video,1)){
?>

should be

<?php
 
if($node->type == "one_voice" && !count($node->field_video,1)){
?>

So my action is now like this:

<?php
 
/**
  * Action function for one_voice_node_download_video_action.
  *
  * Download the video from nimbb.com and attach as a file to the node
  *
  * @param $node
  *   A node object provided by the associated trigger.
  * @param $context
  *   Array, not using it for anything in this function
  */
function one_voice_node_download_video_action($node, $context = array()) {

  
$yesvideo = count($node->field_video,1);
   if(
$node->type == "one_voice" && !$yesvideo){   
    
//if the vuid is set
    
$vuid = $node->field_vuid[$node->language][0]['value'];
     if(
$vuid){
      
//build the URL
      
$url = "http://api.nimbb.com/Video/Download.aspx";
      
$url.= "?key=" . variable_get('nimbb_public_key');
      
$url.= "&code=" . variable_get('nimbb_private_key');
      
$url.= "&guid=" . $vuid . "&format=flv";
      
//retrieve the file
      
$vfile = system_retrieve_file($url, "public://videos/" . $vuid . ".flv", TRUE, FILE_EXISTS_REPLACE);
      
//if the file has been created
      
if($vfile){
        
//map it to the file field, not sure if i need all of these
        
$node->field_video[$node->language][0]['fid'] = $vfile->fid;
        
$node->field_video[$node->language][0]['display'] = 1;
        
$node->field_video[$node->language][0]['description'] = "";
        
$node->field_video[$node->language][0]['uid'] = 1;
        
$node->field_video[$node->language][0]['filename'] = $vfile->filename;
        
$node->field_video[$node->language][0]['uri'] = $vfile->uri;
        
$node->field_video[$node->language][0]['filemime'] = $vfile->filemime;
        
$node->field_video[$node->language][0]['filesize'] = $vfile->filesize;
        
$node->field_video[$node->language][0]['status'] = 1;
        
$node->field_video[$node->language][0]['timestamp'] = $vfile->timestamp;
        
$node->field_video[$node->language][0]['uuid'] = $vfile->uuid;
        
$node->field_video[$node->language][0]['rdf_mapping'] = array();

       }else{
        
drupal_set_message("couldn't download it");
       }
      
     }else{
      
drupal_set_message("no nimbb video ID, moving on..");
     }
       
  }else{
   
drupal_set_message("video already attached");
  }

}
?>

Andrew Mellenger
http://mellenger.com

awm’s picture

Hi,
I am wondering what would be the best way to create a file from raw bases64 encoded data, save it and then attach it to a node. I know services module has file resource that does something like this with the file resource. However, I'm trying to convert raw base64 encoded data into a file and then attach it to a node on a form submit. Any Ideas?