簡體   English   中英

文件上傳spring cloud feign客戶端

[英]File upload spring cloud feign client

當使用 spring cloud netflix 的 feign 客戶端從一個微服務向另一個微服務發出 post 請求時,我在 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"
}

我的 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"]) 在 com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:284) ~[jackson-databind-2.8. 9.jar:2.8.9] 在 com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110) ~[jackson-databind-2.8.9.jar:2.8.9] 在 com.fasterxml.jackson。 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] 在 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] 在 com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689) ~[jackson-databind-2.8.9.jar:2.8. 9] 在 com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.8.9.jar:2.8.9] 在 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] 在 com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.8.9.jar:2.8。 9] 在 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] 在 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);

這是主要的微服務控制器:

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

}

我當然在這里缺少某種序列化程序
任何建議將不勝感激!
謝謝

經過幾天搜索解決方案,我發現了這一點。 你應該開始為 spring 依賴添加偽裝形式:

<dependency>
   <groupId>io.github.openfeign.form</groupId>
   <artifactId>feign-form-spring</artifactId>
   <version>3.3.0</version>
</dependency

那么你的假客戶需要這個彈簧形式編碼器:

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

我希望它會幫助某人。

更新 13/01/2020
配置 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));
  }
}

TL; 博士
將您的 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
}

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

然后當你調用 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);
}

我在帖子中添加了消耗 = MediaType.MULTIPART_FORM_DATA_VALUE,它對我有用。 這是我的假客戶端方法,在前面我使用了 formdata

@PostMapping(path=Urls.UPLOAD_FILE_IN_LIBELLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE )
    public void uploadFileInLibelle(
            @RequestParam String processus,
            @RequestParam String level0Name,
            @RequestParam String nomFichier,
            @RequestParam String nomLibelle,
            @RequestParam String anneeFolderName,
            @RequestParam String semaineFolderName,
            @RequestPart   MultipartFile fichier);

這是我的角度前端

public uploadFiles(
        nomFichier: string,
        nomLibelle: string,
        processus: string,
        level0Name: string,
        semaineFolderName: string,
        anneeFolderName: string,
        byte: File
    ): Observable<any> {
        const formData = new FormData();
        formData.set('processus', processus);
        formData.set('level0Name', level0Name);
        formData.set('nomLibelle', nomLibelle);
        formData.set('anneeFolderName', anneeFolderName);
        formData.set('semaineFolderName', semaineFolderName);
        formData.set('nomFichier', nomFichier);
        formData.set('fichier', byte);

        return this.httpClient.post(this.urlUploadFile, formData);
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM