Android SpringRestTemplate:同时发布图像和对象

Android SpringRestTemplate:同时发布图像和对象,android,spring,spring-boot,resttemplate,Android,Spring,Spring Boot,Resttemplate,我的用户可以在我的服务器上发布食物的照片和食物的内容 例如,假设有人看到美味的东西,拍下它的照片,然后在照片下面写上“美味!”。照片被发送到服务器,消息“Tasty!”(包括用户名、日期、位置等)通过一个名为“Post”的对象通过一个api调用发送到我的服务器 我在android端编写了以下代码: final String url = Constants.POST_PICS; RestTemplate restTemplate = RestClientConfig.getRest

我的用户可以在我的服务器上发布食物的照片和食物的内容

例如,假设有人看到美味的东西,拍下它的照片,然后在照片下面写上“美味!”。照片被发送到服务器,消息“Tasty!”(包括用户名、日期、位置等)通过一个名为“Post”的对象通过一个api调用发送到我的服务器

我在android端编写了以下代码:

    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();
我得到一个错误:

请求网络执行期间发生异常:无法 写入请求:找不到适合请求类型的HttpMessageConverter [范本邮递]

org.springframework.http.converter.HttpMessageNotWritableException: 无法写入请求:找不到适合的HttpMessageConverter 请求类型[Model.Post]

我怀疑这与将内容类型设置为MULTIPART_FORM_DATA有关,但我需要将其设置为此,因为我需要将图片传输到服务器

甚至可以同时使用restTemplate向上游传输多部分文件和另一个对象吗

编辑:

我看过这些帖子:

并根据他们的指导尝试此代码:

    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);
我正在将其保存到我的存储库中,错误为:

java.lang.IllegalArgumentException: Entity must not be null!

我没有看到您的Post类的注册HttpMessageConverter。您可能需要为多值映射注册一个HttpMessageConverter。

好的,几周前我遇到了同样的问题。首先要明确
多部分/表单数据
内容类型的含义:

“多部分/表单数据”消息包含一系列部分,每个部分 表示成功的控件

成功的控件对于提交是“有效的”。每一个成功的 控件将其控件名与其当前值配对作为 提交的表单数据集

简单地说,使用多部分表单数据,您可以向服务器发送不同内容类型的数据。以下是一个示例:

POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:29.0) Gecko/20100101 Firefox/29.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: __atuvc=34%7C7; permanent=0; _gitlab_session=226ad8a0be43681acf38c2fab9497240; __profilin=p%3Dt; request_method=GET
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------9051914041544843365972754266
Content-Length: 554

-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="text"

text default
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain

Content of a.txt.

-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html

<!DOCTYPE html><title>Content of a.html.</title>

-----------------------------9051914041544843365972754266--
简单地说,
FormHttpMessageConverter
无法找到正确的消息转换器来写入对象
Post
。如果希望
Post
以JSON格式编写,则应将
MappingJackson2HttpMessageConverter
添加到
partConverters

@Produces
    public RestTemplate getRestTemplate() {
        RestTemplate template = new RestTemplate();
        template.getMessageConverters().add(0,createFormHttpConverter());
        return template;
    }
 private static HttpMessageConverter<?> createFormHttpConverter(){

        FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
        formHttpMessageConverter.setPartConverters(getPartConverters());
        return formHttpMessageConverter;
    }

private static List<HttpMessageConverter<?>> getPartConverters(){
    RestTemplate template = new RestTemplate();
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    List<HttpMessageConverter<?>> messageConverters = template.getMessageConverters();
    messageConverters.add(0,converter);
    return messageConverters;
}
@products
公共RestTemplate getRestTemplate(){
RestTemplate=新的RestTemplate();
template.getMessageConverters().add(0,createFormHttpConverter());
返回模板;
}
专用静态HttpMessageConverter createFormHttpConverter(){
FormHttpMessageConverter FormHttpMessageConverter=新FormHttpMessageConverter();
formHttpMessageConverter.setPartConverters(getPartConverters());
返回表单HttpMessageConverter;
}
私有静态列表>messageConverters=template.getMessageConverters();
messageConverters.add(0,converter);
返回消息转换器;
}

