File uploads

mistermojo edited this page Nov 12, 2012 · 8 revisions

It's easy to do streaming file-uploads with Geddy. This is a quick example using the scaffold app generated by geddy app.

Set up

Create an app:

$ geddy app uploader_app
$ cd uploader_app

Create an "uploads" directory:

$ mkdir public/uploads

Install the Formidable library (https://github.com/felixge/node-formidable) for parsing multi-part form posts:

npm install formidable

Add an upload route. Add this line in config/router.js under the default route for Main/index:

router.match('/upload').to({controller: 'Main', action: 'upload'});

Edit code

Add the upload form to the view in app/views/main/index.html.ejs:

<div class="hero-unit">
  <%  if (uploadedFile) { %>
  <p>Successfully uploaded
    <a href="/uploads/<%= uploadedFile; %>">
      <%= uploadedFile; %>
    </a>
  </p>
  <p><a href="/">Upload another file</a></p>
  <%  }
      else { %>
  <h3>Upload a file</h3>

  <form action="/upload" method="post" enctype="multipart/form-data">
    <div><input type="file" name="name" /></div>
    <div><input type="submit" value="Upload" /></div>
  </form>
  <%  } %>
</div>

Add the controller code for handling the upload post into app/controllers/main.js:

var formidable = require('formidable')
  , fs = require('fs')
  , path = require('path');

var Main = function () {

  this.index = function (req, resp, params) {
    var data = {
          // Pass the filename down, if this is a redirect from upload
          uploadedFile: params.uploaded_file
        }
      , opts = {
          format: 'html'
        , template: 'app/views/main/index'
        };
    this.respond(data, opts);
  };

  this.upload = function (req, resp, params) {
    var self = this
      , form = new formidable.IncomingForm()
      , uploadedFile
      , savedFile;

    // Handle each part of the multi-part post
    form.onPart = function (part) {
      // Handle each data chunk as data streams in
      part.addListener('data', function (data) {
        // Initial chunk, set the filename and create the FS stream
        if (!uploadedFile) {
          uploadedFile = encodeURIComponent(part.filename);
          savedFile = fs.createWriteStream(path.join('public', 'uploads', uploadedFile));
        }
        // Write each chunk to disk
        savedFile.write(data);
      });
      // The part is done
      part.addListener('end', function () {
        var err;
        // If everything went well, close the FS stream
        if (uploadedFile) {
          savedFile.end();
        }
        // Something went wrong
        else {
          err = new Error('Something went wrong in the upload.');
          self.error(err);
        }
      });
    };

    // Multi-part form is totally done, redirect back to index
    // and pass filename
    form.addListener('end', function () {
      self.redirect('/?uploaded_file=' + uploadedFile);
    });

    // Do it
    form.parse(req);
  };
};

exports.Main = Main;

Upload stuff

Start up the server and start uploading stuff.