Java HTTP Client for Uploading Files (multipart/form-data)

13,398

Your boundaries between parts of data are missing extra two dashes in the beginning: --

I've found this by capturing requests to http://httpbin.org/post made via your program and curl and comparing them via diff tool. I used Wireshark for capturing the requests.

Here's how you can fix this:

private static void addSimpleFormData(String paramName, String wert, PrintWriter body,
                                      final String boundary) {

    body.append("--").append(boundary).append(CRLF);
    body.append("Content-Disposition: form-data; name=\"" + paramName + "\"").append(CRLF);
    body.append("Content-Type: text/plain; charset=" + CHARSET).append(CRLF);
    body.append(CRLF);
    body.append(wert).append(CRLF);
    body.flush();
}

private static void addFileData(String paramName, String filename, byte[] byteStream, PrintWriter body,
                                OutputStream directOutput, final String boundary) throws IOException {

    body.append("--").append(boundary).append(CRLF);
    body.append("Content-Disposition: form-data; name=\"" + paramName + "\"; filename=\"" + filename + "\"")
            .append(CRLF);
    body.append("Content-Type: application/octed-stream").append(CRLF);
    body.append("Content-Transfer-Encoding: binary").append(CRLF);
    body.append(CRLF);
    body.flush();

    directOutput.write(byteStream);
    directOutput.flush();

    body.append(CRLF);
    body.flush();
}

private static void addCloseDelimiter(PrintWriter body, final String boundary) {
    body.append("--").append(boundary).append("--").append(CRLF);
    body.flush();
}

Note extra .append("--") in the beginning of every method.


see https://gist.github.com/shtratos/8e9570a4a5591b2bcecd55ca60b3f24f for full working code

Share:
13,398
Said Savci
Author by

Said Savci

Updated on August 11, 2022

Comments

  • Said Savci
    Said Savci over 1 year

    I'm trying implement an HTTP client that makes multipart requests for uploading files to a HTTP server. The HTML form has three input fields: one for the username, one for the password and one for the file. The server side looks as follows.

    <html>
    <head>
    <title>Uploader</title>
    </head>
    
    <body>
       <div id="header">
             <h1>Uploader</h1>
       </div>
       <div id="content">
             <form id="uploadformular" action="upload" method="post"
                    enctype="multipart/form-data" accept-charset="utf-8">
                    <div class="block">
                           <label for="user">Username</label> <input type="text" id="user"
                                  name="myuser" required />
                    </div>
                    <div class="block">
                           <label for="password">Password</label> <input type="password" id="pin"
                                  name="mypassword" required />
                    </div>
                    <div class="block">
                           <label for="file">ZIP File</label> <input type="file" id="file"
                                  name="myfile" required />
                    </div>
                    <div>
                           <input type="submit" value="Upload" />
                    </div>
             </form>
       </div>
    
    </body>
    </html>
    

    My implementation is as follows.

    public class MultipartUploader {
    
        private static final String CHARSET = "UTF-8";
    
        private static final String CRLF = "\r\n";
    
        public String httpUpload(String url, String filename, byte[] byteStream)
            throws MalformedURLException, IOException {
    
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            final String boundary = Strings.repeat("-", 15) + Long.toHexString(System.currentTimeMillis());
    
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Connection", "Keep-Alive");
            connection.setRequestProperty("Cache-Control", "no-cache");
            connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
    
            OutputStream directOutput = connection.getOutputStream();
            PrintWriter body = new PrintWriter(new OutputStreamWriter(directOutput, CHARSET), true);
    
            body.append(CRLF);
            addSimpleFormData("myuser", "myUserName", body, boundary);
            addSimpleFormData("mypassword", "mySecretPassword", body, boundary);
            addFileData("myfile", filename, byteStream, body, directOutput, boundary);
            addCloseDelimiter(body, boundary);
    
            int responseCode = connection.getResponseCode();
            String responseMessage = connection.getResponseMessage();
    
            String payload = CharStreams.toString(new InputStreamReader(connection.getInputStream()));
            return payload;
        }
    
        private static void addSimpleFormData(String paramName, String wert, PrintWriter body,
            final String boundary) {
    
            body.append(boundary).append(CRLF);
            body.append("Content-Disposition: form-data; name=\"" + paramName + "\"").append(CRLF);
            body.append("Content-Type: text/plain; charset=" + CHARSET).append(CRLF);
            body.append(CRLF);
            body.append(wert).append(CRLF);
            body.flush();
        }
    
        private static void addFileData(String paramName, String filename, byte[] byteStream, PrintWriter body,
            OutputStream directOutput, final String boundary) throws IOException {
    
            body.append(boundary).append(CRLF);
            body.append("Content-Disposition: form-data; name=\"" + paramName + "\"; filename=\"" + filename + "\"")
                .append(CRLF);
            body.append("Content-Type: application/octed-stream").append(CRLF);
            body.append("Content-Transfer-Encoding: binary").append(CRLF);
            body.append(CRLF);
            body.flush();
    
            directOutput.write(byteStream);
            directOutput.flush();
    
            body.append(CRLF);
            body.flush();
        }
    
        private static void addCloseDelimiter(PrintWriter body, final String boundary) {
    
            body.append(boundary).append("--").append(CRLF);
            body.flush();
        }
    }
    

    The server responds with 200 OK. The problem I have is that somehow the HTTP body is not correctly created so that the response I get from the server says that not all fields of the form are set. The server doesn't say which field it is. So my question is do you see any problem with this code? Do I create the multipart request correctly?

    I also tried to upload a file using cURL with the following command and it worked.

    cURL -F "myuser=myUserName" -F "mypassword=mySecretPassword" -F "myfile=@/path/to/my/file.zip" "http://abcdef.gh:1234/path/to/uploader"