Thursday, February 25, 2010

How to Upload Files in CouchApp

‹prev | My Chain | next›

Yesterday I failed to get images to upload as CouchDB document attachments using couchapp. This should be do-able given that the Futon administration interface can do it. Just how easy it is remains to be seen...

...which turns out to be fairly easy!

I borrow code from two of CouchDB's javascript libraries:
  • futon.browse.js—defines how the Futon interface submits its file upload form
  • jquery.dialog.js—attaches actions to futon dialogs like the one that uploads files
What I end up with is this in my upload.html template:
<script src="/_utils/script/json2.js"></script>
<script src="/_utils/script/jquery.js?1.2.6"></script>
<script src="/_utils/script/jquery.couch.js?0.8.0"></script>
<script src="/_utils/script/jquery.form.js?0.9.0"></script>
<script src="<%= asset_path %>/vendor/couchapp/jquery.couchapp.js"></script>
<script type="text/javascript" charset="utf-8">
$("#recipe-upload").submit(function(e) { // invoke callback on submit
e.preventDefault();
var data = {};
$.each($("form :input").serializeArray(), function(i, field) {
data[field.name] = field.value;
});
$("form :file").each(function() {
data[this.name] = this.value; // file inputs need special handling
});

if (!data._attachments || data._attachments.length == 0) {
alert("Please select a file to upload.");
return;
}

$(this).ajaxSubmit({
url: "/<%= dbname %>/<%= docid %>",
success: function(resp) {
$('#saved').fadeIn().animate({ opacity: 1.0 },3000).fadeOut();
}
});
});
</script>
Piece by piece, this function disables normal form submission so that ajaxSubmit() can be used instead:
  e.preventDefault();
Next it assembles all of the data in the form into a Javascript object:
  var data = {};
$.each($("form :input").serializeArray(), function(i, field) {
data[field.name] = field.value;
});
$("form :file").each(function() {
data[this.name] = this.value; // file inputs need special handling
});
After checking to see if the data has an empty file upload field, it performs an ajaxSubmit using the current DB and document ID (as supplied by the show function) and defines a simple on-success callback function:
  $(this).ajaxSubmit({
url: "/<%= dbname %>/<%= docid %>",
success: function(resp) {
$('#saved').fadeIn().animate({ opacity: 1.0 },3000).fadeOut();
}
});
To test this out, I create an empty "test" document (more precisely I use this document from yesterday):



Then, I access the upload show function for the test document (http://localhost:5984/eee/_design/relax/_show/upload/test) and upload my favorite avatar image:



The "Saved" animation shows, which leads me to believe that the image has been successfully uploaded. To make sure, I check the document again:



Sure enough, the image is now attached to the CouchDB test document. Yay!

There is still some more that I would do if I wanted this to be ready for every day use—links back to the main edit page, an automatic redirect after upload, etc.—but that is good enough for today. I am just happy to know that it is possible with relatively little effort.

Day #25

3 comments:

  1. Small addition to a truly purposefull and informative blog entry. This tells the user that the update has failed- firebug console message of success and fail
    (these messages are within pre tags which are not allowed in your blog comments)

    {"ok":true,"id":"test","rev":"25-1223e10f2bd556a67665bc9a0ebae738"}

    {"error":"conflict","reason":"Document update conflict."}



    $(this).ajaxSubmit({
    url: "/<%= dbname %>/<%= docid %>",
    success: function(resp) {
    if(resp.match("ok")){ $('#saved').fadeIn().animate({ opacity: 1.0 },3000).fadeOut();}
    else if(resp.match("error")){ $('#failed').fadeIn().animate({ opacity: 1.0 },3000).fadeOut();}
    }

    ReplyDelete
  2. This is not working for me. I get the error: '{"error":"method_not_allowed","reason":"Only DELETE,GET,HEAD,PUT allowed"}'. Here is my entire html document which I put on couchdb.
    I was however forced to remove the usual HTML brackets because HTML is not allowed in comments for some reason.
    html
    head
    script src="http://code.jquery.com/jquery-1.8.2.min.js"/script
    script src="http://localhost:5984/_utils/script/jquery.js?1.2.6"/script
    script src="http://localhost:5984/_utils/script/jquery.couch.js?0.8.0"/script
    script src="http://localhost:5984/_utils/script/jquery.form.js?0.9.0"/script
    /head
    body
    form method="post"
    input type="file" id="recipe-upload"
    input type="submit"
    /form
    script type="text/javascript" charset="utf-8"
    $("#recipe-upload").submit(function(e) { // invoke callback on submit
    e.preventDefault();
    var data = {};
    $.each($("form :input").serializeArray(), function(i, field) {
    data[field.name] = field.value;
    });
    $("form :file").each(function() {
    data[this.name] = this.value; // file inputs need special handling
    });

    if (!data._attachments || data._attachments.length == 0) {
    alert("Please select a file to upload.");
    return;
    }

    $(this).ajaxSubmit({
    url: "/%= dbname %/%= docid %",
    success: function(resp) {
    $('#saved').fadeIn().animate({ opacity: 1.0 },3000).fadeOut();
    alert("success");
    }
    });
    });
    /script
    /body
    /html
    '

    ReplyDelete
  3. Here is a very simple example with good comments that you can load into your CouchDB. https://gist.github.com/4074427

    ReplyDelete