diff --git a/node_example/node_example.install b/node_example/node_example.install
index 84234e3..b926155 100755
--- a/node_example/node_example.install
+++ b/node_example/node_example.install
@@ -28,6 +28,7 @@ function node_example_schema() {
     'fields' => array(
       'vid'      => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
       'nid'      => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+      'fid'      => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
       'color'    => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
       'quantity' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
     ),
diff --git a/node_example/node_example.module b/node_example/node_example.module
index 6f45a60..aa06c02 100755
--- a/node_example/node_example.module
+++ b/node_example/node_example.module
@@ -9,10 +9,10 @@
  * (Note that custom node types are most often created with CCK in recent
  * versions of Drupal.)
  *
- * Our example node type will allow users to specify a "color" and a "quantity"
- * for their nodes; some kind of rudimentary inventory-tracking system, perhaps?
- * To store this extra information, we need an auxiliary database table as
- * defined in node_example_schema().
+ * Our example node type will allow users to specify a "color" a "quantity"
+ * and a file upload (txt or cvs) for their nodes; some kind of rudimentary
+ * inventory-tracking system, perhaps? To store this extra information, we
+ * need an auxiliary database table as defined in node_example_schema().
  */
 
 /**
@@ -162,6 +162,30 @@ function node_example_form(&$node, $form_state) {
     '#maxlength' => 10
   );
 
+  // Allow file upload.
+  $form['#attributes'] = array('enctype' => "multipart/form-data");
+  // We create a fieldset for the file upload since we need more than one
+  // field.
+  $form['file'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('File'),
+  );
+  $form['file']['file_upload'] = array(
+    '#type' => 'file',
+    '#title' => t('File upload'),
+    '#description' => t('Allowed extensions: txt and csv')
+  );
+  // We check to see if a value for file_upload is set, so users can only
+  // delete files that's actually there
+  if (isset($node->fid) && !empty($node->fid)) {
+    $filename = db_result(db_query("SELECT filename FROM {files} WHERE fid = %d", $node->fid));
+    $form['file']['file_delete'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Delete file: %filename', array('%filename' => $filename)),
+      '#description' => t('Check this, if you want to delete the current file'),
+    );
+  }
+
   return $form;
 }
 
@@ -174,12 +198,46 @@ function node_example_form(&$node, $form_state) {
  *
  * Errors should be signaled with form_set_error().
  */
-function node_example_validate($node, &$form) {
+function node_example_validate(&$node, &$form) {
   if ($node->quantity) {
     if (!is_numeric($node->quantity)) {
       form_set_error('quantity', t('The quantity must be a number.'));
     }
   }
+
+  // Validation for the file:
+  // First we save the uploaded file to the temporary dir while doing some
+  // validation. The first param used is the form name, the second is the
+  // validation we want. Here we only allow txt and csv files. The last param
+  // we pass the dir to save the file, we use Drupal to get the for from the
+  // file-system settings.
+  $file = file_save_upload('file_upload', array(
+    'file_validate_extensions' => array('txt csv'),
+  ), file_directory_path());
+
+  if ($file || (isset($node->file_delete) && $node->file_delete)) {
+    // Delete the old file, if it's set to be deleted, or a new one is uploaded.
+    // Load file object from database.
+    $old_file = db_fetch_object(db_query('SELECT * FROM {files} WHERE fid = %d', $node->file_upload));
+    // Remove the file.
+    if (file_exists($old_file->filepath)) {
+      file_delete($old_file->filepath);
+    }
+    // And remove it from the files table.
+    db_query('DELETE FROM {files} WHERE fid = %d', $fid);
+
+    if ($file) {
+      // Now get ready to do what's needed to save the file.
+      // Mark the file for permanent storage.
+      file_set_status($file, FILE_STATUS_PERMANENT);
+      // Update the files table.
+      drupal_write_record('files', $file, 'fid');
+      // Save the file id to save it later. In Drupal 6 there is a bug in the API
+      // that makes this impossible to do with the API see: http://drupal.org/node/241364
+      // Instead we save the file id in the user's session.
+      $_SESSION['node_example_' . $node->type . '_fid'] = $file->fid;
+    }
+  }
 }
 
 /**
@@ -189,7 +247,10 @@ function node_example_validate($node, &$form) {
  * database inserts.
  */
 function node_example_insert($node) {
-  db_query("INSERT INTO {node_example} (vid, nid, color, quantity) VALUES (%d, %d, '%s', %d)", $node->vid, $node->nid, $node->color, $node->quantity);
+  $fid = isset($_SESSION['node_example_' . $node->type . '_fid']) ? $_SESSION['node_example_' . $node->type . '_fid'] : 0;
+  // Unset the variable so it doesn't give trouble later.
+  unset($_SESSION['node_example_' . $node->type . '_fid']);
+  db_query("INSERT INTO {node_example} (vid, nid, color, quantity, fid) VALUES (%d, %d, '%s', %d, %d)", $node->vid, $node->nid, $node->color, $node->quantity, $fid);
 }
 
 /**
@@ -199,12 +260,23 @@ function node_example_insert($node) {
  * database updates.
  */
 function node_example_update($node) {
+  $fid = isset($_SESSION['node_example_' . $node->type . '_fid']) ? $_SESSION['node_example_' . $node->type . '_fid'] : 0;
+  unset($_SESSION['node_example_fid']);
+  // If delete is set, set fid to 0.
+  if ($node->file_delete) {
+    $node->fid = 0;
+  }
+  // If there is a new file uploaded, save this.
+  if (!empty($fid)) {
+    $node->fid = $fid;
+  }
+
   // if this is a new node or we're adding a new revision,
   if ($node->revision) {
     node_example_insert($node);
   }
   else {
-    db_query("UPDATE {node_example} SET color = '%s', quantity = %d WHERE vid = %d", $node->color, $node->quantity, $node->vid);
+    db_query("UPDATE {node_example} SET color = '%s', quantity = %d, fid = %d WHERE vid = %d", $node->color, $node->quantity, $node->fid, $node->vid);
   }
 }
 
@@ -242,7 +314,7 @@ function node_example_delete($node) {
  * every time a node is loaded, and allows us to do some loading of our own.
  */
 function node_example_load($node) {
-  $additions = db_fetch_object(db_query('SELECT color, quantity FROM {node_example} WHERE vid = %d', $node->vid));
+  $additions = db_fetch_object(db_query('SELECT color, quantity, fid FROM {node_example} WHERE vid = %d', $node->vid));
   return $additions;
 }
 
@@ -286,6 +358,10 @@ function node_example_theme() {
 function theme_node_example_order_info($node) {
   $output = '<div class="node_example_order_info">';
   $output .= t('The order is for %quantity %color items.', array('%quantity' => check_plain($node->quantity), '%color' => check_plain($node->color)));
+  if ($node->fid) {
+    $file = db_fetch_object(db_query("SELECT filename, filepath FROM {files} WHERE fid = %d", $node->fid));
+    $output .= '<br />' . l(t('Download file: %filename', array('%filename' => $file->filename)), $file->filepath, array('html' => TRUE));
+  }
   $output .= '</div>';
   return $output;
 }
