1

Using Spring Integration I upload a file via HTTP POST and route it to a service activator.

In the service activator, I make a call using RestTemplate to another server where to dump the file but I can't figure out why I get the following error for the following code:

What I don't understand is why I get the exception below when I call RestTemplate.exchange()

at pdi.integration.MultipartReceiver.printMultiPartContent(MultipartReceiver.java:41)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)

xml-config

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:int="http://www.springframework.org/schema/integration"
       xmlns:int-http="http://www.springframework.org/schema/integration/http"
       xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">

    <bean id="byteArrayHttpMessageConverter"
          class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
    </bean>

    <bean id="formHttpMessageConverter"
          class="org.springframework.http.converter.FormHttpMessageConverter">
    </bean>

    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

    <bean id="headerMapper" class="org.springframework.integration.http.support.DefaultHttpHeaderMapper">
        <property name="inboundHeaderNames" value="*"/>
        <property name="outboundHeaderNames" value="*"/>
        <property name="userDefinedHeaderPrefix" value=""/>
    </bean>

    <int:channel id="http.frontend.rx"/>
    <int:channel id="http.frontend.tx"/>
    <int:channel id="http.backend.mysql.rx"/>
    <int:channel id="http.backend.mysql.tx"/>
    <int:channel id="http.backend.mongo.rx"/>
    <int:channel id="http.backend.mongo.tx"/>
    <int:channel id="http.backend.file.tx"/>

    <int-http:inbound-gateway
            id="frontEndToMySQLXX"
            request-channel="http.frontend.rx"
            reply-channel="http.frontend.tx"
            header-mapper="headerMapper"
            path="/gateway"
            supported-methods="GET,POST,PUT,DELETE"/>

    <int:router id="frontEndRouter" input-channel="http.frontend.rx" expression="headers.service">
        <int:mapping value="json" channel="http.backend.mysql.tx" />
        <int:mapping value="file" channel="http.backend.mongo.tx" />
        <int:mapping value="upload" channel="http.backend.file.tx" />
    </int:router>

    <!-- removed : message-converters="formHttpMessageConverter,byteArrayHttpMessageConverter" -->
    <int-http:outbound-gateway
            id="toMongoDB"
            request-channel="http.backend.mongo.tx"
            reply-channel="http.backend.mongo.rx"
            url="http://localhost:5050/api/{path}"
            http-method-expression="headers.http_requestMethod"
            header-mapper="headerMapper"
            expected-response-type="byte[]">
        <int-http:uri-variable name="path" expression="headers['urlpath']"/>
    </int-http:outbound-gateway>

    <int-http:outbound-gateway
            id="toMySQLDB"
            request-channel="http.backend.mysql.tx"
            reply-channel="http.backend.mysql.rx"
            url="http://localhost:7070/api/{path}"
            http-method-expression="headers.http_requestMethod"
            expected-response-type="java.lang.String"
            charset="UTF-8">
        <int-http:uri-variable name="path" expression="headers['urlpath']"/>
    </int-http:outbound-gateway>

    <int:service-activator
        id="MySQLToFrontEnd"
        input-channel="http.backend.mysql.rx"
        output-channel="http.frontend.tx"
        ref="messageService"
        method="printContent">
    </int:service-activator>

    <int:service-activator
        id="MongoToFrontEnd"
        input-channel="http.backend.file.tx"
        output-channel="http.frontend.tx"
        ref="multipartReceiver"
        method="printMultiPartContent">
    </int:service-activator>

 </beans>

bean used by service activator

@Component
public class MultipartReceiver {

    public void printMultiPartContent(LinkedMultiValueMap<String, Object> multipartRequest){

        System.out.println("### Successfully received multipart request ###");
        for (String elementName : multipartRequest.keySet()) {
            if (elementName.equals("file")){
                System.out.println("\t" + elementName + " - as UploadedMultipartFile: " +
                        ((UploadedMultipartFile) multipartRequest
                                .getFirst("file")).getOriginalFilename());
            }
        }

        RestTemplate template = new RestTemplate();
        String uri = "http://localhost:5050/api/upload";
        MultiValueMap map = new LinkedMultiValueMap<String,Object>();
        map.add("file", multipartRequest.getFirst("file"));
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "multipart/form-data");
        HttpEntity request = new HttpEntity(map, headers);
        ResponseEntity<byte[]> response = template.exchange(uri, HttpMethod.POST, request, byte[].class);

    }

}

stacktrace :: http://pastebin.com/5Wa9VaRb

working code ::

public void printMultiPartContent(LinkedMultiValueMap<String, Object> multipartRequest) throws IOException {

        final String filename = ((UploadedMultipartFile) multipartRequest.getFirst("file")).getOriginalFilename();

        RestTemplate template = new RestTemplate();
        MultiValueMap<String, Object> multipartMap = new LinkedMultiValueMap<String, Object>();
        multipartMap.add("name", filename);
        multipartMap.add("filename", filename);

        byte[] bytes = ((UploadedMultipartFile) multipartRequest.getFirst("file")).getBytes();
        ByteArrayResource contentsAsResource = new ByteArrayResource(bytes){
            public String getFilename(){
                return filename;
            }
        };

        multipartMap.add("file", contentsAsResource);
        String result = template.postForObject("http://localhost:5050/api/upload", multipartMap, String.class);
        System.out.println(result);

    }
cristi _b
  • 1,783
  • 2
  • 28
  • 43

1 Answers1

2

The problem is you are passing the complete UploadedMultipartFile object to the RestTemplate. Jackson is trying to serialize the object, including the inputStream property, which it cannot.

It appears you need to extract the file contents

byte[] bytes ((UploadedMultipartFile) multipartRequest.getFirst("file")).getBytes();

And set the content type to the UploadedMultipartFile.getContentType().

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • You probably should just send the byte[] not a map. – Gary Russell Aug 03 '14 at 17:50
  • made some tests today trying to send the file as a byte array but got 400 bad request on the other side ... if I test with cUrl or RestConsole it works fine ... I'll retest and post back more details ... – cristi _b Aug 03 '14 at 17:57
  • The fourth answer [here](http://stackoverflow.com/questions/4118670/sending-multipart-file-as-post-parameters-with-resttemplate-requests) looks promising - maybe create a `ByteArrayResource` from the byte[]. – Gary Russell Aug 03 '14 at 20:22