Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Post multipart form with google-http-java-client

It's not clear from the google-http-java-client* docs how you would go about posting a form that has a file field.

For example I'm trying to print a document using the Google Cloud Print API:

HttpRequestFactory httpRequestFactory = getHttpRequestFactory();

Map<String, Object> parameters = Maps.newHashMap();
parameters.put("printerId", printRequest.getPrinterId());
parameters.put("title", printRequest.getTitle());
parameters.put("contentType", printRequest.getContentType());
parameters.put("ticket", new Gson().toJson(printRequest.getOptions()));

MultipartContent content = new MultipartContent();
content.addPart(new MultipartContent.Part(new UrlEncodedContent(parameters)));
content.addPart(new MultipartContent.Part(
        new FileContent(printRequest.getContentType(), printRequest.getFile())));

try {
    HttpResponse response = httpRequestFactory.buildPostRequest(
            SubmitUrl, content).execute();
    System.out.println(IOUtils.toString(response.getContent()));
} catch (IOException e) {
    String message = String.format();
    System.out.println("Error submitting print job: " + e.getMessage());
}

Unfortunately this doesn't work. The API returns the error "Printer Id required for this request." which seems to me like the request isn't properly formed.

What am I doing wrong?

* I'm specifically using the google-http-java-client as it handles automatic refreshing of OAuth tokens etc for me. Please don't reply with solutions that involve using other HTTP clients.

like image 987
kuhnza Avatar asked Apr 11 '14 00:04

kuhnza


1 Answers

So it looks like I misunderstood how form fields are added to multipart messages. The working code now looks like this

HttpRequestFactory httpRequestFactory = getHttpRequestFactory(username);

Map<String, String> parameters = Maps.newHashMap();
parameters.put("printerid", printRequest.getPrinterId());
parameters.put("title", printRequest.getTitle());
parameters.put("contentType", printRequest.getContentType());

// Map print options into CJT structure
Map<String, Object> options = Maps.newHashMap();
options.put("version", "1.0");
options.put("print", printRequest.getOptions());
parameters.put("ticket", new Gson().toJson(options));

// Add parameters
MultipartContent content = new MultipartContent().setMediaType(
        new HttpMediaType("multipart/form-data")
                .setParameter("boundary", "__END_OF_PART__"));
for (String name : parameters.keySet()) {
    MultipartContent.Part part = new MultipartContent.Part(
            new ByteArrayContent(null, parameters.get(name).getBytes()));
    part.setHeaders(new HttpHeaders().set(
            "Content-Disposition", String.format("form-data; name=\"%s\"", name)));
    content.addPart(part);
}

// Add file
FileContent fileContent = new FileContent(
        printRequest.getContentType(), printRequest.getFile());
MultipartContent.Part part = new MultipartContent.Part(fileContent);
part.setHeaders(new HttpHeaders().set(
        "Content-Disposition", 
        String.format("form-data; name=\"content\"; filename=\"%s\"", printRequest.getFile().getName())));
content.addPart(part);

try {
    HttpResponse response = httpRequestFactory.buildPostRequest(
            SubmitUrl, content).execute();
    System.out.println(IOUtils.toString(response.getContent()));
} catch (IOException e) {
    ...
}

The most important parts above were overriding the default HttpMediaType to specify "multipart/form-data" and adding each field as its own part with a "Content-Disposition" header to designate the form field name.

like image 173
kuhnza Avatar answered Nov 03 '22 00:11

kuhnza