[TUTORIAL] Custom image upload with Java Objects

Post here your questions about Actionscript and Java server side extensions development.

Moderators: Lapo, Bax

User avatar
mistermind
Posts: 131
Joined: 15 Sep 2007, 01:33
Contact:

[TUTORIAL] Custom image upload with Java Objects

Postby mistermind » 07 Oct 2010, 19:15

Hey guys, instead of just replying to my previous upload questions, I thought it would be better if I post how I actually did it :)
This tutorial will guide you to how not only upload an image to the server using nothing more then your smartfoxserver server extension, but it will also let you manipulate that image before you do it so.

Requirements:
- You must be familiar with AS3, FileReference and SmartFoxServer.
- SFS 1.6.x.
- Java compiler (http://download.oracle.com/javase/)
- AS3 Base64 (http://jpauclair.net/2010/01/09/base64-optimized-as3-lib/)
- JPGEncoder (http://github.com/mikechambers/as3corelib)


Ok so lets start:
First, instead of using a Java extension, I decided to use the good old Actionscript 1.0 (also called Jscript) and from there I used a custom java class. Why? Well aside from being "easier" to deal with, you can virtually pull anything from Java using custom classes, so, like Lapo's tutorial mentioned, the possibilities are limitless.

The first thing you will notice about using this custom upload is the ability to manipulate the file before you actually let your client send it to the server. We won't be dealing with the upload method from neither FileReference nor smartfox.upload, since they are both limited to the file loaded within the FileReference instance, and the data contained which unfortunately is [read-only].

First, lets start off with the client. This is not the main class, but a segment to be used as a container. I'm also assuming you have your own library buttons, so I wont waste code placing button creations.
SFSUpload.as

Code: Select all

package {
   import flash.display.BitmapData;
   import flash.display.Loader;
   import flash.display.MovieClip;
   import flash.events.Event;
   import flash.events.MouseEvent;
   import flash.geom.Matrix;
   import flash.net.FileReference;
   import flash.utils.ByteArray;
   import it.gotoandplay.smartfoxserver.SmartFoxClient;
   import it.gotoandplay.smartfoxserver.SFSEvent;
   
   public class SFSUpload extends MovieClip {
      
      private var sfs:SmartFoxClient;
      private var file:FileReference;
      private var imageContainer:MovieClip;
      private var imageLoader:Loader;
      
      public function SFSUpload(sfs:SmartFoxClient):void {
         this.sfs = sfs;

         file = new FileReference();
         file.addEventListener(Event.SELECT, selectHandler, false, 0, true);
         file.addEventListener(Event.COMPLETE, onFileLoadComplete, false, 0, true);
         
         browseBtn.addEventListener(MouseEvent.CLICK, browseFile, false, 0, true);
         uploadBtn.addEventListener(MouseEvent.CLICK, saveFile, false, 0, true);
         
         imageLoader = new Loader();
         imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onDataLoadComplete);
         
         imageContainer = new MovieClip();
         addChild(imageContainer);
      }
      
      private function browseFile(event:Event):void {
         file.browse( new Array( new FileFilter( "Images (*.jpg, *.jpeg, *.png, *.gif)", "*.jpg;*.jpeg;*.png;*.gif" ) ) );
      }
      
      private function selectHandler(event:Event):void {
         file.load();
      }
      
      private function onFileLoadComplete(event:Event):void {
         imageLoader.loadBytes(file.data);
      }
      
      private function onDataLoadComplete(event:Event):void {
         var bitmapData:BitmapData = (event.currentTarget.content as Bitmap).bitmapData;
         var matrix:Matrix = new Matrix();
         matrix.scale(150/bitmapData.width, 150/bitmapData.height);
         imageContainer.graphics.clear();
         imageContainer.graphics.beginBitmapFill(bitmapData, matrix, false);
         imageContainer.graphics.drawRect(0, 0, 150, 150);
         imageContainer.graphics.endFill();
      }
      
      private function saveFile(event:Event):void {
         var bitmapData:BitmapData = new BitmapData(150, 150);
         bitmapData.draw(imageContainer);
         var jpgEncoder = new JPGEncoder(60);
         var rawBites:ByteArray = jpgEncoder.encode(bitmapData);
         var rawString:String = Base64.encode(rawBites);
         sfs.sendXtMessage("ASExtension", "upload", { img: rawString }, "json");
      }
   }
}


What this code basically does is let the user select any image and convert it to a 150x150 visualization box. When saving, that visualization is converted to a BitmapData, which is then encoded to jpg and finally converted to ByteArray. But you can't just send bytes through JSON or XML. Thats where Base64 comes in. Base64 basically converts that messy byteArray into a "readable" string which can then be sent to your server.

Now, from the server:
ASExtension.as

Code: Select all

...
function handleRequest(cmd, params, user, fromRoom, protocol) {
    ...
    if (cmd == "upload") {
        if (params.img) { // Make sure they sent it
            var writeByteArrayToFile = new Packages.WriteByteArrayToFile();
            writeByteArrayToFile.write("c:\\new_image.jpg", Packages.it.gotoandplay.extensions.examples.Base64.decode(java.lang.String(params.img)));
        }
    }
}
...


WriteByteArrayToFile.java

Code: Select all

/*
Write byte array to a file using FileOutputStream
This example shows how to write a byte array to a file using write method of
Java FileOutputStream object.
*/
import java.io.*;

public class WriteByteArrayToFile {
    public WriteByteArrayToFile() { }
   
    public void write(String strFilePath, byte[] data) {
        try    {
            FileOutputStream fos = new FileOutputStream(strFilePath);
            fos.write(data);
            fos.close();
        }
        catch(FileNotFoundException ex)    {
            System.out.println("FileNotFoundException : " + ex);
        }
        catch(IOException ioe) {
            System.out.println("IOException : " + ioe);
        }
    }
}


After compiling WriteByteArrayToFile.java, you can then save the class file to either sfsExtensions folder or javaExtensions (given you follow the instructions bellow).

Notice how I had to use Java's own Base64 class (provided in the examples thanks to Lapo) and not a custom made one on Jscript this time. That happens because for some reason ByteArray is not a very reliable var type on Jscript. Even if you try making a variable to carry that value to the writeByteArrayToFile parameter it will most likely freeze your smartfoxserver. Just send your JSON string straight to the Base64 decoder and that result straight to the java object.

One important thing: By default (at least on my SFS version), the javaExtensions folder is not set as a class path. To set it on, edit your start.bat file within the Server folder and add this to the list of folders:

Code: Select all

;./javaExtensions/

At the end, start.bat should look like this:

Code: Select all

@echo off
"..\jre\bin\java.exe" -server -cp "./;./sfsExtensions/;./javaExtensions/;lib/activation.jar;lib/commons-beanutils.jar;lib/commons-collections-3.2.jar;lib/commons-dbcp-1.2.1.jar;lib/commons-lang-2.3.jar;lib/commons-logging-1.1.jar;lib/commons-pool-1.2.jar;lib/concurrent.jar;lib/ezmorph-1.0.3.jar;lib/h2.jar;lib/js.jar;lib/json-lib-2.1-jdk15.jar;lib/json.jar;lib/jsr173_1.0_api.jar;lib/jysfs.jar;lib/jython.jar;lib/nanoxml-2.2.1.jar;lib/wrapper.jar;lib/xbean.jar;lib/javamail/imap.jar;lib/javamail/mailapi.jar;lib/javamail/pop3.jar;lib/javamail/smtp.jar;lib/jetty/jetty.jar;lib/jetty/jetty-util.jar;lib/jetty/jstl.jar;lib/jetty/multipartrequest.jar;lib/jetty/servlet-api.jar;lib/jetty/standard.jar;lib/jsp-2.1/commons-el-1.0.jar;lib/jsp-2.1/core-3.1.0.jar;lib/jsp-2.1/jsp-2.1.jar;lib/jsp-2.1/jsp-api-2.1.jar;lib/jsp-2.1/jstl.jar;lib/jsp-2.1/standard.jar;lib/lsc.jar;lib/commons-io-1.4.jar" -Dfile.encoding=UTF-8 -Djava.util.logging.config.file=logging.properties it.gotoandplay.smartfoxserver.SmartFoxServer
@pause

This is the only way you can get Java's Base64 from the examples folder to work.


Ok that is about it! If I forgot anything please let me know! This is my first time writing a tutorial for anything and I'm pretty sure its not perfect :)
Last edited by mistermind on 08 Oct 2010, 15:50, edited 1 time in total.
SELECT * FROM users WHERE clue > 0
0 rows returned.
User avatar
BigFIsh
Posts: 1698
Joined: 25 Feb 2008, 19:26
Location: New Zealand

Postby BigFIsh » 08 Oct 2010, 05:14

This is quite useful, so I'm gonna have to make it a sticky :-)

Thanks for sharing it with us.
Smartfox's forum is my daily newspaper.
User avatar
Lapo
Site Admin
Posts: 22999
Joined: 21 Mar 2005, 09:50
Location: Italy

Postby Lapo » 09 Oct 2010, 07:11

Well done, thank you! :)
Lapo
--
gotoAndPlay()
...addicted to flash games

Return to “Server Side Extension Development”

Who is online

Users browsing this forum: No registered users and 27 guests