views:

891

answers:

2

How can I parse an uploaded file using Apache Common FileUpload? I tried this:

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request); // This line is where it died.

Unfortunately, the servlet threw an exception without a clear message and cause. Here is the stacktrace:

SEVERE: Servlet.service() for servlet UploadServlet threw exception
javax.servlet.ServletException: Servlet execution threw an exception
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:637)
+2  A: 

You can refer to the link: http://coldjava.hypermart.net/servlets/upload.htm

I do not see a specific problem. In case you are facing a certain problem, do post it.

Kapil Viren Ahuja
just update my code. srry :(
Harry Pham
+7  A: 

To browse and select a file for upload you need a HTML <input type="file"> field in the form. As stated in the HTML specification you have to use the POST method and the enctype attribute of the form have to be set to "multipart/form-data".

<form action="upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" />
</form>

After submitting such a form the binary multipart form data is available in the request body. The Servlet API prior to 3.0 doesn't support builtin facilities to access the multipart form data. The request.getParameter() and consorts would all return null. You can in theory parse it yourself based on HttpServletRequest#getInputStream(). But this is a precise and tedious work which requires precise knowledge of RFC2388. The normal practice is to make use of Apache Commons FileUpload to parse the multpart form data requests. It has an excellent User Guide and FAQ (carefully go through both).

Here's a kickoff example how the doPost() of your UploadServlet may look like when using Commons FileUpload:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
        for (FileItem item : items) {
            if (item.isFormField()) {
                // Process regular form field (input type="text|radio|checkbox|etc", select, etc).
                String fieldname = item.getFieldName();
                String fieldvalue = item.getString();
                // ... (do your job here)
            } else {
                // Process form file field (input type="file").
                String fieldname = item.getFieldName();
                String filename = FilenameUtils.getName(item.getName());
                InputStream filecontent = item.getInputStream();
                // ... (do your job here)
            }
        }
    } catch (FileUploadException e) {
        throw new ServletException("Cannot parse multipart request.", e);
    }

    // ...
}

Alternatively you can also wrap this all in a Filter which parses it all automagically and put the stuff back in the parametermap of the request so that you can continue using request.getParameter() the usual way. You can find an example in this article.

If you're already on the fresh new Servlet 3.0 API, then you can just use standard API provided HttpServletRequest#getParts() to collect the multipart form data items. This is a bit less convenienced than Commons FileUpload (utility methods needed), but still workable. The doPost() of your UploadServlet may then look like instead:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    for (Part part : request.getParts()) {
        String filename = getFilename(part);
        if (filename == null) {
            // Process regular form field (input type="text|radio|checkbox|etc", select, etc).
            String fieldname = part.getName();
            String fieldvalue = getValue(part);
            // ... (do your job here)
        } else if (!filename.isEmpty()) {
            // Process form file field (input type="file").
            String fieldname = part.getName();
            filename = filename.substring(filename.lastIndexOf('/') + 1).substring(filename.lastIndexOf('\\') + 1); // MSIE fix.
            InputStream filecontent = part.getInputStream();
            // ... (do your job here)
        }
    }

    // ...
}

private static String getFilename(Part part) {
    for (String cd : part.getHeader("content-disposition").split(";")) {
        if (cd.trim().startsWith("filename")) {
            return cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
        }
    }
    return null;
}

private static String getValue(Part part) throws IOException {
    String value = new BufferedReader(new InputStreamReader(part.getInputStream(), "UTF-8")).readLine();
    return (value != null) ? value : ""; // Must be empty String according HTTP spec.
}

Also this can be done in a Filter which does all the job transparently so that you can continue using the getParameter() as usual. You can find an example in this article.

Hope this helps :)

