This is copied from https://github.com/xurizaemon/csvimport/blob/master/csvimport.module
Author is Chris Burgess
Thanks to him!
<?php
/** * Demonstration module. * * - Provide form for upload of a CSV file. * - On submission, trigger a batch task which iterates through each row in the file. *//** * Implement hook_menu() */function csvimport_menu() { $items['admin/content/csv_import'] = array('title' => 'Import CSV',
'description' => 'Import content from a <abbr title="Comma Separated Values">CSV</abbr> or <abbr title="Tab Separated Values">TSV</abbr> file.',
'access callback' => 'user_access',
'access arguments' => array('access site administration'),
'page callback' => 'drupal_get_form',
'page arguments' => array('csvimport_form'),
// 'file' => 'csvimport.admin.inc',);
return $items ;}
/** * Build a form to upload CSV to. */function csvimport_form() { $form['#attributes'] = array('enctype' => 'multipart/form-data'
);
$form['csvfile'] = array( '#title' => t('CSV File'),
'#type' => 'file',
'#description' => ($max_size = parse_size(ini_get('upload_max_filesize'))) ? t('Due to server restrictions, the <strong>maximum upload file size is !max_size</strong>. Files that exceed this size will be disregarded.', array('!max_size' => format_size($max_size))) : '',
) ;
$form['submit'] = array('#type' => 'submit',
'#value' => t('Commence Import'),
) ;
$form['#validate'] = array( 'csvimport_validate_fileupload', 'csvimport_form_validate',) ;
return $form ;}
/** * Validate the file upload. It must be a CSV, and we must * successfully save it to our import directory. */function csvimport_validate_fileupload(&$form, &$form_state) {$validators = array(
'file_validate_extensions' => array( 'csv' ),
) ;
if ( $file = file_save_upload('csvfile', $validators, file_directory_path()) ) {
// The file was saved using file_save_upload() and was added to // the files table as a temporary file. We'll make a copy and let // the garbage collector delete the original upload.$directory = file_create_path(variable_get('csvimport','csvimport')) ;
if ( file_check_directory($directory, FILE_CREATE_DIRECTORY) ) { $destination = $directory .'/' . $file->filename; if (file_copy($file, $destination, FILE_EXISTS_REPLACE)) {$form_state['values']['csvupload'] = $destination;
}
else {form_set_error('csvimport', t('Unable to copy upload file to !dest', array('!dest' => $destination)));
}
}
}
}
/** * Validate the upload. Ensure that the CSV looks something like we * expect it to. */function csvimport_form_validate(&$form, &$form_state) {if ( isset( $form_state['values']['csvupload'] ) ) {
if ( $handle = fopen($form_state['values']['csvupload'], 'r') ) {
$line_count = 1 ;
$first = TRUE ;
if ( $line = fgetcsv($handle, 4096) ) { /** * Validate the uploaded CSV here. * * The example CSV happens to have cell A1 ($line[0]) as * below; we validate it only. * * You'll probably want to check several headers, eg: * if ( $line[0] == 'Index' || $line[1] != 'Supplier' || $line[2] != 'Title' ) */ // if ( $line[0] != 'Example CSV for csvimport.module - http://github.com/xurizaemon/csvimport' ) { // form_set_error('csvfile', t('Sorry, this file does not match the expected format.')) ; // }}
fclose($handle);
}
else {form_set_error('csvfile', t('Unable to read uploaded file !filepath', array('!filepath' => $form_state['values']['csvupload'])));
}
}
}
/** * Handle form submission. Read the CSV into a set of batch operations * and fire them off. */function csvimport_form_submit(&$form, &$form_state) {$batch = array(
'title' => t('Importing CSV ...'),
'operations' => array(),'init_message' => t('Commencing'),
'progress_message' => t('Processed @current out of @total.'),
'error_message' => t('An error occurred during processing'),
'finished' => 'csvimport_import_finished',
) ;
if ( isset( $form_state['values']['csvupload'] ) ) {
if ( $handle = fopen($form_state['values']['csvupload'], 'r') ) {
$batch['operations'][] = array('_csvimport_remember_filename', array( $form_state['values']['csvupload'] ) ) ;
$line_count = 1 ;
$first = TRUE ;
$line = fgetcsv($handle, 4096);
while ( $line = fgetcsv($handle, 4096) ) { /** * we use base64_encode to ensure we don't overload the batch * processor by stuffing complex objects into it */$batch['operations'][] = array('_csvimport_import_line', array(array_map('base64_encode', $line)));
}
fclose($handle);
} // we caught this in csvimport_form_validate() } // we caught this in csvimport_form_validate()batch_set($batch);
}
/** * Handle batch completion. */function csvimport_import_finished($success, $results, $operations) {if ( !empty($results['failed_rows']) ) {
$dir = file_directory_path() . '/csvimport/' ; if ( file_check_directory( $dir, FILE_CREATE_DIRECTORY ) ) {$csv_filename = 'failed_rows-'. basename($results['uploaded_filename']); // we validated extension on upload
$csv_filepath = $dir .'/'. $csv_filename;$targs = array(
'!csv_url' => l(check_plain($csv_filename), $csv_filepath), '%csv_filename' => $csv_filename, '%csv_filepath' => $csv_filepath,) ;
if ( $handle = fopen($csv_filepath, 'w+') ) {
foreach( $results['failed_rows'] as $failed_row ) {
fputcsv($handle, $failed_row);
}
fclose($handle);
drupal_set_message(t('Some rows failed to import. You may download a CSV of these rows: !csv_url', $targs), 'error');
}
else {drupal_set_message(t('Some rows failed to import, but unable to write error CSV to %csv_filepath', $targs), 'error');
}
}
else {drupal_set_message(t('Some rows failed to import, but unable to create directory for error CSV at %csv_directory', $targs), 'error');
}
}
return t('The CSV import has completed.');
}
/** * Remember the uploaded CSV filename * * @TODO is there a better way to pass a value from inception of the * batch to the finished function? */function _csvimport_remember_filename($filename, &$context) {$context['results']['uploaded_filename'] = $filename ;
}
/** * Process a single line. */function _csvimport_import_line($line, &$context) {$context['results']['rows_imported']++;
$line = $cleaned_line = array_map('base64_decode', $line); /** * Simply show the import row count. */$context['message'] = t('Importing row !c', array( '!c' => $context['results']['rows_imported'] ));
/** * Alternatively, our example CSV happens to have the title in the * third column, so we can uncomment this line to display "Importing * Blahblah" as each row is parsed. * * You can comment out the line above if you uncomment this one. */$context['message'] = t('Importing %title', array('%title' => $line[2]));
/** * In order to slow importing and debug better, we can uncomment * this line to make each import slightly slower. */usleep(2500);
/** * If the first two columns in the row are "ROW", "FAILS" then we * will add that row to the CSV we'll return to the importing person * after the import completes. */if ( $line[1] == 'ROW' && $line[2] == 'FAILS' ) {
$context['results']['failed_rows'][] = $line ;
}
}




