Last updated by burtbeckwith 3 years ago

Simple Avatar Uploader

Many collaborative websites allow the user to upload small images or avatars to visually represent the user on the website. Grails makes the upload and display of these images almost trivial.

In this example, we will not specify an exact size for the avatar, but use CSS scaling and assume a width-to-height ratio of 4:5.

To start, you need to add two fields to your User domain class, a byte[] to hold the raw image and a String to hold the mime-type of the image:

Domain Class: User.groovy

class User {
  byte[] avatar
  String avatarType

static constraints = { avatar(nullable:true, maxSize: 16384 /* 16K */) avatarType(nullable:true) } }

Next, create a view for the user to select the avatar image to upload:

User View: select_avatar.gsp

  <legend>Avatar Upload</legend>
  <g:uploadForm action="upload_avatar">
    <label for="avatar">Avatar (16K)</label>
    <input type="file" name="avatar" id="avatar" />
    <div style="font-size:0.8em; margin: 1.0em;">
      For best results, your avatar should have a width-to-height ratio of 4:5.
      For example, if your image is 80 pixels wide, it should be 100 pixels high.
    <input type="submit" class="buttons" value="Upload" />

Next, in your UserController, you will need to create a handler for the upload:


private static final okcontents = ['image/png', 'image/jpeg', 'image/gif']

def upload_avatar() { def user = springSecurityService.currentUser // or however you select the current user

// Get the avatar file from the multi-part request def f = request.getFile('avatar')

// List of OK mime-types if (!okcontents.contains(f.getContentType())) { flash.message = "Avatar must be one of: ${okcontents}" render(view:'select_avatar', model:[user:user]) return }

// Save the image and mime type user.avatar = f.bytes user.avatarType = f.contentType"File uploaded: $user.avatarType")

// Validation works, will check if the image is too big if (! { render(view:'select_avatar', model:[user:user]) return } flash.message = "Avatar (${user.avatarType}, ${user.avatar.size()} bytes) uploaded." redirect(action:'show') }

The final piece is extracting and displaying the avatar. First, you'll need to create a handler on User and then some CSS styles to scale the image properly:


def avatar_image() {
  def avatarUser = User.get(
  if (!avatarUser || !avatarUser.avatar || !avatarUser.avatarType) {
  response.contentType = avatarUser.avatarType
  response.contentLength = avatarUser.avatar.size()
  OutputStream out = response.outputStream
Next, add a few classes to your stylesheet:

img.avatar {
  width: 6.0em;
  height: 7.5em;

img.avatar_small { width: 4em; height: 5em; }

img.avatar_tiny { width: 2em; height: 2.5em; }

Notice you don't specify the precise size of the avatar, rather you use em-scaling and a 4:5 width-to-height ratio. This means the avatars will scale based on the user's font/screen size preferences (more about em-scaling in CSS).

Finally, you can summon up a user's avatar in a web page using a pattern similar to:


<g:if test="${user.avatar}">
  <img class="avatar" src="${createLink(controller:'user', action:'avatar_image', id:user.ident())}" />
By changing the class of the <img> tag, you can change the size of the avatar on your page.