7

I am trying to upload file (multi part form data) using HTTPHandler.

WebKit Boundary is getting written to the destination file, thus corrupting the file.

Input files can be any kind of files, including text, zip, apk etc.

Code:

    public void handle(HttpExchange httpExchange) throws IOException {

    URI uri = httpExchange.getRequestURI();
    String httpReqMethod = httpExchange.getRequestMethod();
    Headers headers = httpExchange.getRequestHeaders();
    InputStream  inputStrm = null;
    FileOutputStream destFile = null;
    String contentType = ((headers.get("Content-type") != null) ? (headers.get("Content-type").toString()) : (null));
    httpExchange.getRequestURI().getQuery());

    Map<String, String> queryParams = queryToMap(httpExchange.getRequestURI().getQuery());
    Set<String> keys= headers.keySet();
    Iterator<String> itr = keys.iterator();
    while(itr.hasNext())
    {
      String key = (String)itr.next();
    }

    File file = new File(ACEConstants.WEB_SERVER_CTX_ROOT + uri.getPath()).getCanonicalFile();
    String resource = uri.getPath().substring(
              uri.getPath().indexOf(ACEConstants.WEB_SERVER_CTX_ROOT)+ACEConstants.WEB_SERVER_CTX_ROOT.length()+1);

      if(httpReqMethod.equals(ACEConstants.HTTP_REQUEST_METHOD_POST) )
      {
        if(contentType != null && contentType.contains("multipart/form-data"))
        {
          if(resource.equals("fileUpload"))
          {
            inputStrm = httpExchange.getRequestBody();
            destFile = new FileOutputStream(new File("D:\\"+queryParams.get("fileName")));
            String contentLength = headers.get("Content-length").toString();
            long fileSize = (Long.parseLong(contentLength.substring(1, contentLength.length()-1)));
            int iteration = 1;
            long bytesToBeRead = (fileSize > 1024) ? ((iteration * 1024)) : (inputStrm.available());
            long bytesRemaining = (fileSize) - (iteration * 1024);
              byte[] bytes = new byte[1024];

            if(fileSize <= 1024) 
            {
              bytes = new byte[inputStrm.available()];
              inputStrm.read(bytes);
              destFile.write(bytes);
            }
            else {

              while (inputStrm.read(bytes) != -1) {
                iteration++;
                destFile.write(bytes);
                bytesRemaining =  ( fileSize - ((iteration-1) * 1024));
                if (bytesRemaining >= 1024) {
                  bytesToBeRead = 1024;
                  bytes = new byte[1024];
                }
                else {
                  bytes = new byte[inputStrm.available()];

                  inputStrm.read(bytes);
                  destFile.write(bytes);
                  break;
                }
              }
            }
            destFile.close();
          }
        } 
      }
    }

Here's the HTML code

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript">

        function processForm(frm)
        {
            var fu1 = document.getElementsByName("datafile");
            var filename = fu1[0].value;
            filename = filename.substring(filename.lastIndexOf("\\")+1);
            alert("You selected " + filename);
            frm.action = "http://localhost:64444/ACE/fileUpload?fileName="+filename;
            return true;
        }

    </script>
</head>
<body>

<form name="myForm" enctype="multipart/form-data" method="post" acceptcharset="UTF-8" onsubmit="processForm(this);">
    <p>
        Please specify a file, or a set of files:<br>
        <input type="file" name="datafile" size="40">
    </p>
    <div>
        <input type="submit" value="Send">
    </div>
</form>

</body>
</html>

What is going wrong here? Help would be much appreciated.

EDIT 1:

If the input file is a text file containing text : 1234567890
The output file has contents :

 ------WebKitFormBoundaryKBRUiUWrIpW9wq2j
Content-Disposition: form-data; name="textline"


------WebKitFormBoundaryKBRUiUWrIpW9wq2j
Content-Disposition: form-data; name="datafile"; filename="test.txt"
Content-Type: text/plain

