Guides

Storage OnLine: Using S3 APIs with JSPs for file uploads

Below is how to add the upload functionality to the web application example described in the previous guide.

Prerequisites

First of all, let's observe that file uploading with Servlet/JSP is not well defined, meaning that there are no standard calls to manage this functionality, so it is necessary to use external libraries. The most famous and widely used isApache Commons Upload, which has become a common standard. Once we have downloaded the binary distribution (currently version 1.3.1) we copy the file commons-fileupload-1.3.1.jar nto the WEB-INF/lib folder of the web application. In addition to this library, will need to download and copy inWEB-INF/lib also Apache Commons IO, another library of the Apache Commons group dedicated to utility classes for Input/Output.

Multipart requests

In order to upload files via a form, the enctype="multipart/form-data" attribute must be specified in the <form> tag. This way, the browser will send the files we have chosen in the body of the POST. The management of this request on the server side, however, is more complicated than a 'classic' HTTP POST request (not multipart), but with the help of Apache Commons Upload the process is much easier. Basically, you have to scroll through the list of parameters twice:

  • the first, to interpret the parameters passed with the request;
  • the second, to manage the list of uploaded files.

Below is the code that implements the upload of files to Storage OnLine:

<%@page import="java.util.ArrayList"%>
<%@page import="org.jets3t.service.utils.Mimetypes"%>
<%@page import="org.apache.commons.fileupload.FileItem"%>
<%@page import="java.util.List"%>
<%@page import="org.apache.commons.fileupload.servlet.ServletFileUpload"%>
<%@page import="org.apache.commons.fileupload.FileItemFactory"%>
<%@page import="org.apache.commons.fileupload.disk.DiskFileItemFactory"%>
<%@page import="org.jets3t.service.StorageObjectsChunk"%>
<%@page import="org.jets3t.service.model.S3Object"%>
<%@page import="org.jets3t.service.model.S3Bucket"%>
<%@page import="org.jets3t.service.impl.rest.httpclient.RestS3Service"%>
<%@ include file="WEB-INF/includes/common.jsp"%>
<html>
<body>
<c:catch var="ex">
<%
boolean isMultipart = ServletFileUpload.isMultipartContent(request);

String bucketName = null;
String prefix = null;
String parent = null;
List<FileItem> filesToUpload = new ArrayList<FileItem>();
S3Bucket bucket = null;

// se il form è stato inviato...
if (isMultipart) {
    // ... esegui il parsing dei parametri
    FileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload upload = new ServletFileUpload(factory);
    List<FileItem> items = upload.parseRequest(request);
    
    for(FileItem item : items) {
        // memorizza i parametri del form
        if (item.isFormField()) {
            if ("bucketName".equals(item.getFieldName())) {
                bucketName = item.getString();
            } else if ("prefix".equals(item.getFieldName())) {
                prefix = item.getString();
            } else if ("parent".equals(item.getFieldName())) {
                parent = item.getString();
            }
        }
        else {
            // file caricati
            filesToUpload.add(item);
        }
    }
    
    bucket = s3.getBucket(bucketName);
    
    // ora invia i file sullo storage
    for(FileItem item : filesToUpload) {
        // path assoluto all'interno del bucket
        String key = prefix + item.getName();
        
        // crea l'oggetto con i byte inviati
        S3Object s3Object = new S3Object(key, item.get());

        // determina Content-type
        String mimeType = item.getContentType();
        if (mimeType == null || mimeType.isEmpty()) {
            // se non è stato inviato dal browser prova a determinarlo dall'estensione
            mimeType = Mimetypes.getInstance().getMimetype(item.getName());
        }
        if (mimeType != null && !mimeType.isEmpty()) {
          s3Object.setContentType(mimeType);
        }
        
        // invia oggetto
        s3.putObject(bucket, s3Object);
    }
    
    pageContext.setAttribute("message", "File inviati correttamente");
    
}
else {
    // se non è un upload multipart, interpreta i parametri normalmente
    bucketName = request.getParameter("bucketName");
    prefix = request.getParameter("prefix");
    
    bucket = s3.getBucket(bucketName);
}

pageContext.setAttribute("bucketName", bucketName);
pageContext.setAttribute("prefix", prefix);
pageContext.setAttribute("parent", parent);
pageContext.setAttribute("bucket", bucket);
%>
</c:catch>

<c:choose>
  <c:when test="${!empty(ex)}">
    <div class="errorMessage">
      <p>Errore contattando lo storage: <c:out value="${ex.message}" /></p>
    </div>
  </c:when>
  <c:otherwise>
    
    <h2>jets3t test on Hostingsolutions.it Storage OnLine</h2>
    
    <c:if test="${!empty(message)}">
      <div class="message">
        <c:out value="${message}" />
      </div>
    </c:if>
    
    <c:url var="backUrl" value="listObjectsAdvanced.jsp">
      <c:param name="bucketName" value="${fn:escapeXml(bucketName)}" />
      <c:param name="prefix" value="${fn:escapeXml(prefix)}" />
      <c:param name="parent" value="${fn:escapeXml(parent)}" />
    </c:url>
    
    <p><a href="${backUrl}">Indietro</a></p>
    
    <h3>Carica un file</h3>
    
    <p>Bucket: <em><c:out value="${bucketName}" /></em></p>
    <p>Directory: <em><c:out value="${prefix}" /></em></p>
    
    <form action="uploadFile.jsp" method="post" enctype="multipart/form-data">
      <input type="hidden" name="bucketName" value="${fn:escapeXml(bucketName)}">
      <input type="hidden" name="prefix" value="${fn:escapeXml(prefix)}">
      <input type="hidden" name="parent" value="${fn:escapeXml(parent)}">
      <label for="file">Scegli un file da caricare:</label>
      <input type="file" name="file" id="file" />
      <button type="submit">Invia</button>
    </form>

  </c:otherwise>
</c:choose>

</body>
</html>
      

Note that::

  • parameter parsing is performed prior to file upload;
  • the Content-type is assigned according to the browser's instructions or according to the file extension.

Unlike php, we are not so tied to the system libraries, so we can take advantage of advanced features of the libraries. Commons Upload, for example, allows us to specify various operating parameters, such as, for example, the use of temporary files or (as in our case) processing entirely in memory, maximum size and much more.

Lastly, once the file has been uploaded, the page displays a "Indietro" (back) link that takes us back to the file browser located in the directory where we uploaded the file, allowing us to check that the upload was successful.

Resources