Feign表单文件传输遇到的坑

2022-08-01 10:37:18

遇坑说明

曾经也经常使用Feign进行数据传输,更多的是关注服务熔断和降级的处理。最近参与的一个项目中,有一个发送邮件的功能,本来一切都是那么的简单,最开始的时候,内部写一个工具类调用就可以了,已经成功上线运行。过了一段时间, 邮件里面需要发送附件,后面针对文件都需要做安全扫描,需要调用内部公共扫描接口,由于申请权限过程复杂,流程很长,项目运行过程中不能等待。这时就想着调用已有服务的接口,传输邮件信息就可以了,由被调用服务做安全扫描。最开始也觉得没什么难度,调用一下就可以了,找到别人服务的接口人,发了一个调用实例过来,由于别人也比较忙,就没有多问,好吧,直接开始开工把。

Feign发送表单数据

下面的内容,不是真实的项目内容,但是能够表达问题所在。

实体类似这样,其中我不能够理解为什么文件需要使用map接收,没get到接口人的点,不知道大家是否能够理解:

@Data
public class EmailDTO {
    /**邮件主题*/
    private String title;
    /**正文*/
    private String content;
    /**接受者*/
    private String receiver;
    /**附件列表*/
    private Map<String, MultipartFile> fileList;
}

远程服务接口大概是这样的:

@RestController
@RequestMapping("/email")
public class EmailSendRest {

    @PostMapping("/sendMail")
    public String sendMail(EmailDTO emailDTO) {
        //do something
        return "调用成功";
    }
}

大家看到这样的服务,看到第一眼应该觉得非常简单吧,不就是发送一个表单数据嘛,直接这样堆代码就可以了,第一次调用如下:

@FeignClient(name = "EmailService", configuration = EmailServiceClient.FeignSupportConfig.class)
public interface EmailServiceClient {

    @PostMapping(value = "/email/sendMail", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public void sendEmail(EmailDTO emailDTO);

    /**
     * 支持表单提交
     */
    @Configuration
    class FeignSupportConfig {
        @Bean
        public Encoder feignFormEncoder() {
            return new SpringFormEncoder();
        }
    }
}

最开始的时候,一切都很正常,没有什么异常出现。之后,发送带附件的邮件,想都不想,非常简单啊,直接这样来:

EmailDTO emailDTO = new EmailDTO();
emailDTO.setContent("正文内容");
emailDTO.setTitle("邮件标题");
Map<String,MultipartFile> fileList=new HashMap<>();
fileList.put(multipartFile.getOriginalFilename(),multipartFile);
emailDTO.setFileList(fileList);
this.emailServiceClient.sendEmail(emailDTO);

一切有那么顺利吗?当然没有,接下来你会看到下面的异常:

feign.codec.EncodeException: class java.util.HashMap is not a type supported by this encoder.

蒙圈了,什么情况?不支持?通过debug调试,发现在MultipartFormContentProcessor.process方法中调用了如下方法:

Writer writer = findApplicableWriter(entry.getValue());

这里是用于找到数据对应的Writer,可目前就支持这几种:

addWriter(new ByteArrayWriter());
addWriter(new FormDataWriter());
addWriter(new SingleFileWriter());
addWriter(new ManyFilesWriter());
addWriter(new ParameterWriter());
addWriter(new PojoWriter(writers));

好吧,找到问题所在了,确实不支持HashMap类型,这个时候有点慌了,离发布版本又近了一步了,问过许多同事,他们也没有遇到这种情况,没有使用Feign的表单形式传递Map。抱怨几句对方写接口的负责人,还是看看有没有别的办法吧。

RestTemplate发送表单数据

示例代码:

File file = new File("F:\\软件包\\sublime快捷键.txt");
//设置请求头为表单文件类型数据
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE);

FileSystemResource fileSystemResource = new FileSystemResource(file);
//使用MultiValueMap ,一个key可以对应多个value,在多文件的时候很好使用,这里就使用它 MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("title", "邮件标题");
multiValueMap.add("content", "邮件正文");
multiValueMap.add("fileList[" + file.getName() + "]", fileSystemResource);
//封装请求体
HttpEntity<MultiValueMap<String, Object>> params = new HttpEntity<>(multiValueMap, headers);
//调用
String s = this.restTemplate.postForObject("http://localhost:8080/email/sendMail", params, String.class);

采用RestTemplate解决了上述麻烦的问题,或许我们能够自定义FormEncoder来解决Feign调用问题,如果有Boss使用Feign解决了上述问题,麻烦不吝赐教,谢谢。

总结

记录在真实项目中遇到的一个问题,由于能力有限,无法采用更好的方式解决,希望能够对遇到该问题的伙伴们一个指引。

分享一个小知识点

数组或集合按照指定分隔符拼接字符串:

String[] names ={"张三", "李四", "王五", "赵六"};
String join = StringUtils.join(names, ";");
  • 作者:西红柿丶番茄
  • 原文链接:https://blog.csdn.net/p_programmer/article/details/98466269
    更新时间:2022-08-01 10:37:18