diff --git a/imagefield_zip.module b/imagefield_zip.module
index dc1004c..db61fce 100644
--- a/imagefield_zip.module
+++ b/imagefield_zip.module
@@ -222,32 +222,53 @@ function _imagefield_zip_cck_walker(&$array, &$cck_field, &$zip_field) {
  *   array of image file objects.
  */
 function imagefield_zip_save_and_extract_upload($field_name) {
-  // File validation array for file_save_upload()
-  //  Has a .zip file extension.
-  //  Is valid zip.
-  $validators = array(
-    'file_validate_extensions' => array('zip'),
-    'imagefield_zip_is_valid_zip' => array(),
-  );
+  $files_array = imagefield_zip_files_array_extractor($field_name);
 
   $images = array();
-  if ($file = file_save_upload($field_name, $validators)) {
-    // Extract all files.
-    $extracted = imagefield_zip_extract($file->filepath);
-    if (!empty($extracted)) {
-      // Only select valid image files
-      $images = array_filter($extracted, '_imagefield_zip_is_archived_image');
+  foreach ($files_array as $file_array) {
+    if (substr($file_array['files']['name'][$field_name], -4) === '.zip') {
+      // File validation array for file_save_upload()
+      //  Has a .zip file extension.
+      //  Is valid zip.
+      $validators = array(
+        'file_validate_extensions' => array('zip'),
+        'imagefield_zip_is_valid_zip' => array(),
+      );
     }
     else {
-      watchdog('imagefield_zip', 'Unpacked archive appears to be empty.', array(), WATCHDOG_WARNING);
+      $validators = array();
     }
 
-    // Delete the zip file as we extracted and used everything we wanted from it.
-    imagefield_zip_delete_file($file);
-  }
-  else {
-    watchdog('imagefield_zip', 'Failed to save uploaded file', array(), WATCHDOG_ERROR);
+    $file = imagefield_zip_file_save_upload($field_name, $validators, FALSE, FILE_EXISTS_RENAME, $file_array);
+    if ($file) {
+      $extracted = array();
+      // Extract all files.
+      if (substr($file->filepath, -4) === '.zip') {
+        $extracted = imagefield_zip_extract($file->filepath);
+      }
+      else {
+        $extracted[] = $file;
+      }
+
+      if (!empty($extracted)) {
+        // Only select valid image files
+        $new_images = array_filter($extracted, '_imagefield_zip_is_archived_image');
+        $images = array_merge($images, $new_images);
+      }
+      else {
+        watchdog('imagefield_zip', 'Unpacked archive appears to be empty.', array(), WATCHDOG_WARNING);
+      }
+
+      if (substr($file->filepath, -4) === '.zip') {
+        // Delete the zip file as we extracted and used everything we wanted from it.
+        imagefield_zip_delete_file($file);
+      }
+    }
+    else {
+      watchdog('imagefield_zip', 'Failed to save uploaded file', array(), WATCHDOG_ERROR);
+    }
   }
+
   return $images;
 }
 
@@ -445,12 +466,13 @@ function _imagefield_zip_form(&$form, &$field_name, &$node, &$groups) {
   $form[$field_name .'_zip'][$field_name . '_upload'] = array(
     '#type' => 'file',
     '#title' => t('Zip Upload for @name', array('@name' => $field_name)),
-    '#name' => "files[{$field_name}_zip]",
+    '#name' => "files[{$field_name}_zip][]",
     '#description' => t('To upload multiple images at a time, attach a ZIP file here. The zip must contain only the allowed image formats. Maximum Filesize: @max_filesize', array('@max_filesize' => $max_filesize)),
     '#attributes' => array(
       'accept' => 'zip'
     ),
     '#element_validate' => array('_imagefield_zip_element_validate'),
+    '#attributes' => array('multiple' => 'multiple'),
   );
 
   // Code adapted from content_multiple_value_form().
@@ -729,3 +751,183 @@ function imagefield_zip_mkdir_recursive($pathname, $mode = 0777) {
   }
   return @mkdir($pathname, $mode);
 }
+
+/**
+ * Saves a file upload to a new location.
+ *
+ * The source file is validated as a proper upload and handled as such.
+ * The file will be added to the files table as a temporary file. Temporary
+ * files are periodically cleaned. To make the file permanent file call
+ * file_set_status() to change its status.
+ *
+ * @param $source
+ *   A string specifying the name of the upload field to save.
+ * @param $validators
+ *   (optional) An associative array of callback functions used to validate the
+ *   file. The keys are function names and the values arrays of callback
+ *   parameters which will be passed in after the file object. The
+ *   functions should return an array of error messages; an empty array
+ *   indicates that the file passed validation. The functions will be called in
+ *   the order specified.
+ * @param $dest
+ *   A string containing the directory $source should be copied to. If this is
+ *   not provided or is not writable, the temporary directory will be used.
+ * @param $replace
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE: Replace the existing file.
+ *   - FILE_EXISTS_RENAME: Append _{incrementing number} until the filename
+ *     is unique.
+ *   - FILE_EXISTS_ERROR: Do nothing and return FALSE.
+ * @param $file_array
+ *   Emulate $_FILES array.
+ *
+ * @return
+ *   An object containing the file information, or 0 in the event of an error.
+ */
+function imagefield_zip_file_save_upload($source, $validators = array(), $dest = FALSE, $replace = FILE_EXISTS_RENAME, $file_array = array()) {
+  global $user;
+
+  // Add in our check of the the file name length.
+  $validators['file_validate_name_length'] = array();
+
+  // Return 0 if the file is not there.
+  if (   empty($file_array)
+      || !isset($file_array['files']['name'][$source])
+      || !is_uploaded_file($file_array['files']['tmp_name'][$source])
+        ) {
+    return 0;
+  }
+
+  // Check for file upload errors and return FALSE if a
+  // lower level system error occurred.
+  switch ($file_array['files']['error'][$source]) {
+    // @see http://php.net/manual/en/features.file-upload.errors.php
+    case UPLOAD_ERR_OK:
+      break;
+
+    case UPLOAD_ERR_INI_SIZE:
+    case UPLOAD_ERR_FORM_SIZE:
+      drupal_set_message(t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $source, '%maxsize' => format_size(file_upload_max_size()))), 'error');
+      return 0;
+
+    case UPLOAD_ERR_PARTIAL:
+    case UPLOAD_ERR_NO_FILE:
+      drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $source)), 'error');
+      return 0;
+
+      // Unknown error
+    default:
+      drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $source)), 'error');
+      return 0;
+  }
+
+  // Build the list of non-munged extensions.
+  // @todo: this should not be here. we need to figure out the right place.
+  $extensions = '';
+  foreach ($user->roles as $rid => $name) {
+    $extensions .= ' '. variable_get("upload_extensions_$rid",
+    variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
+  }
+
+  // Begin building file object.
+  $file = new stdClass();
+  $file->filename = file_munge_filename(trim(basename($file_array['files']['name'][$source]), '.'), $extensions);
+  $file->filepath = $file_array['files']['tmp_name'][$source];
+  $file->filemime = file_get_mimetype($file->filename);
+
+  // If the destination is not provided, or is not writable, then use the
+  // temporary directory.
+  if (empty($dest) || file_check_path($dest) === FALSE) {
+    $dest = file_directory_temp();
+  }
+
+  $file->source = $source;
+  $file->destination = file_destination(file_create_path($dest .'/'. $file->filename), $replace);
+  $file->filesize = $file_array['files']['size'][$source];
+
+  // Call the validation functions.
+  $errors = array();
+  foreach ($validators as $function => $args) {
+    array_unshift($args, $file);
+    // Make sure $file is passed around by reference.
+    $args[0] = &$file;
+    $errors = array_merge($errors, call_user_func_array($function, $args));
+  }
+
+  // Rename potentially executable files, to help prevent exploits.
+  if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
+    $file->filemime = 'text/plain';
+    $file->filepath .= '.txt';
+    $file->filename .= '.txt';
+    // As the file may be named example.php.txt, we need to munge again to
+    // convert to example.php_.txt, then create the correct destination.
+    $file->filename = file_munge_filename($file->filename, $extensions);
+    $file->destination = file_destination(file_create_path($dest .'/'. $file->filename), $replace);
+  }
+
+
+  // Check for validation errors.
+  if (!empty($errors)) {
+    $message = t('The selected file %name could not be uploaded.', array('%name' => $file->filename));
+    if (count($errors) > 1) {
+      $message .= '<ul><li>'. implode('</li><li>', $errors) .'</li></ul>';
+    }
+    else {
+      $message .= ' '. array_pop($errors);
+    }
+    form_set_error($source, $message);
+    return 0;
+  }
+
+  // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary directory.
+  // This overcomes open_basedir restrictions for future file operations.
+  $file->filepath = $file->destination;
+  if (!move_uploaded_file($file_array['files']['tmp_name'][$source], $file->filepath)) {
+    form_set_error($source, t('File upload error. Could not move uploaded file.'));
+    watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->filepath));
+    return 0;
+  }
+
+  // If we made it this far it's safe to record this file in the database.
+  $file->uid = $user->uid;
+  $file->status = FILE_STATUS_TEMPORARY;
+  $file->timestamp = time();
+  drupal_write_record('files', $file);
+
+  return $file;
+}
+
+/**
+ * Extract each file uploaded to make it look like a single file.
+ *
+ * @param $field_name
+ *   A string specifying the name of the upload field.
+ *
+ * @return
+ *   An array containing an emulation of a single file in the $_FILES array.
+ */
+function imagefield_zip_files_array_extractor($field_name) {
+  $files_array = array();
+  if (!is_array($_FILES['files']['name'][$field_name]) && $_FILES['files']['name'][$field_name] != 'array') {
+    $files_array[] = $_FILES;
+  }
+  else {
+    foreach ($_FILES['files'] as $key_name => $values) {
+      foreach ($values as $key_field_name => $info) {
+        if ($key_field_name != $field_name) {
+          continue;
+        }
+
+        if (is_array($info)) {
+          foreach ($info as $delta => $value) {
+            $files_array[$delta]['files'][$key_name][$key_field_name] = $value;
+            if ($key_name == 'orig_name' && empty($files_array[$delta]['files']['name'][$key_field_name])) {
+              $files_array[$delta]['files']['name'][$key_field_name] = $value;
+            }
+          }
+        }
+      }
+    }
+  }
+  return $files_array;
+}
