AddThis

Tuesday 28 August 2018

Uploading Files (Servlet 3.0)


Before Servlet 3.0, processing file upload requires 3rd party libraries such as Apache Commons FileUpload. Servlet 3.0 (which requires Tomcat 7) builds in file upload support.
File upload over HTTP is specified in "RFC 1867 Form-based File Upload in HTML". Read "File Upload using multipart/form-data POST Request".
Client-side HTML Form: "FileUpload.html"
On the client-side, you provide an HTML <form> with an input element <input type="file"> as follows:
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h2>Upload File</h2>
    <form method="post" enctype="multipart/form-data" action="upload">
      Choose a file: <input type="file" name="uploadedFile" /><br />
      <input type="submit" />
    </form>
  </body>
</html>

Server-side
Servlet 3.0 introduces a new annotation @MultipartConfig with these attributes:
1.                 location: An absolute path to a directory in your file system (NOT relative to your context root) to store files temporarily while processing parts, when the file is bigger than fileSizeThreshold. This directory shall exist; otherwise, an IOException will be thrown.
2.                 fileSizeThreshold: The file size in bytes after which the file will be stored temporarily in the location.
3.                 maxFileSize: The maximum file size in bytes. If the size of the file is bigger than this, Tomcat throws an IllegalStateException.
4.                 maxRequestSize: The maximum size in bytes for the entire multipart/form-data request (i.e., all the parts).
For example,
@MultipartConfig(location="d:\\temp\\upload", fileSizeThreshold=1024*1024, 
maxFileSize=5*1024*1024, maxRequestSize=2*5*1024*1024)
A new interface javax.servlet.http.Part is also introduced to represent a part of a form item that were received within a multipart/form-data POST request. The following methods are declared:
1.                 getName(): Get the name of this part.
2.                 getSize(): Get the size of this part.
3.                 getInputStream(): Get the content of this part as an InputStream.
4.                 write(String filename): Write this part to file. The filename is relative to the "location" in @MultipartConfig. The container may simply rename the temporary file. Existing file will be overridden.
5.                 delete(): Delete the underlying storage, including the temporary file. Do not call delete() after write().
6.                 getContentType(): Get the content type of this part.
7.                 getHeader(String name)getHeaders(String name)getHeaderNames(): Get the header.
The method request.getParts() (in javax.servlet.http.HttpServletRequest) returns a collection of all parts. The request.getPart(String name) returns a Part for given name attribute (if you have other input elements besides file).
"FileUploadServlet30.java" with URL "/upload"
package com.programingsoeasy;

import java.io.*;
import java.util.Collection;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet(
   name = "upload",
   urlPatterns = {"/upload"})
@MultipartConfig(
   location="d:\\temp\\upload",
   fileSizeThreshold=1024*1024,
   maxFileSize=5*1024*1024,
   maxRequestSize=2*5*1024*1024)
public class FileUploadServlet30 extends HttpServlet {

   @Override
   protected void doPost(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      Collection<Part> parts = request.getParts();

      try {
         out.write("<h2>Number of parts : " + parts.size() + "</h2>");
         for(Part part : parts) {
            printPartInfo(part, out);
            String filename = getFileName(part);
            if (filename != null) {
               part.write(filename); // relative to location in @MultipartConfig
            }
         }
       } finally {
         out.close();
      }
   }

   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
      getServletContext()
              .getRequestDispatcher("/FileUpload.html")
              .forward(request, response);
   }

   // Print the headers for the given Part
   private void printPartInfo(Part part, PrintWriter writer) {
      StringBuilder sb = new StringBuilder();
      sb.append("<p>Name: ").append(part.getName()).append("<br>");
      sb.append("ContentType: ").append(part.getContentType()).append("<br>");
      sb.append("Size: ").append(part.getSize()).append("<br>");
      for(String header : part.getHeaderNames()) {
         sb.append(header).append(": ").append(part.getHeader(header)).append("<br>");
      }
      sb.append("</p>");
      writer.write(sb.toString());
   }

   // Gets the file name from the "content-disposition" header
   private String getFileName(Part part) {
    for (String token : part.getHeader("content-disposition").split(";")) {
      if (token.trim().startsWith("filename")) {
        return token.substring(token.indexOf('=') + 1).trim()
            .replace("\"", "");
      }
    }
    return null;
  }
}

Total parts : 1

Name: uploadedFile
ContentType: text/plain
Size: 811
content-type: text/plain
content-disposition: form-data; name="uploadedFile"; filename="uploadedFile.txt"
1.                 The client-side "FileUpload.html" has one submission part, i.e., <input type="file">.
2.                 The printPartInfo() prints the headers of the given part. The output is as shown above.
3.                 The part.write() method is used to write the file under the location of the @MultipartConfig with the filename extracted from the content-disposition header.
An Multi-part HTML Form - "FileUploadMultipart.html"
The HTML form below has four input fields, which will be sent in four parts, as shown in the output below.
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h2>Upload File</h2>
    <form method="post" enctype="multipart/form-data" action="upload">
      Who are you: <input type="text" name="username" /><br />
      Choose the file to upload: <input type="file" name="file1" /><br />
      Choose another file to upload: <input type="file" name="file2" /><br />
      Comments:<br />
      <textarea name="comment"></textarea><br />
      <input type="submit" value="SEND" />
    </form>
  </body>
</html>

Number of parts : 4

Name: username
ContentType: null
Size: 5
content-disposition: form-data; name="username"

Name: file1
ContentType: text/plain
Size: 811
content-type: text/plain
content-disposition: form-data; name="file1"; filename="uploadedFile.txt"

Name: file2
ContentType: audio/mpeg
Size: 4842585
content-type: audio/mpeg
content-disposition: form-data; name="file2"; filename="away.mp3"

Name: comment
ContentType: null
Size: 7
content-disposition: form-data; name="comment"
Notes:
1.                 You can use request.getPart(name) to retrieve a particular part with the given name attribute, instead of request.getParts(), which retrieves all parts in a Collection<Part>.
2.                 You can also use the following code to read the data from each part:
InputStream instream = request.getPart(part.getName()).getInputStream();
int byteRead;
while ((byteRead = instream.read()) != -1) {
   out.write(byteRead);
}




2 comments:

100 AWS Services in Just One Line Each

  100 AWS Services in Just One Line Each Amazon EC2:  Virtual servers in the cloud. Amazon S3:  Scalable object storage service. Amazon RDS:...