Showing posts with label servlets. Show all posts
Showing posts with label servlets. Show all posts

Monday, January 07, 2013

Reading multiple files with servlets. HTML 5.

Intro

In this example we check how to process the form having one input of file type where a user chooses several files at once. We use Servlet specification of version 3.0 which is implemented in Tomcat server 7. First of all I'd recommend to get familiar with this post to perform proper configuration of your Java application and development environment. Then.. Let's start.
The example (tutorial) application will use simple html page (JSP is not used in this example). That page has one form allowing to choose several files through one dialog and then post button to submit the data. Under the form there will be a frame where the form will return the result to.
Not to make the example too complex lets say our application will be printing the content of each file to that frame with the corresponding filenames. Once you understand how it works you may process the files in any other way rather than printing their content.
So we need to pass through several steps to accomplish the goal. They are:

  1. Create html form
  2. Create servlet
  3. Process the data (read multiple files with servlet)

HTML Form

Form is the facility to post your data from your client to the server. Either you're using CGI approach or servlet like in this example you have to consider two things:
  • which data and in which format you're going to send
  • which URL the server should interpret as your data handler
The simple page body we'll be using looks like this

HTML form - Upload files with servlet example


<body>
 <form id="example-form" 
  action="HandlerServlet" 
  method="post"
  enctype="multipart/form-data" 
  target="resultFrame">
   <input type="file" name="example-input" multiple />
   <input type="submit"/>
 </form>
 <br />
 <iframe name="resultFrame" frameborder="1" width="100%" height="400" scrolling="yes" />
</body>



There are some important things in lines: 3,5,6 and 7.

  • HandlerServlet is actually relative URL to be considered by the server as handler of the data that is coming from your form
  • enctype is the attribute which should be set to "multipart/form-data" if you're going send file content. Otherwise you'll be sending the only file names
  • target attribute specifies the frame the server response will be printed out to (you should have the frame with corresponding name on your page like we do)
  • multiple is the attribute wich should be specified if you'd like to choose several files simultaneously (not working in IE pre 10)
Now you're ready to write some server code.

Creating servlet

Writing a servlet is not a rocket science. You simply need to create new class, extend it from javax.servlet.http.HttpServlet and override some methods. We're going to override the only one - "protected void doPost(..)" which actually takes the client request wrapped with "HttpServletRequest req". Latter contains all the data we need to reach our goal (retrieve the content of the files sent from the client). So the skeleton will look like this:

@MultipartConfig
public class HandlerServlet extends HttpServlet{

 /**
  * 
  */
 private static final long serialVersionUID = -717277851360696409L;

 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {

 }
}
NB! Notice that the annotation (line: 1) is pretty much important. Having no such one specified you would not be able to parse your request into the parts.
Register your servlet in web.xml file so that the server knows which URL is associated with it. Use this example:

  <servlet>
    <description></description>
    <display-name>HandlerServlet</display-name>
    <servlet-name>HandlerServlet</servlet-name>
    <servlet-class>your.package.HandlerServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>HandlerServlet</servlet-name>
    <url-pattern>/HandlerServlet</url-pattern>
  </servlet-mapping>

, where your.package is the package you place your servlet to.

