Apache Commons File Upload – Order Dependency

I have a web page that I’m not responsible for. It has a mixture of inputs – mainly text and file inputs. I also have a Java Servlet that this web page calls, which I didn’t write originally but now look like I’m responsible for maintaining.

Unfortunately the file processing part of the servlet requires that that some of the other parameters have already been passed across – mainly to create the temporary directory to upload the files to. The servlet however just takes the fields as they come, and then blindy does the upload whether or not the required parameters have been set.

During testing its been noticed that these ‘null’ directories are being created. The initial solution was to re-order the original web page to have the parameters come across in the correct order, which is a fine solution, but of course without taking responsibility for that webpage I’m not in a position to guarantee they’ll remain that way. So a better solution needed to be found.

An Example of the Existing Code (with error condition)

A simple jsp page, contain 2 text inputs and a file input. Note the second input comes after the file input. I’m pretending in this example to require the 2nd text input as part of the directory location.

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <form method="POST" action="processFormServlet" enctype='multipart/form-data'>
            <h1>Hello World!</h1>
            <b>Title: </b><input name="title" type="text" />
            <b>File attachment: </b><input class="dropdown" type='file' name='file1' />
            <b>Path: </b><input name="path" type="text" />
            <input type="submit" value="Save" />
        </form>
    </body>
</html>

The servlet.

String path = "";

try {
   ServletFileUpload upload = new ServletFileUpload();
   FileItemIterator iter = upload.getItemIterator(request);

   while (iter.hasNext()) {
      final FileItemStream item = iter.next();
      final String name = item.getFieldName();
      InputStream stream = item.openStream();

      if (item.isFormField()) {
         if (!name.equalsIgnoreCase("submit")) {
            String value = Streams.asString(stream);

             if (name.equalsIgnoreCase("path")) {
                path = value;
             }
             out.println(name + " = " + value + "<br/>");
          }
       } else {
          String filename = item.getName();
          if (!"".equals(filename)) {
             if (path.length() == 0) {
                out.println("Path is zero, about to process file!<br/>");
             }
          }
       }
    }
 } catch (FileUploadException ex) {
    Logger.getLogger(processFormServlet.class.getName()).log(Level.SEVERE, null, ex);
 }

Please note – none of this is production code, just a quick hack to show the issue and attempt to resolve it.

The Solution

I introduced a class to store the filename and the output stream. The output stream however got lost, so I needed to write this into a ByteArrayOutputStream.

The UploadFile.java class

public class UploadFile {
    private String filename;
    private ByteArrayOutputStream outputStream;

    public String getFilename() {
        return filename;
    }

    public ByteArrayOutputStream getOutputStream() {
        return outputStream;
    }

    public UploadFile(String filename, InputStream stream) throws IOException {
        this.filename = filename;
        outputStream = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int len;
        while ((len = stream.read(buf)) > 0) {
            outputStream.write(buf, 0, len);
        }
    }
}

The Updated Servlet Code

            String path = "";

            try {
                ServletFileUpload upload = new ServletFileUpload();
                FileItemIterator iter = upload.getItemIterator(request);
                List<UploadFile> itemsToBeProcessed = new ArrayList();

                while (iter.hasNext()) {
                    final FileItemStream item = iter.next();
                    final String name = item.getFieldName();
                    InputStream stream = item.openStream();

                    if (item.isFormField()) {
                        if (!name.equalsIgnoreCase("submit")) {
                            String value = Streams.asString(stream);

                            if (name.equalsIgnoreCase("path")) {
                                path = value;
                            }
                            out.println(name + " = " + value + "<br/>");
                        }
                    } else {
                        String filename = item.getName();
                        if (!"".equals(filename)) {
                            if (path.length() == 0) {
                                out.println("Path is zero, about to process file!<br/>");
                                itemsToBeProcessed.add(new UploadFile(filename, stream));
                            }
                        }
                    }

                    if ((path.length() > 0) && (itemsToBeProcessed.size() > 0)) {
                        out.println("Got items to process. Size = " + itemsToBeProcessed.size() + "<br/>");
                        for (UploadFile fileToBeUploaded : itemsToBeProcessed) {
                            File file = null;
                            FileOutputStream fos = null;
                            try {
                                file = new File("c:/temp" + File.separator + fileToBeUploaded.getFilename());
                                fos = new FileOutputStream(file);
                                fileToBeUploaded.getOutputStream().writeTo(fos);
                            } catch (Exception e) {
                                System.out.println("Exception raised in Assignment::uploadFile() - Message: " + e.getMessage());
                            } finally {
                                if (fileToBeUploaded.getOutputStream() != null) {
                                    fileToBeUploaded.getOutputStream().close();
                                }

                                if (fos != null) {
                                    fos.close();
                                }
                            }
                        }
                    }
                }
            } catch (FileUploadException ex) {
                Logger.getLogger(processFormServlet.class.getName()).log(Level.SEVERE, null, ex);
            }

Again – the solution isn’t meant as production code. I needs quite a bit of tidying up and refactoring, but I think the essence demonstrates the prolbem and its solution.

It is tempting to finish processing the form, adding all the files into the uploadFile list and processing them at the end. However, but optionally adding them to the list, we can at least process them in the hope the original web page hasn’t been altered.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s