My evil scheme for world domination has a web front end that allows file uploads. Unfortunately the standard web browser support for file uploads is as attractive as a monkey's bottom and, unless you have very strange tastes in either area, about as useful. Why doesn't one of the emerging updates to HTML support a humane file upload picker? We may never know.
There are several possible work-arounds.
- You can use a Java Applet. This is an approach known, even to Java heads like me, as "teh suck."1
- You can use a Flash gizmo. This generally looks lovely, but last time I was involved in a project doing this it emerged that the multipart file support was kind of broken in at least one version of the Flash plugin. Or possibly someone was doing something incompetent; I don't know. I do know that I hate Flash anyway2.
- You can use a dedicated plugin of some sort. This is sort of the worst of both worlds; not only will it make the user install some piece of flakey crap, but also it will probably not work on some browsers - or, indeed, in the case of some plugins I've tried, on any browsers.
- Or you can just make the filesystem available as a share of some sort3.
So I've gone for that last option. The problem is that my application isn't using a filesystem. So I haven't got one I can share. It's storing all the files in an honest to god PostgreSQL database as blobs. So instead of sharing out the filesystem, I'm skipping the middle man and working on an implementation of the WebDAV4 distributed authoring protocol to expose the various table contents directly. This is my world of geeky fun. Inevitably my implementation of the protocol has been dubbed WebDAVE.
I have hit a few problems though. The WebDAV spec is not as clear as, for example, the SMTP5 specification and it makes some fairly heavy use of XML. In some areas it is quite hand wavily vague about quite what content to expect. Still, my application is now able to talk to the Dolphin filemanager, so it's coming together. I have no doubt that it will explode violently on contact with other clients, but some more rigorous testing should take care of the worst deficiencies.
Along the way I encountered two particularly baffling issues, both eventually resolved to errors on my part, but I thought I'd document them here for future reference.
Firstly, after issuing a MKCOL (create folder) request, the Dolphin client complained "A folder named New Folder already exists." If I did a refresh the folder suddenly materialized. It took me ages to spot that I was returning the 200 (OK) response code, instead of the 201 (CREATED) response code as required by the standard.
Secondly, when uploading files with the PUT operation, my files were ending up zero bytes long. Utterly baffling that one, because I already had form based file uploads working absolutely fine and the WebDAV support was invoking most of the same logic. That was caused by my use of Hiberate's methods for creating Blobs. If you call Hibernate.createBlob(InputStream) the input stream's available() method must return the full size of the incoming blob. However, when reading a ServletInputStream from a browser request, that typically returns 0 because the incoming data supply depends upon the client6. The solution is to use the alternative Hibernate.createBlob(InputStream,int) method to specify the number of octets to read from the incoming stream. The incoming PUT request specifies the content length, so this information is available when it's needed.
Once WebDAVE is complete I'll try to resist the temptation to add an FTP client.
Footnotes
1 Why do Java Applets still take a zillion years to load? Hell, I can launch the entirely Java based Tomcat application server quicker than most Java Applets. What on earth do they spend all that time doing? I think we should be told.
2 This may seem intolerant, but in fact, since I discovered Desktop Tower Defence and (of course) Scrabulous, I've mellowed enough that I no longer think that all Flash developers should be put to death. Unless they create Flash based adverts.
3 Actually the Flickr photo hosting site takes the alternative approach of creating a desktop widget to allow you to upload via their website. It's a neat solution, but not one I plan to emulate.
4 If you fancy emulating this act of stupidity, you'll need to brush up on RFC2068 describing the HTTP 1.1 specification and RFC2518 describing the WebDAV distributed authoring extensions to it.
5Which was specified in the beautifully written RFC0821 by the immortal Jon Postel.
6 Why didn't my form based method fail in the same way? Because it was coming in as multipart form data from a POST, rather than the input stream of a PUT request. I was therefore using Spring's CommonsMultipartResolver support class. In my configuration this converts the incoming POST data into a set of temporary files. The logic on this path through the application was therefore supplying a FileInputStream to the Hibernate method, and the available() method on a file normally returns the number of bytes in the file.