文件上传春云假装客户端

ZiO*_*iOS 1 java multipart microservices spring-cloud feign

当使用spring cloud netflix的假客户端从一个微服务发送到另一个微服务的帖子请求时,我在Postman中收到以下错误:

{
"timestamp": 1506933777413,
"status": 500,
"error": "Internal Server Error",
"exception": "feign.codec.EncodeException",
"message": "Could not write JSON: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile[\"inputStream\"]->java.io.FileInputStream[\"fd\"])",
"path": "/attachments"
}
Run Code Online (Sandbox Code Playgroud)

我的eclipse控制台显示以下异常:

com.fasterxml.jackson.databind.JsonMappingException:没有为类java.io.FileDescriptor找到序列化程序,也没有发现创建BeanSerializer的属性(为了避免异常,请禁用SerializationFeature.FAIL_ON_EMPTY_BEANS)(通过引用链:org.springframework.web.multipart. support.StandardMultipartHttpServletRequest $ StandardMultipartFile ["inputStream"] - > java.io.FileInputStream ["fd"])at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:284)〜[jackson-databind-2.8. 9.jar:2.8.9]在com.fasterxml.jackson的com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110)〜[jackson-databind-2.8.9.jar:2.8.9]. databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1135)〜[jackson-databind-2.8.9.jar:2.8.9] at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69 )〜[jackson-databind-2.8.9.jar:2.8.9] at com.fasterxml.jackson.databind.ser.impl.UnknownSerial izer.serialize(UnknownSerializer.java:32)〜[jackson-databind-2.8.9.jar:2.8.9] at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)〜[jackson -databind-2.8.9.jar:2.8.9] at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689)~ [jackson-databind-2.8.9.jar:2.8. [9] at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)~ [jackson-databind-2.8.9.jar:2.8.9] at com.fasterxml.jackson.databind.ser. BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)~ [jackson-databind-2.8.9.jar:2.8.9] at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689)~ [jackson-databind-2.8.9.jar:2.8.9] at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)~ [jackson-databind-2.8.9.jar:2.8. 9] at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:29 2)〜[jackson-databind-2.8.9.jar:2.8.9] at com.fasterxml.jackson.databind.ObjectWriter $ Prefetch.serialize(ObjectWriter.java:1429)~ [jackson-databind-2.8.9.jar :2.8.9] at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:951)〜[jackson-databind-2.8.9.jar:2.8.9]

更新1

这是我的假装界面:

@FeignClient(name="attachment-service", fallback=AttachmentHystrixFallback.class)
public interface AttachmentFeignClient {

@RequestMapping("upload")
void upload(@RequestPart(name="file") MultipartFile file, @RequestParam(name="attachableId") Long attachableId, 
        @RequestParam(name="className") String className, @RequestParam(name="appName") String appName);
Run Code Online (Sandbox Code Playgroud)

这是主要的微服务控制器:

@RestController
public class AttachmentController implements Serializable {

/**
 * 
 */
private static final long serialVersionUID = -4431842080646836475L;

@Autowired
AttachmentService attachmentService;

@RequestMapping(value = "attachments", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void upload(@RequestPart MultipartFile file, @RequestParam Long attachableId, @RequestParam String className, @RequestParam String appName) throws Exception {
    attachmentService.uploadFile(file, attachableId, className, appName);
}

}
Run Code Online (Sandbox Code Playgroud)

我肯定错过了某种序列化器在这里
任何建议将不胜感激!
谢谢

Mar*_*ine 10

几天后,我找到了解决方案.你应该开始为spring依赖添加假装形式:

<dependency>
   <groupId>io.github.openfeign.form</groupId>
   <artifactId>feign-form-spring</artifactId>
   <version>3.3.0</version>
</dependency
Run Code Online (Sandbox Code Playgroud)

然后你的假装客户需要这个弹簧形式编码器:

@FeignClient(name="attachment-service",  configuration = {AttachmentFeignClient.MultipartSupportConfig.class}
 fallback=AttachmentHystrixFallback.class)
public interface AttachmentFeignClient {

@RequestMapping(value= {"upload"}, consumes = {"multipart/form-data"})
void upload(@RequestPart(name="file") MultipartFile file, @RequestParam(name="attachableId") Long attachableId, 
        @RequestParam(name="className") String className, @RequestParam(name="appName") String appName);

 public class MultipartSupportConfig {
    @Bean
    @Primary
    @Scope("prototype")
    public Encoder feignFormEncoder() {
        return new SpringFormEncoder();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我希望它能帮助别人.


Ahm*_*eem 8


2020 年 1 月 13 日 更新配置 FeignClient 以使用 @RequestPart 处理 MultiPartFile
请注意,只有当您有一个代表整个正文的 RequestPart 时,此方法才适用。有多个 RequestPart 是 FeignClient 的另一个问题。


feignclient 的配置类似于@marting-choraine 所做的,只是在 FeignConfigurationClass 中添加了额外的 Encoder Configuration。

因此,使用我在下面提供的相同示例,您需要执行以下操作,而不是实现自定义映射器

@Configuration
@EnableFeignClients(basePackages = "YourPackage")
public class FeignConfiguration {

  @Bean
  public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
      return new SpringFormEncoder(new SpringEncoder(messageConverters));
  }
}
Run Code Online (Sandbox Code Playgroud)

TL;DR
将您的 MultiPartFile 转换为 MultiValueMap。请参阅下面的示例


@martin-choraine提到的答案是拥有 FeignClient 方法签名的正确和最佳答案,与您尝试调用的实际端点签名相同。但是,有一种方法不需要您定义 FormEncoder 或添加任何额外的依赖项,因为在某些应用程序中您不允许这样做(企业狗屎);您需要做的就是将 MultipartFile 转换为 MultiValueMap,它可以完美地工作,因为标准编码器将能够对其进行序列化。


要调用的实际端点

@PostMapping(path = "/add", consumes = MULTIPART_FORM_DATA_VALUE, produces = APPLICATION_JSON_VALUE)
 public MyResponseObject add(@RequestParam(name = "username") String username,
                             @RequestPart(name = "filetoupload") MultipartFile file) {
              Do something
}
Run Code Online (Sandbox Code Playgroud)

FeignClient 中的 POST 方法应如下所示

@PostMapping(path = "/myApi/add", consumes = MULTIPART_FORM_DATA_VALUE, 
              produces = APPLICATION_JSON_VALUE)
 public MyResponseObject addFile(@RequestParam(name = "username") String username,
                           @RequestPart(name = "filetoupload") MultiValueMap<String, Object> file);
Run Code Online (Sandbox Code Playgroud)

然后当你调用 addFile 时,你应该像这样提供 MultiValueMap

public MyResponseObject addFileInAnotherEndPoint(String username, MultipartFile file) throws IOException {

    MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
    ByteArrayResource contentsAsResource = new ByteArrayResource(file.getBytes()) {
        @Override
        public String getFilename() {
            return file.getOriginalFilename();
        }
    };
    multiValueMap.add("filetoupload", contentsAsResource);
    multiValueMap.add("fileType", file.getContentType());
    return this.myFeignClient.addFile(username, multiValueMap);
}
Run Code Online (Sandbox Code Playgroud)