BalusC
I try to use Apache FileUpload like you suggest, It get to the servlet go, but it throw an exception at `List items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);` I try to see print the exception in the catch, but nothing would come out. I feel like my `request` is null, so when it parse, it throw an exception
Harry Pham
Read the server logs.
BalusC
Can I see the server log in Eclipse? I been running Tomcat in my Eclipse so when I go the Tomcat folder, the logs in there are like months old
Harry Pham
Ah, it's inside Eclipse. It will by default go to the Eclipse console. Maybe you looked at wrong console instance, or you've (accidently) pinned another console instance. You can switch between consoles using that gray monitor icon, the one-before-rightmost icon. You can unpin console by clicking the icon with green pin once again, the second-before-rightmost icon. This way it will automatically switch to the last active console.
BalusC
I just updated my post.
Harry Pham
The root cause of the exception is missing. Look further in the stacktrace. Isn't there more? To exclude the one and other, you did set form encoding type to `multipart/form-data`?
BalusC
I did. Actually, now it does not throw any exception anymore, however, it does not get in the for loop either. So print out `items.size()`, and I got `0`. Any idea why?
Harry Pham
And the form *did* contain input elements? It's then apparently already been parsed somewhere before. Aren't you already using some MVC (like) framework with file upload capabilities? Such framework might have a `Filter` which takes care about this all. Check your `web.xml` for the case that.
BalusC
Yeah, the form does contain an input element type file and type submit. I dont think I use any frame at all. My web.xml only contain `<servlet>` tag and `<servlet-mapping>` tag. Since my html code on the client side is only 4 lines of code. It must be the server side that mess up. But it just weird, since it get to the servlet but, when it parse the request. It got nothing :(
Harry Pham
It does seems like the problem come from the possibility that I might have parse the request already. sighhh . I might need to clean eclipse to reinstall it. What plugin do u use for Web development for eclipse BalusC? I use WTP(web tool platform) does it come with certain framework that might parse my request without my knowledge?
Harry Pham
I just use Eclipse for Java EE (note the EE). It has everything (including WTP) preinstalled. Further on, as became clear in your other question, you didn't use a real `<form>` at all. You was attempting to upload it using ajax. Do it step by step instead of all different things together at once. This makes excluding and nailing down the root cause of the problem easier.
BalusC
phewww uninstall Eclipse with the newest release version did not solve the problem. Apparently, other ppl having the same problem as I do: http://forum.springsource.org/archive/index.php/t-26786.html. Kind of make me feel less stupid. I think I am going to try your second method that you post up there. This might be a stupid question to ask, but how do I get servlet 3.0 api, because eclipse give error on HttpServletRequest#getParts()
Harry Pham
Just get a Servlet 3.0 implementation. As far Glassfish v3 is the only one which is final. Tomcat 7.0 is underway.
BalusC
I got some obstacles when I try to get Servlet 3 implementation. I Download `Glassfish v3` from https://glassfish.dev.java.net/downloads/v3-final.html.. Create new server `Glassfish v.3 Java EE 6` in Eclipse, then point to the installation directory. In my servlet, I add @MultipartConfig before class definition and add `import javax.servlet.MultipartConfigElement;` and `import javax.servlet.annotation.MultipartConfig;` However I still get the error getParts() is undefined type for `HttpServletRequest`. Any idea why?
Harry Pham
Sounds like as if you copypasted appserver-specific libraries of an older appserver make/version into the webapp's `/WEB-INF/lib`. You shouldn't do that. Get rid of them and leave those libs there where they belongs.
BalusC
Well I started a branch new Dynamic Web Project, and choose my target runtime is `GlassFish v3 Java EE 6` (which is the new glassfish server I created as mentioned above). Therefore I dont have anything inside `/WEB-INF/lib`. However In my `Java Resources:src`, I do see `GlassFish v3 Java EE 6` with all the `.jar` files in there
Harry Pham
The problem symptom suggests that there's still an old `HttpServletRequest` implementation wandering somewhere in the classpath which got precedence in classloading. Maybe it is the `/jre/lib` or `/jre/lib/ext`?
BalusC
Yup. That was the problem. Thank you BalusC
Harry Pham
:( sadly request.getParts() still return me nothing :( sigh. I am not sure what is going anymore. It seems to be a very simple task, not sure why it is not doing what it supposed to :( It might be that it hate MAC, I will try to port the code to my windows box and see what happen. :( :(
Harry Pham
Was the form encoding type set to `multipart/form-data`? Don't you have any filters in the request chain which parse the stream as well?
BalusC
Yup it is. `<form action="http://localhost:8080/Test/Upload" enctype="multipart/form-data" method="post">`. I do not think that I have implemented any filter chain at all. In your article, you implemented `@WebFilter`, but I have not tried it yet
Harry Pham
On the servlet side, I print out the content type of the request: `multipart/form-data; boundary=----WebKitFormBoundary5AQYMuVtcljfz0O1`. So it is multi-part/form-data. Not sure why the data not coming through
Harry Pham
Odd. Try to print each line of `request.getReader()` to stdout with help of `BufferedReader#readLine()`. Do you see any evidence of any input data? Try to add some text fields as well (`input type="text"`) and fill them. Oh, don't call `getInputStream()` or `getParts()` beforehand or it will be empty anyway.
BalusC
When I `BufferedReader(request.getReader())`, and do a `readLine()` from that `BufferedReader()`, I got the what it look like the header of the request back. But nothing more than that. It immediately jump off my `while(br.readLine()!=null)`, here is what I get back from the `BufferedReader`: `------WebKitFormBoundary5XNHKSD1kM3rIP8x--`. Do you think I can zip the project and send it to you to see if it run on your machine? I am not asking you to debug it for me just to see if it run. Plus this is not my full project, I create a brand new one and implement only file upload.
Harry Pham
I just did. Thank you very much for helping me out.
Harry Pham
Your `input` elements are missing the `name` attribute. This denotes the request parameter name.
BalusC
thank you. I tested it, and it worked now. Thank you very much
Harry Pham
You're welcome.
BalusC