使用 Open Feign 上传文件

转自:https://www.baeldung.com/java-feign-file-upload

1. 概述

在本教程中,我们将演示如何使用 Open Feign 上传文件。 Feign 是微服务开发者通过 REST API 以声明方式与其他微服务进行通信的强大工具。

2. 前置条件

假设为文件上传开了一个 RESTful Web 服务,下面给出了详细信息:

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

因此,为了解释通过 Feign 客户端上传文件,我们将调用暴露的 Web 服务 API,如下所示:

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

3. 依赖

为了支持文件上传的 application/x-www-form-urlencodedmultipart/form-data 编码类型,我们需要 feign-corefeign-formfeign-form-spring 模块。

因此,我们将以下依赖项添加到 Maven:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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>

我们也可以使用内部有 feign-corespring-cloud-starter-openfeign

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.0</version>
</dependency>

4. 配置

让我们将@EnableFeignClients 添加到我们的主类中:

1
2
3
4
5
6
7
@SpringBootApplication
@EnableFeignClients
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}

@EnableFeignClients 注解允许对声明为 Feign 客户端的接口进行组件扫描。

5. 通过Feign客户端上传文件

5.1. 通过带注解的客户端

让我们为带注解的 @FeignClient 类创建所需的编码器:

1
2
3
4
5
6
7
8
9
10
11
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 对其进行注解。 我们还将添加名称和配置属性及其对应的值:

1
2
3
4
5
@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);
}

UploadClient 指向前置条件中提到的 API。

在使用 Hystrix 时,我们将使用 fallback 属性作为替代添加。 这是在上传 API 失败时完成的。

现在我们的@FeignClient 将如下所示:

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

最后,我们可以直接从服务层调用 FeignSupportConfig

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

5.2. 通过 Feign.builder

在某些情况下,我们的 Feign Clients 需要自定义,并且在上面描述的注解方式满足不了我们自定义需求。 在这种情况下,我们使用 Feign.builder() API 创建客户端。

让我们构建一个代理接口,其中包含针对文件上传的 REST API 的文件上传方法:

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

注解@RequestLine 定义了API 的HTTP 方法和相对资源路径,@Headers 指定了Content-Type 等请求头。

现在,让我们在代理接口中调用指定的方法。 我们将从我们的服务类中执行此操作:

1
2
3
4
5
6
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;
}

在这里,我们使用 Feign.builder() 工具类来构建 UploadResource 代理接口的实例。 我们还使用了 SpringFormEncoder 和 RESTful Web Service-based URL。

6. 验证

让我们创建一个测试来验证带有注解的客户端的文件上传:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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);
}
}

现在,让我们创建另一个测试来验证使用 Feign.Builder() 上传的文件:

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

7. 结论

在本文中,我们展示了如何使用 OpenFeign 实现多部分文件上传,以及将其用在简单应用程序中的各种方法。

我们还看到了如何配置 Feign 客户端或使用 Feign.Builder() 来执行相同的操作。

像往常一样,本教程中使用的所有代码示例都可以在 GitHub 上找到。