1. Overview

In this tutorial, we’ll demonstrate how to upload a file using Open Feign. Feign is a powerful tool for microservice developers to communicate via REST API with other microservices in a declarative manner.

2. Prerequisite

Let’s assume that a RESTful web service is exposed for a file upload, and given below are the details:

POST http://localhost:8081/upload-file

So, to explain the file upload via Feign client, we’ll call the exposed web service API as shown below:

@PostMapping(value = "/upload-file")
public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
    // File upload logic
}

3. Dependencies

To support the application/x-www-form-urlencoded and multipart/form-data encoding types for the file upload, we’ll need feign-core, feign-form, and feign-form-spring modules.

Therefore, we’ll add the following dependencies to Maven:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-core</artifactId>
    <version>10.12</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form</artifactId>
    <version>3.8.0</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form-spring</artifactId>
    <version>3.8.0</version>
</dependency>

We can also use spring-cloud-starter-openfeign which has feign-core internally:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>3.1.0</version>
</dependency>

4. Configuration

Let’s add @EnableFeignClients to our main class. You can visit spring cloud open feign tutorial for more details:

@SpringBootApplication
@EnableFeignClients
public class ExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }
}

@EnableFeignClients annotation allows component scanning for the interfaces that are declared as Feign clients.

5. File Upload via Feign Client

5.1. Via Annotated Client

Let’s create the required encoder for the annotated @FeignClient class:

public class FeignSupportConfig {
    @Bean
    public Encoder multipartFormEncoder() {
        return new SpringFormEncoder(new SpringEncoder(new ObjectFactory<HttpMessageConverters>() {
            @Override
            public HttpMessageConverters getObject() throws BeansException {
                return new HttpMessageConverters(new RestTemplate().getMessageConverters());
            }
        }));
    }
}

Note that FeignSupportConfig does not need to be annotated with @Configuration.

Now, let’s create an interface and annotate it with @FeignClient. We’ll also add the name and configuration attributes with their corresponding values:

@FeignClient(name = "file", url = "http://localhost:8081", configuration = FeignSupportConfig.class)
public interface UploadClient {
    @PostMapping(value = "/upload-file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String fileUpload(@RequestPart(value = "file") MultipartFile file);
}

The UploadClient points to the API mentioned in the prerequisite.

While working with Hystrix, we’ll use the fallback attribute to add as an alternative. This is done when the upload API fails*.*

Now our @FeignClient will look like this:

@FeignClient(name = "file", url = "http://localhost:8081", fallback = UploadFallback.class, configuration = FeignSupportConfig.class)

And finally, we can call UploadClient directly from the service layer:

public String uploadFile(MultipartFile file) {
    return client.fileUpload(file);
}

5.2. Via Feign.builder

In some cases, our Feign Clients need to be customized, which is not possible in the annotation manner as described above. In such a case, we create clients using the Feign.builder() API.

Let’s build a proxy interface containing a file upload method targeted to the REST API for the file upload:

public interface UploadResource {
    @RequestLine("POST /upload-file")
    @Headers("Content-Type: multipart/form-data")
    Response uploadFile(@Param("file") MultipartFile file);
}

The annotation @RequestLine defines the HTTP method and the relative resource path of the API, and @Headers specifies the headers such as Content-Type.

Now, let’s invoke the specified method in the proxy interface. We’ll do this from our service class:

public boolean uploadFileWithManualClient(MultipartFile file) {
    UploadResource fileUploadResource = Feign.builder().encoder(new SpringFormEncoder())
      .target(UploadResource.class, HTTP_FILE_UPLOAD_URL);
    Response response = fileUploadResource.uploadFile(file);
    return response.status() == 200;
}

Here, we have used the Feign.builder() utility to build an instance of the UploadResource proxy interface. We have also used the SpringFormEncoder and RESTful Web Service-based URL.

6. Verification

Let’s create a test to verify the file upload with the annotated client:

@SpringBootTest
public class OpenFeignFileUploadLiveTest {
    
    @Autowired
    private UploadService uploadService;
    
    private static String FILE_NAME = "fileupload.txt";
    
    @Test
    public void whenAnnotatedFeignClient_thenFileUploadSuccess() {
        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
        File file = new File(classloader.getResource(FILE_NAME).getFile());
        Assert.assertTrue(file.exists());
        FileInputStream input = new FileInputStream(file);
        MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain",
          IOUtils.toByteArray(input));
        String uploadFile = uploadService.uploadFile(multipartFile);

        Assert.assertNotNull(uploadFile);
    }
}

And now, let’s create another test to verify the file upload with the Feign.Builder():

@Test
public void whenFeignBuilder_thenFileUploadSuccess() throws IOException {
    // same as above
    Assert.assertTrue(uploadService.uploadFileWithManualClient(multipartFile));
}

7. Conclusion

In this article, we have shown how to implement a Multipart File upload using OpenFeign, and the various ways to include it in a simple application.

We’ve also seen how to configure a Feign client or use the Feign.Builder() in order to perform the same*.*

As usual, all code samples used in this tutorial are available over on GitHub.