In the Vaadin Mobile Application Framework, Component.Event is the superclass of all component-originating events and is the one required method of the Component.Listener interface. In the Handling Events in Vaadin Mobile Applications article, we got an overview of events in Vaadin. In today’s follow up, we’ll work with the Component.Event to load and display an image selected by an UploadField control.
Working with the componentEvent() Listener Method
Being at the top of the Event hierarchy, the Listener’s componentEvent() method receives a generic Event object as a parameter. But that’s fine because it has a getSource() method to reference the control that fired the event. It returns an Object so you have to cast the return value as your control type. Once we have our UploadField reference, we can invoke its getValue() method to fetch the selected File information. Be careful when casting this value because its type depends on the FieldType property. It can be set to FILE, UTF8_STRING, or BYTE_ARRAY:
- FILE: returns a java.io.File.
- UTF8_STRING: returns a String. Good for working with text data.
- BYTE_ARRAY: returns an array of bytes. Good for working with binary data.
In the following example, the componentEvent() is overridden to validate the file type. Although the File returned by getValue() is actually a .tmp file in a public directory, we can still read it using the ImageIO class. It returns null if the file is not a valid image. We can then notify the user to try again. This particular example uses the static Notification.show() method. It displays a box in the center of the screen:
final UploadField ufStartPositionImage = new UploadField(); ufStartPositionImage.setCaption("Start Position Image"); ufStartPositionImage.setButtonCaption("Choose image..."); ufStartPositionImage.setAcceptFilter("image/*"); ufStartPositionImage.addListener(new Listener() { @Override public void componentEvent(Event event) { UploadField source = (UploadField) event.getSource(); File file = (File) source.getValue(); try { BufferedImage image = ImageIO.read(file); if (image == null) { Notification.show("The file "+file.getName()+" is not an image", Type.WARNING_MESSAGE); } else { //you can do something with the image here... } } catch(IOException ex) { Notification.show("The file "+file.getName()+" could not be opened; an error occurred.", Type.ERROR_MESSAGE); } } }); ufStartPositionImage.setFieldType(FieldType.FILE); layout.addComponent(ufStartPositionImage);
By default the UploadField displays the file name and path to the temp uploaded file:
Althought the label text is cut off, inspecting the element source reveals the full obfuscated path as “C:\Users\jxg768\AppData\Local\Temp\upload_tmpfile_14395797323741599763930502044415.tmp”.
Here is what happens when I choose a non-image file such as a PDF or MS Word document:
Displaying the Uploaded Image
In the case of images, it might be nice to see it once it’s been uploaded. Displaying an image is not difficult because Vaadin provides an Image UI component. It’s just a matter of setting the UploadField’s FieldType to a BYTE_ARRAY instead of a FILE:
final UploadField ufStartPositionImage = new UploadField(); final Image image = new Image("Selected Image"); ufStartPositionImage.setFieldType(FieldType.BYTE_ARRAY); ufStartPositionImage.addListener(new Listener(){ @Override public void componentEvent(Event event) { showUploadedImage(ufStartPositionImage, image); } }); layout.addComponent(ufStartPositionImage); image.setVisible(false); image.setImmediate(true); layout.addComponent(image);
The following method accepts the UploadField control and Image component will display the image. The image source must be set to a StreamResource, so some conversion is required. The StreamResource class constructor accepts a StreamSource Interface and file name as parameters. We could write a separate class that implements the StreamSource Interface, but the standard way to use it is to create the class inline using the curly braces {} and overriding the getStream() method to return a ByteArrayInputStream from the byte[] data.
private void showUploadedImage(UploadField upload, Image image) { Object value = upload.getValue(); final byte[] data = (byte[]) value; StreamResource resource = new StreamResource( new StreamResource.StreamSource() { @Override public InputStream getStream() { return new ByteArrayInputStream(data); } }, "filename.png"); image.setSource(resource); image.setVisible(true); }
The second argument to the StreamResource() constructor is the filename that the file will be saved as on the Web server, i.e.:
http://localhost:8089/ExercisesMobileApp/APP/connector/0/16/source/filename.png
The above code does an excellent job of loading and displaying the image file, but it’s not so much a preview than a full size image:
Resizing the Uploaded Image to a Thumbnail
For optimal results, image resizing details are best left to a specialized library, but if you’re not that particular about the image quality, here’s a pure Java solution:
private void showUploadedImage(UploadField upload, Image image) { Object value = upload.getValue(); final byte[] data = (byte[]) value; StreamResource resource = new StreamResource( new StreamResource.StreamSource() { @Override public InputStream getStream() { try { BufferedImage bi = ImageIO.read(new ByteArrayInputStream(data)); int scaleX = (int) (bi.getWidth() * 0.25); int scaleY = (int) (bi.getHeight() * 0.25); java.awt.Image newImg = bi.getScaledInstance(scaleX, scaleY, java.awt.Image.SCALE_SMOOTH); /* Write the image to a buffer. */ ByteArrayOutputStream imagebuffer = new ByteArrayOutputStream(); BufferedImage bimage = new BufferedImage(scaleX, scaleY, BufferedImage.TYPE_INT_RGB); Graphics bg = bimage.getGraphics(); bg.drawImage(newImg, 0, 0, null); bg.dispose(); ImageIO.write(bimage, "jpg", imagebuffer); /* Return a stream from the buffer. */ return new ByteArrayInputStream(imagebuffer.toByteArray()); } catch (IOException e) { return null; } } }, "filename.png"); image.setSource(resource); image.setVisible(true); }
In the above code, the image gets redrawn via a ByteArrayOutputStream to one quarter its original size to produce the following:
Conclusion
Now that we know the basics of loading and displaying an image, in a future article, we’ll take a look at how to give your thumbnail a specific width or height, while producing the best quality output using the imgscalr library.
Rob Gravelle resides in Ottawa, Canada, and is the founder of GravelleWebDesign.com. Rob has built systems for Intelligence-related organizations such as Canada Border Services, CSIS as well as for numerous commercial businesses.
In his spare time, Rob has become an accomplished guitar player, and has released several CDs. His band, Ivory Knight, was rated as one Canada’s top hard rock and metal groups by Brave Words magazine (issue #92) and reached the #1 spot in the National Heavy Metal charts on Reverb Nation.