您需要告诉Spring如何将请求参数映射到您的对象。您可以通过实现自定义的
HttpMessageConterter
来实现这一点,就像Alexander建议的那样,但我认为有一种更简单的方法:使用命令对象(有时称为表单备份对象):


您需要配置/注册springs多部分解析器,并需要将请求作为多部分请求发送。

我使用以下命令做了类似的事情:

  HttpHeaders headers = new HttpHeaders();
  headers.add("Accept","application/json");     
  headers.setContentType(MediaType.MULTIPART_FORM_DATA);
  MultiValueMap<String, Object> map =  new LinkedMultiValueMap<String, Object>();
  map.add("image", new FileSystemResource(file));
  map.add("post", post);
  HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(map, headers);
  RestTemplate restTemplate = RestClientConfig.getRestTemplate(context, true);
  restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
  ResponseEntity<Post> response = restTemplate.postForObject(url, requestEntity, Post.class);
HttpHeaders=newhttpheaders();
添加(“接受”、“应用程序/json”);
headers.setContentType(MediaType.MULTIPART\u FORM\u DATA);
MultiValueMap=新链接的MultiValueMap();
添加(“图像”,新文件系统资源(文件));
地图。添加(“post”,post);
HttpEntity requestEntity=新的HttpEntity(映射、头);
RestTemplate RestTemplate=RestClientConfig.getRestTemplate(上下文,true);
restemplate.getMessageConverters().add(新表单httpMessageConverter());
ResponseEntity response=restemplate.postForObject(url、requestEntity、Post.class);
尝试以下方法: 在这里发送jsonString,然后使用objectwriter将其转换为object。如果需要更多解释,请告诉我

@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;
    }
}

MappingJackson2HttpMessageConverter()是否能将其正确映射到json对象?坦白地说,如果您的帖子中有Jackson注释,我会这样认为。我假设您已经在SpringConversionService中为您的“@RequestParam(“post”)post”注册了一个转换器。我会尝试用字符串替换您的Post类,看看它是否有效。感谢您的回答-我刚刚尝试过,resttemplate能够将它发送到服务器,但当它到达服务器时,image对象成功发送,但Post对象在服务器端返回空值。你能告诉我你的代码在服务器端是什么样子的吗?这样我就可以看到你在RequestMappings、RequestParams等方面做了什么。我仍然得到实体不能为null!我的服务器端出错。你能告诉我你的服务器端代码吗?你有什么类型的服务器?php、groovy等?如果Spring自动内置了此功能,我不希望这样做/
@Produces
    public RestTemplate getRestTemplate() {
        RestTemplate template = new RestTemplate();
        template.getMessageConverters().add(0,createFormHttpConverter());
        return template;
    }
 private static HttpMessageConverter<?> createFormHttpConverter(){

        FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
        formHttpMessageConverter.setPartConverters(getPartConverters());
        return formHttpMessageConverter;
    }

private static List<HttpMessageConverter<?>> getPartConverters(){
    RestTemplate template = new RestTemplate();
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    List<HttpMessageConverter<?>> messageConverters = template.getMessageConverters();
    messageConverters.add(0,converter);
    return messageConverters;
}
public class PostWithPicCommand() {
   public PostWithPic() {}; //Default constructor is required

   //name the variables like the request parameters!
   private Post post;
   private MultipartFile image;

   Getter and Setter!
}

@RequestMapping(value = "/uploadpostpic", method = RequestMethod.POST)
public Post uploadPostWithPic(PostWithPicCommand postWithPicCommand
      /*no @Param attribte for postWithPicCommand*/) {    
    ....
}
  HttpHeaders headers = new HttpHeaders();
  headers.add("Accept","application/json");     
  headers.setContentType(MediaType.MULTIPART_FORM_DATA);
  MultiValueMap<String, Object> map =  new LinkedMultiValueMap<String, Object>();
  map.add("image", new FileSystemResource(file));
  map.add("post", post);
  HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(map, headers);
  RestTemplate restTemplate = RestClientConfig.getRestTemplate(context, true);
  restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
  ResponseEntity<Post> response = restTemplate.postForObject(url, requestEntity, Post.class);
@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;
    }
}
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);