In a previous post I outlined how easy it is to upload files using Dojo's pluggable I/O system. By including the IframeIO package, Dojo makes sending forms containing file upload elements is just as easy as making any other Ajax request.
But what about when you want to send data as though it were a file being uploaded, but you don't actually want to pull a file off the disk? Eugene Lazutkin needed exactly this and worked up patches which are now a part of Dojo. Admittedly it's not something you need all the time. After all, you've been making due without it for years, right? But there are many situations where being able to treat the browser as a file editor for plain-text content makes sense, and sending content pre-encoded as files can help.
The default XMLHTTP transport class supports this unique feature through the file
argument. The system expects file
to be an object with 3 properties:
name
- the filename
contentType
- optional, defaults to "application/octet-stream"
content
- the content of your file as a string
From here it's easy to construct a dojo.io.bind()
request that uploads some content as a file:
dojo.io.bind({
url: "upload.cgi",
// we expect to get JSON back from this CGI
mimetype: "text/json",
// handle the returned JSON
load: function(type, data, evt){ },
// normal properties
content: {
foo: "bar",
baz: "thud"
},
// our file content
file: {
name: "upload.txt",
contentType: "plain/text",
content: "look ma! no form node!"
}
});
From here all of the mime-encoding magic is handled for you.
File uploading without the file: just one more reason to drop that 10-line one-off XMLHTTP wrapper and pick up something better.
If you've got a recent Safari build, give this demo a spin. Being able to do drop shadows without images is a pretty neat trick, but it only works on Safari for now. Firefox 1.5 only has stub functions in it's <canvas> implementation where it's shadow property getters and setters should be.
I'm working on a more portable variant to support FF, which we can then turn into a Dojo widget. Stay tuned.
About a month ago I wrote about the internal tension I feel between learning, doing, and experimentation. Yesterday Jennifer pointed me to a new essay by Paul Graham that hits the nail on the head. I had posited that my tension was between figuring out what is possible and working on things that I know to be possible, but I may have been lying to myself a bit. As I reflect on it, the tension is between things that I know to be easily tractable and relatively unimportant (chores) and difficult but terribly important (real work).
It's useful, but also a bit weird, to have someone lay out what you're struggling with in such clear terms.
Professional's need professional tools, which is one of the main reasons we started the Dojo project. Dojo's I/O system is a prime example. From built-in caching (and cache busting) to making requests bookmarkable, the dojo.io.bind()
abstraction makes doing Ajax easier than with any other client-side library. As I promised earlier I'm going to cover the memory leak prevention and file uploading support in dojo.io.bind()
.
As with DOM event handlers, IE gets easily confused about what it can and cannot clean up when you make XMLHTTP requests. The naive way of listening for the "finished" state in an asynchronous request is to attach a function to the XMLHTTP object's onreadystatechange
slot and determine inside the handler if the desired state has been reached. The problem though is the same as with DOM event handlers; since the XMLHTTP object in IE is an ActiveX control, IEs reference counting mechanism goes walkabout when presented with an event handler that is assigned from the JavaScript context. The contexts (closures) referenced by the event handler get leaked. Oops.
After much testing, we figured out a pattern that prevents this kind of leakage. Instead of attaching a method to be called back to, Dojo's IO system starts a timer to watch for state changes on in-flight network requests. When a change is detected, we dispatch the correct event handler from outside the XMLHTTP object's event system. Since we never set a closure reference between the ActiveX object and the JavaScript context, we never leak it. Of course, once there are no more operations "in the air", we cancel the timer that watches for state changes to prevent further resource usage.
But wait! there's more!
Dojo's IO system not only handles hand-rolled requests, it will also submit entire forms in the background for you. Just pass in a reference to the form's DOM Node in the formNode
parameter and it gets automatically serialized into a request of the specified variety (either POST or GET). There's no reason that adding Ajax to legacy apps should be harder than doing it in new development.
The big caveat-emptor, however, has always been uploading files. If you pass a form node to the default XMLHTTP back end for dojo.io.bind()
, it will refuse to process the request since there's no portable way to get the contents of a file on disk from inside the browser environment. But forms are different. Browsers let users explicitly specify which file to upload in a form and submitting these forms carries the encoded form content along for the ride. It'd be really handy if we could treat these forms the same way in an Ajax application.
As alluded to earlier, dojo.io.bind()
supports "pluggable" back-ends. When a request is passed into bind
, it asks each of the registered transport systems if they will accepted the request in turn. The first one to say "yes" gets passed the request to handle. In most cases, this is the XMLHTTP transport, but not for forms with file uploads. For these occasions, Dojo provides an "IframeIO" module. Since we can "target" a form to be submitted in whatever frame we please, the IframeIO class lets the browser process the request normally, but still in the background. Using it is as simple as adding the following line to your scripts:
dojo.require("dojo.io.IframeIO");
Now, passing the formNode
parameter will work for both upload and non-upload cases. The XMLHTTP transport will still get used for most requests but when presented with a file upload, your code will now handle it gracefully, albeit with that click sound in the background when the form is submitted. A small price to pay for being able to add file-upload capability to single-page applications!
Next time: uploading files...without the file?
For the better part of a year, I've been saying to anyone that will listen that IE 7 is either a ruse or a bad joke. Getting into the details of why takes a lot of doing, and so I usually skip it. Dare Obasanjo has captured it perfectly.
Older Posts
Newer Posts