1

I'm writing a Java Application, which posts content to a wordpress Rest Api. However I have a problem with programmatically POST a ".png" file with Java SpringBoot, because I don't know how to add the form-data body to HttpEntity<>(body, headers);

I've accomplished this using Postman -> Body -> form-data ->"file":"myFile.png" see screenshots: headers in Postman here Body in Postman here

I've written this code in Java Spring:

private MediaResponse uploadMedia (File graphicsFile) {
    String uploadUrl =  baseUrl + mediaUrl;
    HttpHeaders headers = getHttpHeader();

    headers.add(headerKeyAuthorization, User.getInstance().getUsertoken());
    headers.add("Content-Disposition", "attachment;filename=image.png");
    headers.add("Content-Type", "image/png");

    ...

I thought about doing something like that:

Map<String, File> body = new HashMap<>();
parameters.put("file", new File("image.png"));

HttpEntity requestEntity = new HttpEntity<>(body, headers);

//not interesting in this case
//excecuteMediaRequest(uploadUrl, HttpMethod.POST, requestEntity);

to add the file to the body.

Now my question is: which "key":"value" pairs I have to set in the header(HttpHeaders), and how can I add the file to the body to achieve the same POST like in Postman?

My actual solution of course produces an error:

Exception in thread "main" org.springframework.web.client.RestClientException: Could not write request: no suitable HttpMessageConverter found for request type [java.util.HashMap] and content type [image/png]

Solution:

I've get it to work with a little workaround and @Ajit Somans help. Here is the code that works for my scenario. Note that the methods generateBytArray(), executeMediaRequest() and the class MediaResponse are self written.

/**
 * Uploads media to a rest resource.
 *
 * @param graphicsFile the media file which should be uploaded
 * @return a MediaResponse which has access to resource urls and media information.
 */
private MediaResponse uploadMedia (File graphicsFile) {
    String uploadUrl =  baseUrl + mediaUrl;
    final String filename = graphicsFile.getName();
    //create headers for form data
    HttpHeaders header = getHttpHeader();
    header.set(headerKeyAuthorization, User.getInstance().getUsertoken());
    header.set("Content-Disposition", "form-data;");

    //produces a byte array resource
    ByteArrayResource contentAsResource = new ByteArrayResource(generateBytArray(graphicsFile)){
        @Override
        public String getFilename(){
            return filename;
        }
    };

    MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
    formData.add("file", contentAsResource);
    //create request entity with header and body
    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(formData, header);
    //executes request with in custom method.
    MediaResponse respondingObject = executeMediaRequest(uploadUrl, HttpMethod.POST, requestEntity);
    return respondingObject;
}

As you can see, we don't set the "Content-Type" option, but set the "Content-Disposition" to "form-data" instead of "attachment". The Key Part is to convert the media File, which is .png, into a byte[]. After that we produce a ByteArrayResource like mentioned in this post. At least we're just setting the byte array into the body and execute the request to the given url endpoint.

here the method to convert a File into a byte[]:

 /**
 * generates a byte Array of a file.
 *
 * @param file the file to generate a byte array of.
 * @return byte array of the given file.
 */
private byte[] generateBytArray(File file) {
    byte[] res = new byte[0];
    try {
        //File file = fileResource.getFile();
        BufferedImage image = ImageIO.read(file);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", baos);
        res = baos.toByteArray();

    } catch (IOException e) {
        e.printStackTrace();
    }

    return res;
}

And the excecuting method:

    /**
 * Method to execute a Request to a Rest Api where we want to upload media to.
 *
 * @param url the url endpoint of the resource, where we upload the media file.
 * @param method the http request method, which ist POST in this case.
 * @param entity the http entity where header and body are stored.
 * @return a MediaResponse which has access to resource urls and media information.
 */
private MediaResponse executeMediaRequest(String url, HttpMethod method, HttpEntity entity) {
    ParameterizedTypeReference<MediaResponse> responseType = new ParameterizedTypeReference<MediaResponse>() {};
    ResponseEntity<MediaResponse> response = template.exchange(url, method, entity,
            responseType, MediaResponse.class);

    MediaResponse responseObject = response.getBody();
    logger.info("\n ******** POST MEDIA from response with param: \n " +
                    "Post id: '{}' \n " +
                    "Post REST resource endpoint: '{}' \n" +
                    "Post Permalink '{}'\n *********",
            responseObject.getMediaID(), responseObject.getRestSelfUrl(), responseObject.getPermalink());

    return responseObject;
}

Thanks @Ajit Soman

HasBert
  • 113
  • 1
  • 9
  • 1
    Could you try to upload your png image without setting `content-type:image/png` in postman – Ajit Soman Sep 24 '17 at 08:15
  • 1
    @AjitSoman Yea that works, too. If I do it with Java and Spring like you mentioned in your answer I get a `Exception in thread "main" org.springframework.web.client.HttpServerErrorException: 500 Internal Server Error`. If I set the "Content-Type" programmtically to "image/png" I get a `Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.util.LinkedMultiValueMap] and content type [image/png]` error. Any ideas how to solve this? – HasBert Sep 24 '17 at 18:25

1 Answers1

3

Use LinkedMultiValueMap instead of Map also use FileSystemResource instead of File. Your file upload code may look like this:

    LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
    map.add("file", new FileSystemResource("FILE_LOCATION"));
    HttpHeaders headers = new HttpHeaders();
    headers.set("Content-Type", "image/png");
    .. other headers...
    HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new    HttpEntity<LinkedMultiValueMap<String, Object>>(
                        map, headers);
    RestTemplate template = new RestTemplate();
    String result = template.postForObject("FILE_UPLOAD_URL",requestEntity, String.class);
    System.out.println(result);
    return result;
Ajit Soman
  • 3,926
  • 3
  • 22
  • 41
  • 1
    I tested that and got a `Exception in thread "main" org.springframework.web.client.RestClientException: Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.util.LinkedMultiValueMap] and content type [image/png]` exception, also if I set the `Content-Type` to `application/json`. If I don't add the `headers.set("Content-Type", "image/png");` I got a `500 Internal Server Error`. I've added MessageConverters `FormHttpMessageConverter` `MappingJackson2HttpMessageConverter` to even get to the 500 error. Any ideas? – HasBert Sep 24 '17 at 18:36
  • remove this line :`headers.set("Content-Type", "image/png");` and try again – Ajit Soman Sep 25 '17 at 04:42
  • 1
    I did it and it helped, thanks. I edited my post with the solution, which works for me. – HasBert Sep 25 '17 at 23:01