I don't use SmartGWT Server, our server is a JBoss where we have implemented REST Web Services.
I will try to provide a complete tutorial in order to :
- Choose one or several image files in the file chooser of an UploadItem
- Display it in a com.smartgwt.client.widgets.Img object in my browser
It seemed so easy to do such basic stuff but I was wrong.
Due to security reasons, the browser does not give a way to get complete path to the file.
Then your SmartGWT client code has no way to read the file on client workstation.
The method uploadItem.getDisplayValue() gives you on Firefox only file name and on Chrome file name with "C:/fakepath/" as suffix.
The only method (I'm writing this tutorial in order to get confirmation) to do such thing is to :
- Perform a uploadItem.submitForm() on client
- Implement a Servlet on server which will :
* receive submit form datapackage
* extract file(s) binary
* call back client in order to give client file(s) binary
- Client get called back and display file(s) binary in Img objects
To make it a little more complicated, the uploadItem.submitForm will make your browser change page and go on Servlet's defined URL.
You may not want this and want to stay on your current page waiting for being called back (me too).
To do such thing, you have to define an invisible frame which will be the target of submitForm().
The Servlet will provide Javascript code as return to this invisible Frame in order to get your client called back.
That is why we will have a Javascript native method on our client.
I build this solution from all those pages :
http://www.mrsondao.com/TopicDetail.aspx?TopicId=2
http://forums.smartclient.com/showthread.php?t=3102
http://ruchi0711.blogspot.fr/2011/07...smart-gwt.html
http://maryniuk.blogspot.fr/2009/09/...-smartgwt.html
http://blog.karanfil.info/2011/03/sm...le-upload.html
http://stackoverflow.com/questions/8...ynamic-form-wi
http://forums.smartclient.com/showthread.php?t=5477
Let's do it step by step.
---------------------
-- INVISIBLE FRAME --
---------------------
This invisible frame has to be added to your client.
In my case, mainLayout is my main layout containing our whole MMI.
-----------------
-- SUBMIT FORM --
-----------------
submitForm method is performed on a DynamicForm
In my case, I have created a class com.example.client.MyCanvas which extends CanvasItem.
The DynamicForm is added to my canvas.
----------------------------------------------
-- Javascript native function for call back --
----------------------------------------------
After adding uploadForm in myCanvas, we can add call back mechanism
Method addUploadImageCallback in MyCanvas.
With this, the invisible frame we have declared previously will be able to receive result from servlet and executed the method uploadCompleteCallback on myCanvas.
Now we can declare method uploadCompleteCallback in MyCanvas.
-------------
-- SERVLET --
-------------
In the file web.xml :
myapp == GWT.getModuleBaseURL()
This generated Javascript code will be send back to the invisible frame declared previously.
This invisible frame will execute such code and call uploadCompleteCallback method with message argument on myCanvas.
-----------------------
-- END FIRST VERSION --
-----------------------
At this point, it should be working and you should see on client console the message "coucou".
---------------------
-- EXTRACT FILE(S) --
---------------------
In servlet I use Apache Commons File Upload in order to extract file(s) easily, there is many others ways to do it.
-----------------------
-- BINARY AS BASE 64 --
-----------------------
I choose to encode the binary from byte[] to Base64 String.
I use Apache Commons Codec.
Now you can return imageFileAsBase64 instead of "coucou"
-----------------
-- ADD HEADERS --
-----------------
In order to be display on client in Img object (or TileGrid for example), you must add headers to your Base64 String :
Let's assume the image file uploaded is a jpg.
Others mimeType : http://www.sitepoint.com/web-foundat...complete-list/
Now you can return imagePayload instead of imageFileAsBase64
--------------------
-- DISPLAY IN IMG --
--------------------
In uploadCompleteCallback method, you can display image in a Img object :
---------------------
-- THIS IS THE END --
---------------------
I worry about my solution, i found it too complicated for something so basic (just display an image in client .....).
I'm new on SmartGWT development and basically Front-End development.
I take advice and critics. Is there some way much more easier to do that ?
It hard for me to admit I need a Javascript native function to perform a call back, it must be another way.
Thanks by advance.
I will try to provide a complete tutorial in order to :
- Choose one or several image files in the file chooser of an UploadItem
- Display it in a com.smartgwt.client.widgets.Img object in my browser
It seemed so easy to do such basic stuff but I was wrong.
Due to security reasons, the browser does not give a way to get complete path to the file.
Then your SmartGWT client code has no way to read the file on client workstation.
The method uploadItem.getDisplayValue() gives you on Firefox only file name and on Chrome file name with "C:/fakepath/" as suffix.
The only method (I'm writing this tutorial in order to get confirmation) to do such thing is to :
- Perform a uploadItem.submitForm() on client
- Implement a Servlet on server which will :
* receive submit form datapackage
* extract file(s) binary
* call back client in order to give client file(s) binary
- Client get called back and display file(s) binary in Img objects
To make it a little more complicated, the uploadItem.submitForm will make your browser change page and go on Servlet's defined URL.
You may not want this and want to stay on your current page waiting for being called back (me too).
To do such thing, you have to define an invisible frame which will be the target of submitForm().
The Servlet will provide Javascript code as return to this invisible Frame in order to get your client called back.
That is why we will have a Javascript native method on our client.
I build this solution from all those pages :
http://www.mrsondao.com/TopicDetail.aspx?TopicId=2
http://forums.smartclient.com/showthread.php?t=3102
http://ruchi0711.blogspot.fr/2011/07...smart-gwt.html
http://maryniuk.blogspot.fr/2009/09/...-smartgwt.html
http://blog.karanfil.info/2011/03/sm...le-upload.html
http://stackoverflow.com/questions/8...ynamic-form-wi
http://forums.smartclient.com/showthread.php?t=5477
Let's do it step by step.
---------------------
-- INVISIBLE FRAME --
---------------------
Code:
// Hidden frame in order to perform upload image file private static NamedFrame uploadFileHiddenFrame = new NamedFrame("uploadFileHiddenFrame"); ... uploadFileHiddenFrame.setWidth("1px"); uploadFileHiddenFrame.setHeight("1px"); uploadFileHiddenFrame.setVisible(false); mainlayout.addMember(uploadFileHiddenFrame);
In my case, mainLayout is my main layout containing our whole MMI.
-----------------
-- SUBMIT FORM --
-----------------
submitForm method is performed on a DynamicForm
Code:
// DynamicForm in order to load one image // client-server servlet call and back final DynamicForm uploadForm = new DynamicForm(); uploadForm.setEncoding(Encoding.MULTIPART); // Set servlet name uploadForm.setAction(GWT.getModuleBaseURL() + "uploadImageFile"); // In order to do not perform full page reload // target is set to CATV hidden frame defined in HomeView uploadForm.setTarget("uploadFileHiddenFrame"); // Upload item allowing to choose image file uploadItem = new UploadItem(); uploadItem.addChangedHandler(new ChangedHandler() { @Override public void onChanged(ChangedEvent event) { // Call servlet threw hidden frame uploadForm.submitForm(); } }); uploadForm.setItems(uploadItem);
The DynamicForm is added to my canvas.
Code:
public MyCanvas(...){ ... myCanvas.setCanvas(uploadForm); ... }
-- Javascript native function for call back --
----------------------------------------------
After adding uploadForm in myCanvas, we can add call back mechanism
Code:
public MyCanvas(...){ ... // Add native javascript mechanism in order to perform a call back addUploadImageCallback(this); ... }
Code:
private native void addUploadImageCallback(com.example.client.MyCanvas load) /*-{ $wnd.uploadCompleteCallback = function(imagePayload) { load.@com.example.client.MyCanvas::uploadCompleteCallback(Ljava/lang/String;)(imagePayload); }; }-*/;
Now we can declare method uploadCompleteCallback in MyCanvas.
Code:
private void uploadCompleteCallback(String imagePayload) { System.out.println(imagePayload); }
-- SERVLET --
-------------
In the file web.xml :
Code:
<!-- Upload Image File --> <servlet> <servlet-name>UploadImageFile</servlet-name> <servlet-class>com.example.server.UploadImageFile</servlet-class> </servlet> <servlet-mapping> <servlet-name>UploadImageFile</servlet-name> <url-pattern>/myapp/uploadImageFile</url-pattern> </servlet-mapping>
Code:
package com.example.server; import ... public class UploadImageFile extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter out = resp.getWriter(); resp.setContentType("text/html"); String message = "coucou"; out.println("<html>"); out.println("<head>"); out.println("<script type =\"text/javascript\">"); out.println("function foo() { "); out.println("window.top.uploadCompleteCallback('" + message + "');"); out.println("}"); out.println("</script>"); out.println("</head>"); out.println("<body onload=\"foo();\">"); out.println("</body>"); out.println("</html>"); resp.flushBuffer(); } }
This invisible frame will execute such code and call uploadCompleteCallback method with message argument on myCanvas.
-----------------------
-- END FIRST VERSION --
-----------------------
At this point, it should be working and you should see on client console the message "coucou".
---------------------
-- EXTRACT FILE(S) --
---------------------
In servlet I use Apache Commons File Upload in order to extract file(s) easily, there is many others ways to do it.
Code:
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2.1</version> </dependency>
Code:
public class UploadImageFile extends HttpServlet { private final DiskFileItemFactory factory = new DiskFileItemFactory(); public UploadImageFile() { // set size threshold to 20 MB factory.setSizeThreshold(2000000); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final ServletFileUpload upload = new ServletFileUpload(factory); final List<FileItem> fileItems = upload.parseRequest(req); for (FileItem fileItem : fileItems) { String fileName = fileItem.getName(); byte[] imageFileAsByteArray = fileItem.get(); } PrintWriter out = resp.getWriter(); resp.setContentType("text/html"); String message = "coucou"; out.println("<html>"); out.println("<head>"); out.println("<script type =\"text/javascript\">"); out.println("function foo() { "); out.println("window.top.uploadCompleteCallback('" + message + "');"); out.println("}"); out.println("</script>"); out.println("</head>"); out.println("<body onload=\"foo();\">"); out.println("</body>"); out.println("</html>"); resp.flushBuffer(); } }
-- BINARY AS BASE 64 --
-----------------------
I choose to encode the binary from byte[] to Base64 String.
I use Apache Commons Codec.
Code:
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency>
Code:
// Convert byte[] as Base64 String String imageFileAsBase64 = new String(Base64.encodeBase64(imageFileAsByteArray));
-----------------
-- ADD HEADERS --
-----------------
In order to be display on client in Img object (or TileGrid for example), you must add headers to your Base64 String :
Let's assume the image file uploaded is a jpg.
Code:
String mimeType = "image/jpeg"; String headers = "data:" + mimeType + ";base64,"; String imagePayload = headers + imageFileAsBase64;
Now you can return imagePayload instead of imageFileAsBase64
--------------------
-- DISPLAY IN IMG --
--------------------
In uploadCompleteCallback method, you can display image in a Img object :
Code:
Img picture = new Img(); picture.setSrc(imagePayload); // add picture to your MMI
-- THIS IS THE END --
---------------------
I worry about my solution, i found it too complicated for something so basic (just display an image in client .....).
I'm new on SmartGWT development and basically Front-End development.
I take advice and critics. Is there some way much more easier to do that ?
It hard for me to admit I need a Javascript native function to perform a call back, it must be another way.
Thanks by advance.
Comment