diff --git a/resources/node_resource.inc b/resources/node_resource.inc
index 928e09d..386b351 100644
--- a/resources/node_resource.inc
+++ b/resources/node_resource.inc
@@ -114,6 +114,112 @@ function _node_resource_definition() {
           'access arguments' => array('access content'),
         ),
       ),
+      'actions' => array(
+        'presave_file' => array(
+          'help' => 'Create temporary file(s) from a "multipart/form-data" posted form data. Must be attached to a node later or Drupal will automatically delete it after 6 hours. Requires: node_type and field_name.',
+          'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'),
+          'callback' => '_node_resource_presave_file',
+          'access callback' => '_node_resource_validate_node_type_field_name',
+          'access arguments' => array('create'),
+          'access arguments append' => TRUE,
+          'args' => array(
+            array(
+              'name' => 'node_type',
+              'optional' => TRUE,
+              'type' => 'string',
+              'description' => 'The node type\'s machine_name that the field_name is associated with.',
+              'default value' => NULL,
+              'source' => array('data' => 'node_type'),
+              'optional' => FALSE,
+            ),
+            array(
+              'name' => 'field_name',
+              'optional' => TRUE,
+              'type' => 'string',
+              'description' => 'The machine name of the file field associated with the node type.',
+              'default value' => NULL,
+              'source' => array('data' => 'field_name'),
+              'optional' => FALSE,
+            ),
+          ),
+        ),
+        'presave_filedata' => array(
+          'help' => 'Creates a temporary file from base64 encoded data. Must be attached to a node later or Drupal will automatically delete it after 6 hours. Requires: node_type, field_name, file_name, file_data.',
+          'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'),
+          'callback' => '_node_resource_presave_filedata',
+          'access callback' => '_node_resource_validate_node_type_field_name',
+          'access arguments' => array('create'),
+          'access arguments append' => TRUE,
+          'args' => array(
+            array(
+              'name' => 'node_type',
+              'optional' => TRUE,
+              'type' => 'string',
+              'description' => 'The node type\'s machine_name that the field_name is associated with.',
+              'default value' => NULL,
+              'source' => array('data' => 'node_type'),
+            ),
+            array(
+              'name' => 'field_name',
+              'optional' => TRUE,
+              'type' => 'string',
+              'description' => 'The machine name of the file field associated with the node type.',
+              'default value' => NULL,
+              'source' => array('data' => 'field_name'),
+            ),
+            array(
+              'name' => 'file_name',
+              'optional' => TRUE,
+              'type' => 'string',
+              'description' => 'A string containing a filename that the file will be saved to.',
+              'default value' => NULL,
+              'source' => array('data' => 'file_name'),
+            ),
+            array(
+              'name' => 'file_data',
+              'optional' => TRUE,
+              'type' => 'string',
+              'description' => 'The base64 encoded data.',
+              'default value' => NULL,
+              'source' => array('data' => 'file_data'),
+            ),
+          ),
+        ),
+      ),
+      'targeted_actions' => array(
+        'attach_file' => array(
+          'help' => 'Upload and attach file(s) to a node. POST multipart/form-data to node/123/attach_file',
+          'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'),
+          'callback' => '_node_resource_attach_file',
+          'access callback' => '_node_resource_access',
+          'access arguments' => array('update'),
+          'access arguments append' => TRUE,
+          'args' => array(
+            array(
+              'name' => 'nid',
+              'optional' => FALSE,
+              'source' => array('path' => 0),
+              'type' => 'int',
+              'description' => 'The nid of the node to attach a file to',
+            ),
+            array(
+              'name' => 'field_name',
+              'optional' => FALSE,
+              'source' => array('data' => 'field_name'),
+              'description' => 'The file parameters',
+              'type' => 'string',
+            ),
+            array(
+              'name' => 'attach',
+              'optional' => TRUE,
+              'source' => array('data' => 'attach'),
+              'description' => 'Attach the file(s) to the node. If FALSE, this clears ALL files attached, and attaches the files',
+              'type' => 'int',
+              'default value' => TRUE,
+            ),
+          ),
+        ),
+      ),
       'relationships' => array(
         'files' => array(
           'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/node_resource'),
@@ -534,3 +640,262 @@ function _node_resource_load_node_comments($nid, $count = 0, $start = 0) {
 
   return !empty($cids) ? comment_load_multiple($cids) : array();
 }
+
+/**
+ * Attaches or overwrites file(s) to an existing node.
+ *
+ * Example form element used to post files to attach_file:
+ * <form action="site.com/endpoint/node/1234/attach_file" method="post"
+ * enctype="multipart/form-data">
+ * <input name="files[anything1]" type="file" />
+ * <input name="files[anything2]" type="file" />
+ * <input name="field_name" type="text" value="field_image" />
+ * <input name="attach" type="text" value="0" />
+ *
+ * The name="files[anything]" format is required to use file_save_upload().
+ *
+ * @param $nid
+ *   Node ID of the node the file(s) is being attached to.
+ * @param $field_name
+ *   Machine name of the field that is attached to the node.
+ * @param $attach
+ *   Optional. Defaults to true. This means that files will be attached to the
+ *   node, alongside existing files. If the maximum number of files have already
+ *   been uploaded to this node an error is given.
+ *   If false, it removes the files, and attaches the new files uploaded.
+ * @return
+ *   An array of files that were attached in the form:
+ *   array( array( fid=>N, uri=>http://site.com/endpoint/file/N), array( fid=>N,
+ *   uri=>http://site.com/endpoint/file/N) )
+ *
+ * @see file_save_upload()
+ * @see file
+ */
+function _node_resource_attach_file($nid, $field_name, $attach = TRUE) {
+  $node = node_load($nid);
+  $node_type=$node->type;
+
+  if (empty( $node->{$field_name}['und'] )) {
+    $node->{$field_name}['und'] = array();
+  }
+
+  _node_resource_validate_node_type_field_name('update', array($node_type, $field_name));
+
+  $counter = 0;
+  if ( $attach ) {
+    $counter = count($node->{$field_name}['und']);
+  }
+  else {
+    $node->{$field_name}['und'] = array();
+  }
+
+  $options = array('attach' => $attach, 'file_count' => $counter);
+
+  list($files, $file_objs) = _node_resource_file_save_upload($node_type, $field_name, $options);
+
+  foreach ($file_objs as $file_obj) {
+    $node->{$field_name}['und'][$counter++] = (array)$file_obj;
+  }
+
+  node_save($node);
+
+  return $files;
+}
+
+/**
+ * Uses a node's file field to save a file which moves the file to be saved to
+ * the appropriate folder.
+ *
+ * Creates a temporary file(s) from a "multipart/form-data" posted form. Must
+ * be attached to a node later. Otherwise Drupal will automatically DELETE it
+ * after 6 hours. Requires: node_type and field_name.
+ *
+ * @param $node_type
+ *   The unique machine name identifier for a node type.
+ * @param $field_name
+ *   Machine name of the file field on the above node type.
+ * @return
+ *   An array of the file(s) saved.
+ */
+function _node_resource_presave_file($node_type, $field_name) {
+  list($files, $file_objs) = _node_resource_file_save_upload($node_type, $field_name);
+  return $files;
+}
+
+/**
+ * Creates a temporary file from a base64 encoded form post. It must be attached
+ * to a node later. Otherwise Drupal will automatically DELETE it after 6 hours.
+ *
+ * Requires: node_type and field_name.
+ *
+ * @param $node_type
+ *   The unique machine name identifier for a node type.
+ * @param $field_name
+ *   Machine name of the file field on the above node type.
+ * @param $file_name
+ *   The filename the data should be stored into.
+ * @param $file_data
+ *   The base64 encoded data of the file.
+ * @return
+ *   An array of the file(s) saved.
+ */
+function _node_resource_presave_filedata($node_type, $field_name, $file_name, $file_data) {
+  // The field_name on node_type was checked in the access callback.
+  $instance = field_info_instance('node', $field_name, $node_type);
+  $field = field_read_field($field_name);
+  $field_uri = file_field_widget_uri($field, $instance);
+
+  if (!$temp_file = file_save_data(base64_decode($file_data), 'temporary://', FILE_EXISTS_RENAME)) {
+    return services_error(t("Could not write file to temporary destination."), 500);
+  }
+
+  // Check if destination folder exists.
+  if (isset($field_uri) && !file_prepare_directory($field_uri, FILE_CREATE_DIRECTORY)) {
+    return services_error(t('The upload directory %directory for the file field !name could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', array('%directory' => $field_uri, '!name' => $field_name)));
+  }
+
+  $file_name = str_replace('/', '_', $file_name);
+  // Use file_create_filename to check the file_name for bad characters.
+  $file_name = drupal_basename( file_create_filename($file_name, ''));
+  $file_name = file_munge_filename($file_name, $instance['settings']['file_extensions'], FALSE);
+
+
+  $destination = $field_uri;
+  // Add slash if needed (public:// vs public://path/to/folder).
+  $destination .= substr($destination, -1) != '/' ? '/' : '';
+  $destination .= $file_name;
+
+  // Building file object that is ONLY used for file_validate();
+  $temp_validate = new stdClass();
+  $temp_validate->filename = drupal_basename($destination);
+  $temp_validate->uri      = $destination;
+  $temp_validate->filemime = file_get_mimetype($temp_validate->filename);
+  $temp_validate->filesize = $temp_file->filesize;
+
+  $validators = array(
+    'file_validate_extensions' => array(0 => $instance['settings']['file_extensions']),
+    'file_validate_size' => array( 0 => parse_size($instance['settings']['max_filesize'])),
+  );
+
+  $errors = file_validate($temp_validate, $validators);
+  // Check for errors.
+  if (!empty($errors)) {
+    return services_error(t('The specified file %name failed validation. ' . implode(" ", $errors), array('%name' => $file_name)));
+  }
+
+  $file = file_move($temp_file, $destination, FILE_EXISTS_RENAME);
+
+  $files = array();
+  if ($file->fid) {
+    drupal_chmod($destination);
+    // Add info to the array that will be returned/encoded to xml/json.
+    $files[] = array(
+      'fid' => $file->fid,
+      'uri' => services_resource_uri(array('file', $file->fid)),
+    );
+  }
+  else {
+    return services_error(t('An unknown error occurred'), 500);
+  }
+
+  return $files;
+}
+
+/**
+ * Services wrapper for file_save_upload.
+ *
+ * @see file_save_upload()
+ * @see file_managed_file_save_upload()
+ */
+function _node_resource_file_save_upload($node_type, $field_name, $options= array() ) {
+  // The field_name on node_type should be checked in the access callback.
+  $instance = field_info_instance('node', $field_name, $node_type);
+  $field = field_read_field($field_name);
+  $cardinality = $field['cardinality'];
+
+  // If cardinality is unlimited, don't bother checking for space.
+  if (( $cardinality > 0 ) && isset( $options['attach'] ) && ($options['attach']) && isset( $options['file_count'] )) {
+    $file_count = $options['file_count'];
+    if ( $file_count == $cardinality ) {
+      return services_error(t('This node has the maximum number of files.'));
+    }
+  }
+
+  $destination = file_field_widget_uri($field, $instance );
+  if (isset($destination) && !file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) {
+    return services_error(t('The upload directory %directory for the file field !name could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', array('%directory' => $destination, '!name' => $field_name)));
+  }
+
+  $validators = array(
+    'file_validate_extensions' =>  (array)$instance['settings']['file_extensions'],
+    'file_validate_size' => array( 0 => parse_size($instance['settings']['max_filesize']) ),
+  );
+
+  $files = $file_objs = array();
+  $counter = 0;
+
+  foreach ($_FILES['files']['error'] as $key => $val) {
+
+    // Let the file module handle the upload and moving.
+    if (!$file = file_save_upload($key, $validators, $destination, FILE_EXISTS_RENAME) ) {
+      return services_error(t('Failed to upload file. @upload', array('@upload' => $key)), 406);
+    }
+
+    if ($file->fid) {
+      // Add info to the array that will be returned/encdoed to xml/json.
+      $files[] = array(
+        'fid' => $file->fid,
+        'uri' => services_resource_uri(array('file', $file->fid)),
+      );
+      $file_objs[] = $file;
+    }
+    else {
+      return services_error(t('An unknown error occurred'), 500);
+    }
+
+    $counter++;
+
+    // If attaching files, give an error if trying to attach too many.
+    if (isset($file_count) && ($counter + $file_count > $cardinality)) {
+      return services_error(t('The maximum number of files for this node has been reached.'));
+    }
+    // If the  maximum number of files for this field has been reached, stop.
+    if ($cardinality == $counter) {
+      break;
+    }
+  }
+
+  return array( $files, $file_objs);
+}
+
+/**
+ *  Helper function to validate data for presave_file and presave_filedata.
+ *
+ * @param $op
+ *   Array representing the attributes a node edit form would submit.
+ * @param $args
+ *   Resource arguments passed through from the original request (node_type,
+ *   field_name).
+ * @return bool
+ *   TRUE/FALSE based on access.
+ */
+function _node_resource_validate_node_type_field_name($op = 'create', $args = array()) {
+  $node_type = $args[0];
+  $field_name = $args[1];
+
+  $temp_node= array('type' => $node_type);
+
+  // Validate access to the node type.
+  if (!node_access($op, $node_type)) {
+    return FALSE;
+  }
+
+  // An invalid node type throws an exception, and stops before the return below.
+  _node_resource_validate_type( $temp_node );
+
+  if (!field_info_instance('node', $field_name, $node_type)) {
+    return services_error(t('Field name \'@field_name\' not found on node type \'@node_type\'', array('@field_name' => $field_name, '@node_type' => $node_type)), 406);
+  }
+
+  return TRUE;
+}
