1. 概述

在本教程中,我们将演示如何使用 OpenFeign 实现文件上传功能。Feign 是一个强大的工具,允许微服务开发者以声明式的方式通过 REST API 与其他微服务通信。

2. 前提条件

我们假设已有一个用于文件上传的 RESTful Web 服务接口暴露在外,其详情如下:

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

为了演示通过 Feign 客户端上传文件,我们将调用如下 Controller 接口:

@PostMapping(value = "/upload-file")
public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
    // 文件上传逻辑
}

3. 依赖配置

为了支持 application/x-www-form-urlencodedmultipart/form-data 类型的编码,我们需要引入以下模块:

  • feign-core
  • feign-form
  • feign-form-spring

对应的 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>

你也可以使用 spring-cloud-starter-openfeign,它内部已包含了 feign-core

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

4. 配置启用 Feign

在主类上添加 @EnableFeignClients 注解以启用 Feign 客户端扫描功能:

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

@EnableFeignClients 会自动扫描并注册标记为 Feign Client 的接口。

5. 使用 Feign Client 实现文件上传

5.1. 使用注解方式定义 Client

首先,我们需要配置一个 Encoder 来处理 multipart 表单数据:

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());
            }
        }));
    }
}

⚠️ 注意:FeignSupportConfig 不需要标注为 @Configuration

接下来,创建一个接口并使用 @FeignClient 注解:

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

该接口将调用前面提到的上传接口。

如果项目使用了 Hystrix,可以添加 fallback 实现:

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

最后,在服务层直接调用:

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

5.2. 使用 Feign.builder 手动构建客户端

在一些需要高度自定义的场景下,可以使用 Feign.builder() 来构建客户端:

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

其中:

  • @RequestLine 指定请求方法和路径
  • @Headers 设置请求头,如 Content-Type

在服务类中调用:

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

✅ 这种方式更灵活,适用于运行时动态配置客户端的场景。

6. 验证文件上传功能

我们可以编写测试用例来验证两种方式的文件上传功能。

6.1. 使用注解方式的测试

@SpringBootTest
public class OpenFeignFileUploadLiveTest {

    @Autowired
    private UploadService uploadService;

    private static String FILE_NAME = "fileupload.txt";

    @Test
    public void whenAnnotatedFeignClient_thenFileUploadSuccess() throws IOException {
        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);
    }
}

6.2. 使用 Feign.builder 的测试

@Test
public void whenFeignBuilder_thenFileUploadSuccess() throws IOException {
    // 同上准备 multipartFile
    Assert.assertTrue(uploadService.uploadFileWithManualClient(multipartFile));
}

✅ 两个测试都验证了文件能成功上传并返回预期结果。

7. 总结

本文介绍了如何使用 OpenFeign 实现 multipart 文件上传功能,包括以下两种方式:

  • 使用 @FeignClient 注解声明式调用
  • 使用 Feign.builder() 手动构建客户端

同时也展示了如何配置 Encoder 和 fallback 处理。

如需查看完整代码,可访问 GitHub 项目地址


原始标题:File Upload With Open Feign | Baeldung