In my particular case UI of the application should be Flash/Flex and the backend is on Java restricted to Goggle App Engine.
The form is quite easy one, it can be called "Add Employee" form. It has a dozen of textual fields and a "Photo" field that should pick up a file from the user's file system and upload it to the server.
Our goal is simple: we need to implement submission of the whole form in a HTTP request with MIME type "multipart/form-data". This MIME type is described in this RFC2388.
Let's start from the Flash side. The class we need here is flash.net.FileReference. It allows us to activate system "Open File" dialog to select a file via the browse() method and it's upload() method sends the HTTP request of the "multipart/form-data" MIME type.
So here is a very simple MXML and code-behind for the file upload component:
<?xml version="1.0" encoding="utf-8"?>
<flex:FileUploadBase xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:flex="com.dizainland.hr.flex.*">
<s:Label text="File not selected." id="filePath"/>
<s:Button label="browse" click="browseFile(event)"/>
</flex:FileUploadBase>
package com.dizainland.hr.flex
{
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.FileReference;
import flash.net.URLRequest;
import spark.components.HGroup;
import spark.components.Label;
public class FileUploadBase extends HGroup
{
public var filePath: Label;
protected var fileReference: FileReference = new FileReference();
public function FileUploadBase()
{
super();
fileReference.addEventListener(Event.SELECT, onFileSelected);
}
protected function browseFile(event: MouseEvent):void
{
fileReference.browse();
}
protected function onFileSelected(event: Event):void
{
filePath.text = fileReference.name;
}
public function upload(request: URLRequest, fieldName: String):void
{
fileReference.upload(request, fieldName);
}
}
}
And here is the form itself:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955"
minHeight="600"
xmlns:flex="com.dizainland.hr.flex.*">
<s:VGroup>
<flex:TextFormItem itemName="First Name" id="firstName"/>
<flex:TextFormItem itemName="Mid Name" id="midName"/>
<flex:TextFormItem itemName="Last Name" id="lastName"/>
<flex:TextFormItem itemName="Position" id="position"/>
<flex:TextFormItem itemName="Mobile Number" id="mobileNumber"/>
<flex:TextFormItem itemName="Email" id="email"/>
<flex:FileUpload id="fileUpload"/>
<s:Button label="Add" click="addEmployee(event)"/>
</s:VGroup>
<fx:Script>
<![CDATA[
protected function addEmployee(event: MouseEvent):void
{
var url: String = Configuration.END_POINT_URL;
var request: URLRequest = new URLRequest(url);
request.method = URLRequestMethod.POST;
var employee: URLVariables = new URLVariables();
employee.firstName = firstName.itemValue;
employee.midName = midName.itemValue;
employee.lastName = lastName.itemValue;
employee.position = position.itemValue;
employee.mobileNumber = mobileNumber.itemValue;
employee.email = email.itemValue;
request.data = employee;
fileUpload.upload(request, "employeePhoto");
}
]]>
</fx:Script>
</s:Application>
As you see the trick here is to use FileReference.upload() method for generation of the multipart HTTP request but all textual fields of the form are added in a URLVariables instance to the URLRequest object which we prepare before and which we then feed to the FileReference.upload() method.
Now it is time to show the java side.
As you might already guessed we will process the multipart request in a servlet :)
Theoretically it is possible to write required doPost() method completely from scratch but it will take a lot of time since the anatomy of multipart requests is not trivial. That's why you need to download Apache Commons FileUpload library and add it to your project build path.
It provides a ServletFileUpload class as well as a bunch of other classes that can parse the multipart HTTP request.
The doPost() method is given below:
As you also noticed you will need one more Apache library: Apache Commons IO for some helpers to work with streams.
package com.dizainland.hr.controllers;
import java.io.InputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.IOUtils;
import com.dizainland.hr.data.EmployeeModel;
import com.dizainland.hr.services.EmployeeService;
import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.Email;
@SuppressWarnings("serial")
public class EmployeeControllerServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
{
ServletFileUpload upload = new ServletFileUpload();
try
{
EmployeeModel newEmployee = new EmployeeModel();
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext())
{
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (item.isFormField())
{
String value = Streams.asString(stream);
if (name.equals("firstName"))
{
newEmployee.setFirstName(value);
}
if (name.equals("midName"))
{
newEmployee.setMidName(value);
}
if (name.equals("lastName"))
{
newEmployee.setLastName(value);
}
if (name.equals("position"))
{
newEmployee.setPosistion(value);
}
if (name.equals("mobileNumber"))
{
newEmployee.setMobileNumber(value);
}
if (name.equals("email"))
{
newEmployee.setEmail(new Email(value));
}
}
else
{
byte[] bytes = IOUtils.toByteArray(stream);
Blob photo = new Blob(bytes);
newEmployee.setPhoto(photo);
}
}
new EmployeeService().addEmployee(newEmployee);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
This post should give you the impression of how to handle forms upload. Provided code snippets may be not that accurate and optimal but that is how I prototype applications.
Would be glad to see your comments.
0 comments:
Post a Comment