Grails Cookbook - A collection of tutorials and examples

Grails Example Application - Simple Document Management System

This is an example application that implements a simple document management system. The goal is to show how to upload or download documents in a Grails application. The output will look like this:

The user can view list of documents:
e1
Clicking an item will download the document file:
e3

There is a form to upload and add a document to the repository:
e2

Domain

As this is just a sample application, there is only one domain in the application to represent a document:
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.

Listing

Listing of documents is just simple. For the example, I just used the generated scaffold code and shortened to fit better in a blog post.
Controller code:
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.

Configuring Upload Location

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

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:
  • file.empty - check if there is really an uploaded file
  • file.originalFilename - the filename of the document uploaded
  • file.transferTo - transfer the file to a new path.

Document Download

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.

Remarks

The above code is just to serve as a working example on how to code uploading and downloading of files. To help start projects that needs such feature.
The full source code for this example can be viewed here or can be downloaded here.

List of Example Applications
  • Simple Ajax Chat
  • Simple Document Management System
  • Save Documents To Database
  • Download / Export Excel File
  • Download / Export ZIP File
  • Grails Forum Application

  • Tags: DMS, download, example, uploadForm