Previously posted on blog.labrat.info on January 13, 2011
I’ve been playing with Grails for a bit and I was trying to find out how I can upload images to what I’ve been playing with. Sure there’s a couple plugins out there. But I wanted something simple and something I could mess around with.
After some searching I found Valum’s AJAX Uploader page. I was impressed in just how simple his solution was yet so powerful. It also seemed quite simple to add this to Grails.
The first thing to do is to go his Git page on Github and download his files. This should have created a new directory with everything in there.
Copy the client files from the Git checkout into the correct places in the Grails project. This is to make your life easier when deploying as you can let Grails do all the magic of changing paths as you mode from development to production.
The files get moved like this:
valums-file-uploader/client/fileuploader.css -> web-app/css/ valums-file-uploader/client/fileuploader.js -> web-app/js/ valums-file-uploader/client/loading.gif -> web-app/images/
I wanted to have flexibility to embed the upload facility into any class to I created a view grails-app/views/_uploader.gsp. Here I use a lot of the gsp facilities to get the correct files we moved into the project to load once the view has loaded. One thing I didn’t find a good clean way to do was to change the CSS so it would point to the correct animated GIF. The hacky way I did it is to just overwrite the entire CSS line inside the view. You’ll probably want to change ${domainInstance?.id} to match your Domain’s name.
<link href="${resource(dir:'css',file:'fileuploader.css')}" rel="stylesheet" type="text/css"> <div id="image-uploader"> <noscript> <p>Please enable JavaScript to use file uploader.</p> <!-- or put a simple form for upload here --> </noscript> </div> <script src="${resource(dir:'js',file:'fileuploader.js')}" type="text/javascript"></script> <%-- Override the style in the file with the correct pathname --%> <style type="text/css"> .qq-upload-spinner { display:inline-block; background: url("${resource(dir:'images',file:'loading.gif')}"); width:15px; height:15px; vertical-align:text-bottom; } </style> <script> function createUploader(){ var uploader = new qq.FileUploader({ element: document.getElementById('image-uploader'), action: '<g:createLink action="upload" id="${domainInstance?.id}" />', sizeLimit: 3145728, // 3 MB allowedExtensions: ['jpg', 'jpeg'], debug: false }); } // in your app create uploader as soon as the DOM is ready // don't wait for the window to load window.onload = createUploader; </script>
Now it’s pretty amazing just how little you have to add to your Domain Controller to support the upload. I just added one more definition that looks like this:
def upload = { File file = new File("/tmp/" + request.getHeader('x-file-name')) try { InputStream inputStream = request.getInputStream(); OutputStream out=new FileOutputStream (file.getAbsolutePath()) byte[] buf = new byte [1024] int len while((len=inputStream.read(buf))>0) { out.write(buf,0,len) } out.close() inputStream.close() } catch (all) { file.delete() render(status: response.SC_INTERNAL_SERVER_ERROR, text:"{success: false}") } render(status: response.SC_OK, text:"{success: true}") }
And that’s all that needs to be done. Now go to whatever view you like, in my case I decided you can only upload once you’ve created an object so I just added the following into the grails-app/view/edit.gst:
<g:render template="uploader" model="['domainInstance':domainInstance]" />
And again please change domainInstance to match your Domain.
I’m amazed at just how simple this was.