Te falta esto en la definición de tu formulario:
$form['#attributes']['enctype'] = 'multipart/form-data'; // If this is not here, upload will fail on submit
Aquí está la lógica que uso para crear un widget de carga de archivos en un formulario:
// these give us the file upload widget:
$form['#attributes']['enctype'] = 'multipart/form-data'; // If this is not here, upload will fail on submit
$form['fid'] = array( '#title' => t('Upload image'),
'#type' => 'file',
'#description' => t('Images must be one of jpg, bmp, gif or png formats.'),
);
Y aquí está la contraparte de esa lógica, que tengo en la devolución de llamada validada de mi formulario, porque tengo restricciones de nombre de archivo de imagen en mi lógica, pero puede colocar esto en la devolución de llamada de envío si lo desea:
// @see: http://api.drupal.org/api/function/file_save_upload/6
// $file will become 0 if the upload doesn't exist, or an object describing the uploaded file
$file = file_save_upload( 'fid' );
error_log( 'file is "'.print_r( $file, true ).'"' );
if (!$file) {
form_set_error('fid', t('Unable to access file or file is missing.'));
}
Eso es.
$form['#attributes']['enctype']
en Drupal 7. Se ocupa automáticamente