Lets now distract from the java code and check what the client actually sends. To know that we need to use some traffic sniffer. I use Fiddler. So, I prepared two files and going to send them to the server (no matter we have no one yet).
If you configured your project right (as described here) you should be ale to start your project. Start it and open your page with the form. Send some files (nothing will be done on the server as we haven't implemented doPost yet). 
The data captured by Fiddler looks like this

----------------------------------------------------------------------
-----------------------------187161971819895
Content-Disposition: form-data; name="example-input"; filename="file1.txt"
Content-Type: text/plain

string 11
string 12
-----------------------------187161971819895
Content-Disposition: form-data; name="example-input"; filename="file2.txt"
Content-Type: text/plain

string 21
string 22
string 23
-----------------------------187161971819895--
----------------------------------------------------------------------

Processing the data

The request sends the data divided by the parts. Once we get the request in the servlet we need the approach to distinguish those parts and handle each piece.
Multipart schema - Upload several files with servlet example

Servlet specification of version 3.0 provides convenient mechanism to parse such the parts. So let's see how to get the pieces of the request data

Collection<Part> webParts = req.getParts();

Now you have the collection of the separate parts. The last thing to we need to do is somehow print the file content and the file name. Here we encounter another pitfall of mutlipart processing. As it is seen from the capture dump both the files are assigned with the same input name "example-input" so we have to distinguish them basing on something else. The good choice is to do that by the file name. So we need to get Content-Disposition header for each retrieved part. Add the following method to your servlet:

 private String getFileName(Part part){
  Pattern p = Pattern.compile("filename=\"(.+?)\"");
  Matcher m = p.matcher(part.getHeader("Content-Disposition"));
  if(m.find()){
   return m.group(1);
  }else{
   throw new IllegalStateException("Cannot fin filename in web request header.");
  }
 }

This method will help us to determine the file name which is stored in the part we're currently processing.
Each part provides the inputStream so that one can retrieve the data from the part. Lets finally do the following steps:
  1. Get all the parts which are coming with the request
  2. While coming through the collection get the inputStream of the item
  3. Get the name of sequential file from the part
  4. Wrap the inputStream of the sequential part with LineNumberReader.
  5. Read the part content line-by-line
  6. Print the lines to the servlet output (use resp.getWriter().println(..))
So the final result will be looking so:

@MultipartConfig
public class HandlerServlet extends HttpServlet{

 /**
  * 
  */
 private static final long serialVersionUID = -717277851360696409L;

 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {

  Collection<Part> webParts = req.getParts();
  for(Part webPart: webParts){
   resp.getWriter().println("File Name: " + getFileName(webPart));
   LineNumberReader lnr = new LineNumberReader(new InputStreamReader(webPart.getInputStream()));
   String line = null;
   while((line = lnr.readLine()) != null){
    resp.getWriter().println(line);
   }
  }
  resp.flushBuffer();  
 }
 
 private String getFileName(Part part){
  Pattern p = Pattern.compile("filename=\"(.+?)\"");
  Matcher m = p.matcher(part.getHeader("Content-Disposition"));
  if(m.find()){
   return m.group(1);
  }else{
   throw new IllegalStateException("Cannot fin filename in web request header.");
  }
 }

}
That is it!

Tuesday, November 20, 2012

How to prepare your Eclipse IDE for web development

Objective

Once you're going to start developing some java-based web-applications or just static web-sites you need to perform some simple steps to configure your development environment. Basically you need three things to start developing:
  1. Eclise IDE (J2EE edition)
  2. Tomcat server (Servlet container and servlet specification implementation)
  3. JDK to support basic java features along with runtime environment
Lets go step-by-step here. At the moment of this post publishing the latest version of Eclipse IDE was Juno release.  Download it from here so that you've got the version suitable for your operating system. I'm using Windows 7 64bit so all further statements will be related to it. 
That will be great to create some sandbox so that you can use it as isolated disk space to have some practice. After you're pretty much familiar with the configuration you can repeat the steps with your own paths. Say, create the following folder where all the magic is going to happen: c:/sandbox. Create Projects subfolder to keep Eclipse project there.

Configure your Eclipse and Tomcat instances

Extract the eclipse archive to c:/sandbox. So you now have your IDE under c:/sandbox/Eclipse. Start it up (lets not talk about performance tuning of your IDE - this is not our topic here). Set up the workspace to Now you have IDE working. Unfortunately that's not enough to start coding. Close welcome screen.
You won't be able to code a piece of java code until you have JDK installed. Follow this link to download J2SE 6 for your operating system (make sure you're registered as oracle user as you will have to input your oracle credentials).

Install the distribution to C:\sandbox\JDK_SE6. Switch to Eclipse.
  • Go to Window\Preferences
  • In the preferences tree go to Java\Installed JREs
  • Click [Add] button. Choose Standard VM in new dialog and click Next
  • Specify folder C:\sandbox\JDK_SE6 for JRE home. Once you do that all other fields are pre-filled automatically
  • Now you may remove all other VMs from the list if they are.
  • Click OK to close preferences dialog
You now can write and execute Java code. However we're going to create web application, aren't we? Hence we need to install servlet implementation. That's also good to have a web server. Fortunately all those stuff is combined under Apache Tomcat server. Lets use the latest version at the moment. You may take it from here. Unpack the content of the archive to our sandbox so that we have Apache Tomcat server under C:\sandbox\apache-tomcat-7.0.32.

Lets bind eclipse to that apache instance.
  • Go to Window\Preferences again. In preferences tree go to Server\Runtime Environments.
  • Click [Add...] button
  • In "New Server Runtime Environment" dialog choose Apache Tomcat v7.0. Check "Create a new local server" cehck-box. Click Next
  • Click [Browse...] button and choose the folder C:\sandbox\apache-tomcat-7.0.32
  • Under JRE you may choose either default JRE of workbench or JDK_SE6. They are all the same. However lets choose the second option to be more defined.
  • Click Finish and then OK to close preferences window.

Creating a template project.

Lets now create some project to get aware how to create the project each time you want to start trying new create something new.
  • In Eclipse press Ctrl+N so that "New" dialog is opened. 
  • Choose "Dynamic Web Project" and click Next
  • There type some project name (note that the project path is attached automatically to our sandbox)
  • Choose Apache Tomcat v7.0 as target runtime
  • Set dynamic web module version to 3.0
  • Click Next until it gets disabled :)
  • There check the check-box "Generate web.xml deployment descriptor"
  • Finally click [Finish]
So.. Congratulations! You've now got web project configured. To make sure it's working lets do the following.
  • In your Project Explorer expand WebContent folder
  • Right-click the folder and choose New->Html file
  • Name it index.html and click okay
  • Within <body>...</body> enclosure  add piece of html-code like <h1>It really works!</h1>. Save the changes
  • Right click the project root in project explorer. Select "Run as -> Run on Server"
  • You should now see your html page which was deployed to the web-server and now loaded from it
That's all