Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring RestTemplate: post both an image and an object at the same time

My users can post a photo of a food stuff and a post of what the food stuff is about to my server.

For example, let say, someone sees something delicious, snaps a picture of it and then writes "Tasty!" underneath the picture. The photo is sent to the server, and the message "Tasty!" including the users name, date, location etc is sent in an object called "Post" to my server using one api call.

I have written the following code on my android side:

    final String url = Constants.POST_PICS;
    RestTemplate restTemplate = RestClientConfig.getRestTemplate(context, true);
    //adding StringHttpMessageConverter, formHttpMessageConverter and MappingJackson2HttpMessageConverter to restTemplate
    restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
    FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
    restTemplate.getMessageConverters().add(formHttpMessageConverter);
    restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
    //putting both objects into a map
    MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
    map.add("image", new FileSystemResource(file));
    map.add("post", post);
    HttpHeaders imageHeaders = new HttpHeaders();
    //setting content type to multipart as the image is a multipart file
    imageHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
    HttpEntity<MultiValueMap<String, Object>> imageEntity = new HttpEntity<MultiValueMap<String, Object>>(map, imageHeaders);
    ResponseEntity<Post> response = restTemplate.exchange(url, HttpMethod.POST, imageEntity, Post.class);
    return response.getBody();

This is the code on the Spring side:

        @RequestMapping(value = "/uploadpostpic", method = RequestMethod.POST)
public Post uploadPostWithPic(@RequestParam("image") MultipartFile srcFile,
                                  @RequestParam("post") Post post) {
    return serviceGateway.uploadPostWithPic(srcFile, post);
}

I'm getting an error:

An exception occurred during request network execution :Could not write request: no suitable HttpMessageConverter found for request type [Model.Post]

org.springframework.http.converter.HttpMessageNotWritableException: Could not write request: no suitable HttpMessageConverter found for request type [Model.Post]

I suspect it is to do with the content-type being set to MULTIPART_FORM_DATA but I need it set to this as I need to transfer the picture up to the server.

Is it even possible to transfer a multipart file and another object upstream using restTemplate at the same time?

EDIT:

I have looked at these posts:

Resttemplate form/multipart: image + JSON in POST

Sending Multipart File as POST parameters with RestTemplate requests

And tried according to their guidance this code:

    final String url = Constants.POST_PIC;
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
    restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
    restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
    restTemplate.getMessageConverters().add(new ResourceHttpMessageConverter());

    FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
    formHttpMessageConverter.addPartConverter(new MappingJackson2HttpMessageConverter());
    formHttpMessageConverter.addPartConverter(new ResourceHttpMessageConverter()); // This is hope driven programming
    formHttpMessageConverter.addPartConverter(new ByteArrayHttpMessageConverter());

    restTemplate.getMessageConverters().add(formHttpMessageConverter);

    MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<>();

    byte[] bFile = new byte[(int) imageFile.length()];
    FileInputStream fileInputStream;

    //convert file into array of bytes
    fileInputStream = new FileInputStream(imageFile);
    fileInputStream.read(bFile);
    fileInputStream.close();

    ByteArrayResource bytes = new ByteArrayResource(bFile) {
        @Override
        public String getFilename() {
            return "file.jpg";
        }
    };

    //post portion of the multipartRequest
    HttpHeaders xHeader = new HttpHeaders();
    xHeader.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<Post> xPart = new HttpEntity<>(post, xHeader);
    multipartRequest.add("post", xPart);

    //picture portion of the multipartRequest
    HttpHeaders pictureHeader = new HttpHeaders();
    pictureHeader.setContentType(MediaType.IMAGE_JPEG);
    HttpEntity<ByteArrayResource> picturePart = new HttpEntity<>(bytes, pictureHeader);
    multipartRequest.add("srcFile", picturePart);

    //adding both the post and picture portion to one httpentity for transmitting to server
    HttpHeaders header = new HttpHeaders();
    header.setContentType(MediaType.MULTIPART_FORM_DATA); 
    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity(multipartRequest, header);
    return restTemplate.postForObject(url, requestEntity, Post.class);

On the other side, the post = null and I'm not sure why it is null.

This is all I'm trying to do on the server side:

public Post uploadPostPic(MultipartFile srcFile, Post post) {
    Post savedPost = repo.save(post);
 }

I'm saving it into my repository and the error is :

java.lang.IllegalArgumentException: Entity must not be null!
like image 219
Simon Avatar asked Dec 05 '15 18:12

Simon


1 Answers

Try something like this: Send jsonString here and later convert it to object using objectwriter.Let me know if you need more explanation.

@RequestMapping(value = "/uploadMultipleFile", method = RequestMethod.POST)
    public @ResponseBody
    String uploadMultipleFileHandler(@RequestParam("name") String[] names,
            @RequestParam("file") MultipartFile[] files) {

        if (files.length != names.length)
            return "Mandatory information missing";

        String message = "";
        for (int i = 0; i < files.length; i++) {
            MultipartFile file = files[i];
            String name = names[i];
            try {
                byte[] bytes = file.getBytes();

                // Creating the directory to store file
                String rootPath = System.getProperty("catalina.home");
                File dir = new File(rootPath + File.separator + "tmpFiles");
                if (!dir.exists())
                    dir.mkdirs();

                // Create the file on server
                File serverFile = new File(dir.getAbsolutePath()
                        + File.separator + name);
                BufferedOutputStream stream = new BufferedOutputStream(
                        new FileOutputStream(serverFile));
                stream.write(bytes);
                stream.close();

                logger.info("Server File Location="
                        + serverFile.getAbsolutePath());

                message = message + "You successfully uploaded file=" + name
                        + "<br />";
            } catch (Exception e) {
                return "You failed to upload " + name + " => " + e.getMessage();
            }
        }
        return message;
    }
}

EDITED:

Eventually, I had to make use of the jsonString to solve my problem. It is not ideal as the url will end up becoming very long but it is the quickest way to solve my problem:

Please have a look at the advice from mykong on how to transform your object to jsonString and retransform them back into objects:

ObjectMapper mapper = new ObjectMapper();
Staff obj = new Staff();

//Object to JSON in String
String jsonInString = mapper.writeValueAsString(obj);

//JSON from String to Object
Staff obj = mapper.readValue(jsonInString, Staff.class);

http://www.mkyong.com/java/jackson-2-convert-java-object-to-from-json/

like image 116
zakaiter Avatar answered Oct 05 '22 13:10

zakaiter