There is a form to upload and add a document to the repository:
package asia.grails.simpledms class Document { String filename String fullPath Date uploadDate = new Date() static constraints = { filename(blank:false,nullable:false) fullPath(blank:false,nullable:false) } }The filename is to preserve the original filename uploaded by the user. The fullPath is where the document is saved after upload. The uploadDate is just for keeping track of the date and time the document is uploaded.
class DocumentController { def list() { params.max = 10 [documentInstanceList: Document.list(params), documentInstanceTotal: Document.count()] } }I hardcoded to 10 records per page for simplicity.
list.gsp
<!DOCTYPE html> <html> <head> <meta name="layout" content="main"> <title>Document List</title> </head> <body> <div class="nav" role="navigation"> <ul><li><g:link class="create" action="create">Upload New Document</g:link></li></ul> </div> <div class="content scaffold-list" role="main"> <h1>Document List</h1> <g:if test="${flash.message}"><div class="message" role="status">${flash.message}</div></g:if> <table> <thead> <tr> <g:sortableColumn property="filename" title="Filename" /> <g:sortableColumn property="uploadDate" title="Upload Date" /> </tr> </thead> <tbody> <g:each in="${documentInstanceList}" status="i" var="documentInstance"> <tr class="${(i % 2) == 0 ? 'even' : 'odd'}"> <td><g:link action="download" id="${documentInstance.id}">${documentInstance.filename}</g:link></td> <td><g:formatDate date="${documentInstance.uploadDate}" /></td> </tr> </g:each> </tbody> </table> <div class="pagination"> <g:paginate total="${documentInstanceTotal}" /> </div> </div> </body> </html>
Note the entry with:
<g:link action="download" id="${documentInstance.id}">${documentInstance.filename}</g:link>This is the link to download the file given the document id.
We can configure where to upload the documents by introducing the configuration variable uploadFolder in Config.groovy. We can have different values depending on the environment. Append the following at the bottom of Config.groovy
environments { development { uploadFolder = "c:/temp/upload/" } test { uploadFolder = "c:/temp/upload/" } production { uploadFolder = "c:/temp/upload/" } }We can use this value when uploading the file later
Document upload is simple. For the form, we can use the tag uploadForm. This will set the enctype attribute to "multipart/form-data" automatically.
<%@ page import="asia.grails.simpledms.Document" %> <!DOCTYPE html> <html> <head> <meta name="layout" content="main"> <title>Upload New Document</title> </head> <body> <div class="nav" role="navigation"> <ul><li><g:link class="list" action="list">Document List</g:link></li></ul> </div> <div class="content scaffold-create" role="main"> <h1>Upload New Document</h1> <g:if test="${flash.message}"><div class="message" role="status">${flash.message}</div></g:if> <g:uploadForm action="upload"> <fieldset class="form"> <input type="file" name="file" /> </fieldset> <fieldset class="buttons"> <g:submitButton name="upload" class="save" value="Upload" /> </fieldset> </g:uploadForm> </div> </body> </html>The controller action to receive the uploaded form:
package asia.grails.simpledms class DocumentController { def upload() { def file = request.getFile('file') if(file.empty) { flash.message = "File cannot be empty" } else { def documentInstance = new Document() documentInstance.filename = file.originalFilename documentInstance.fullPath = grailsApplication.config.uploadFolder + documentInstance.filename file.transferTo(new File(documentInstance.fullPath)) documentInstance.save() } redirect (action:'list') } }We can access the uploadFolder we set in Config.groovy using the code grailsApplication.config.uploadFolder. The logic of the action is straightforward. The file uploaded can be accessed through request.getFile('file'). The following properties/methods were used:
For downloading, we just need to retrieve the file from the filesystem and send it to the response.
package asia.grails.simpledms class DocumentController { def download(long id) { Document documentInstance = Document.get(id) if ( documentInstance == null) { flash.message = "Document not found." redirect (action:'list') } else { response.setContentType("APPLICATION/OCTET-STREAM") response.setHeader("Content-Disposition", "Attachment;Filename=\"${documentInstance.filename}\"") def file = new File(documentInstance.fullPath) def fileInputStream = new FileInputStream(file) def outputStream = response.getOutputStream() byte[] buffer = new byte[4096]; int len; while ((len = fileInputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, len); } outputStream.flush() outputStream.close() fileInputStream.close() } } }The calls to response.setContentType("APPLICATION/OCTET-STREAM") and response.setHeader("Content-Disposition", "Attachment;Filename=\"${documentInstance.filename}\"") are necessary to force the browser to download. Otherwise the browser will try to render the document. The rest of the code is just to read the file and send it to the response object.