1234567890
------WebKitFormBoundaryKBRUiUWrIpW9wq2j--
5
  • Please share some errors, what get's written... something showing the error. Commented Nov 20, 2015 at 9:19
  • You asked for multipart/form-data and that's what you get there. Did you check stackoverflow.com/questions/2422468/… ? Commented Nov 20, 2015 at 11:18
  • @Jan : How do I get the HTTPServletrequest through HTTPExchange? Commented Nov 20, 2015 at 12:46
  • You don't. But you can still use apache commons fileupload by creating a custom wrapper - see my answer. Commented Nov 24, 2015 at 10:25
  • What error are you getting ? Commented Nov 24, 2015 at 15:29

2 Answers 2

10
+50

How to Upload Files with HttpHandler

File Upload to HttpHandler results in boundary and other MIME information being written into the request contents. As parsing this information is quite comples and error-prone, one could resort to use Commons FileUpload which is proven to work great in classic Servlet environments.

Consider this example with a custom made ContextRequest. This would handle all the parsing of boundaries added into your request body by multipart/form-data while still allowing you to keep HttpHandler interface.

The main idea consists of implementing an own version of ContextRequest to work with the data provided in HttpHandler:

   public HttpHandlerRequestContext implements RequestContext {

          private HttpExchange http;

          public HttpHandlerRequestContext(HttpExchange http) {
                this.http = http;
          }

          @Override
          public String getCharacterEncoding() {
                //Need to figure this out yet
                return "UTF-8"; 
          }

          @Override
          public int getContentLength() {
                //tested to work with 0 as return. Deprecated anyways
                return 0; 
          }

          @Override
          public String getContentType() {
                //Content-Type includes the boundary needed for parsing
                return http.getRequestHeaders().getFirst("Content-type");
          }

          @Override
          public InputStream getInputStream() throws IOException {
                 //pass on input stream
                return http.getRequestBody();
          }
 }

For reference: Here's a working example listing the received files.

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map.Entry;

import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class HttpServerTest {

    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
        server.createContext("/fileupload", new MyHandler());
        server.setExecutor(null); // creates a default executor
        server.start();
    }

    static class MyHandler implements HttpHandler {
        @Override
        public void handle(final HttpExchange t) throws IOException {
            for(Entry<String, List<String>> header : t.getRequestHeaders().entrySet()) {
                System.out.println(header.getKey() + ": " + header.getValue().get(0));
            }
            DiskFileItemFactory d = new DiskFileItemFactory();      

            try {
                ServletFileUpload up = new ServletFileUpload(d);
                List<FileItem> result = up.parseRequest(new RequestContext() {

                    @Override
                    public String getCharacterEncoding() {
                        return "UTF-8";
                    }

                    @Override
                    public int getContentLength() {
                        return 0; //tested to work with 0 as return
                    }

                    @Override
                    public String getContentType() {
                        return t.getRequestHeaders().getFirst("Content-type");
                    }

                    @Override
                    public InputStream getInputStream() throws IOException {
                        return t.getRequestBody();
                    }

                });
                t.getResponseHeaders().add("Content-type", "text/plain");
                t.sendResponseHeaders(200, 0);
                OutputStream os = t.getResponseBody();               
                for(FileItem fi : result) {
                    os.write(fi.getName().getBytes());
                    os.write("\r\n".getBytes());
                    System.out.println("File-Item: " + fi.getFieldName() + " = " + fi.getName());
                }
                os.close();

            } catch (Exception e) {
                e.printStackTrace();
            }            
        }
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

works like a charm. Thanks!!! Just fetched the content type from FileItem.getContentType(), and wrote the content using os.get().
This solution is not working anymore. I am getting this error cannot access HttpServletRequest.
Hi @Dhruvgarg, what exactly are you trying to do? I don't use HttpServletRequest in my example. You might be missing an import / a dependency to servlet-api?
Hi, I was trying to implement very simple web framework using annotations in java (for learning only) and was unable to manually implement form-data logic. I added javax.servlet-api and now it's working, sorry my mistake, I didn't knew servlet is being used internally.
Hi, it does not work for HTTPS, it seems caused by 2 certificates, my HttpsServer has its own certificate, and the servlet has another one, I guest.
-1

I have faced same issue please use Apache httpClient API for it its best api for connection with URL(file uploading and dealing with multipart form data)

1 Comment

Question was about receiving end - httpClient is for sending end (where HTML is used...)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.