溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

微服務(wù)之間怎么通過feign調(diào)用接口上傳文件

發(fā)布時間:2021-06-30 15:22:01 來源:億速云 閱讀:235 作者:chen 欄目:開發(fā)技術(shù)

這篇文章主要介紹“微服務(wù)之間怎么通過feign調(diào)用接口上傳文件”,在日常操作中,相信很多人在微服務(wù)之間怎么通過feign調(diào)用接口上傳文件問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”微服務(wù)之間怎么通過feign調(diào)用接口上傳文件”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

具體需求:

我們的項目是基于springboot框架的springcloud微服務(wù)搭建的,后端服務(wù)技術(shù)層面整體上分為business服務(wù)和core服務(wù),business服務(wù)用于作為應(yīng)用層,直接連接客戶端,通常用于聚合數(shù)據(jù),core服務(wù)用來客戶端具體操作不同需求來控制數(shù)據(jù)庫,文件上傳是通過客戶端上傳接口,通過business服務(wù),由服務(wù)端調(diào)用feign接口,也是第一次做這種文件中轉(zhuǎn),遇到各種問題,下面是我自己的解決方案,不喜勿噴,代碼小白一枚;

一、core服務(wù)層接口@requestmapping

屬性加上consumes=MediaType.MULTIPART_FORM_DATA_VALUE如下代碼

@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,
                         @RequestParam(name = "id",required = true) Integer id,
                         @RequestParam(name = "desc",required = false)  String desc,
                         @RequestParam(name = "fileId",required = false) Integer fileId )

解釋:@RequestMapping存在以下兩個屬性:

1.String[] consumes() default {};

2.String[] produces() default {};

兩個屬性的解釋及參考例子:

① 屬性produces:指定返回值類型,并且可以設(shè)置返回值類型和返回值的字符編碼;代碼例子參考如下:

屬性produces="application/json"時,返回json數(shù)據(jù)

屬性produces="MediaType.APPLICATION_JSON_VALUE;charset=utf-8"時,設(shè)置返回數(shù)據(jù)的字符編碼為utf-8

@Controller    
@RequestMapping(value = "/getperson", method = RequestMethod.GET, produces="application/json")    
public Object getPerson(int id) {       
    //實現(xiàn)自己的邏輯調(diào)用
  Person p= new person();
 
    return p;
}

特別說明:produces="application/json"和注解@ResponseBody是一樣的效果,使用了注解其實可以不使用該屬性了

② 屬性consumes: 指定處理請求當(dāng)中的提交內(nèi)容類型(Content-Type):application/json, text/html等;

代碼例子參考如下:

 @PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,
                         @RequestParam(name = "id",required = true) Integer id,
                         @RequestParam(name = "desc",required = false)  String desc,
                         @RequestParam(name = "fileId",required = false) Integer fileId ){
}

解釋: MediaType.MULTIPART_FORM_DATA_VALUE 代表的值為multipart/form-data它會將表單的數(shù)據(jù)處理為一條消息,以標(biāo)簽為單元,用分隔符分開。既可以上傳鍵值對,也可以上傳文件。當(dāng)上傳的字段是文件時,會有Content-Type來表名文件類型;content-disposition,用來說明字段的一些信息;

二、business客戶層接口@requestmapping

屬性加上consumes=MediaType.MULTIPART_FORM_DATA_VALUE如下代碼

   @PostMapping(value = "/upload",produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
            consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,
                  @RequestParam(name = "id",required = true) Integer id,
                  @RequestParam(name = "desc",required = false)  String desc,
                  @RequestParam(name = "fileId",required = false) Integer fileId );

具體大概就這么多。能力有限,多多指教!!!

feign微服務(wù)間文件上傳(Finchley版本)

在Spring Cloud 的Feign組件中并不支持文件的傳輸,會出現(xiàn)這樣的錯誤提示:

feign.codec.EncodeException: class [Lorg.springframework.web.multipart.MultipartFile; is not a type supported by this encoder.
    at feign.codec.Encoder$Default.encode(Encoder.java:90) ~[feign-core-9.5.1.jar:na]
    at feign.form.FormEncoder.encode(FormEncoder.java:87) ~[feign-form-3.3.0.jar:3.3.0]
    at feign.form.spring.SpringFormEncoder.encode(SpringFormEncoder.java:64) ~[feign-form-spring-3.3.0.jar:3.3.0]

但是我們可以通過使用Feign的擴(kuò)展包實現(xiàn)這個功能。

一. 示例介紹

服務(wù)名端口號角色
feign_upload_first8100feign服務(wù)提供者
feign_upload_second8101feign服務(wù)消費者

我們調(diào)用feign_upload_second的上傳文件接口上傳文件,feign_upload_second內(nèi)部使用feign調(diào)用feign_upload_first實現(xiàn)文件上傳。

二 、單文件上傳

2.1 feign_upload_first服務(wù)提供者

文件上傳的服務(wù)提供者接口比較簡單,如下所示:

@SpringBootApplication
public class FeignUploadFirstApplication {
  @RestController
  public class UploadController {    
    @RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
      return file.getOriginalFilename();
    }
  }
  public static void main(String[] args) {
    SpringApplication.run(FeignUploadFirstApplication.class, args);
  }
}

2.2 feign_upload_second服務(wù)消費者

增加擴(kuò)展包依賴

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

新增feign實現(xiàn)文件上傳的配置類

@Configuration
public class FeignSupportConfig {
  @Bean
  public Encoder feignFormEncoder() {
    return new SpringFormEncoder();
  }
}

feign遠(yuǎn)程調(diào)用接口

@FeignClient(name = "file",url = "http://localhost:8100",configuration = FeignSupportConfig.class)
public interface UploadService {
  @RequestMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  String handleFileUpload(@RequestPart(value = "file") MultipartFile file);
}

上傳文件接口

@RestController
public class UploadController {
  @Autowired
  UploadService uploadService;
  
  @RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
    return uploadService.handleFileUpload(file);
  }
}

2.3 測試

使用postman進(jìn)行測試,可以正常上傳文件

三、多文件上傳

既然單個文件可以上傳,那么多文件應(yīng)該也沒問題吧,我們對上面的代碼進(jìn)行修改

3.1 feign_upload_first服務(wù)提供者

文件上傳的服務(wù)提供者接口比較簡單,如下所示:

@SpringBootApplication
public class FeignUploadFirstApplication {
  @RestController
  public class UploadController {
    
    @RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
      return file.getOriginalFilename();
    }
    
    @RequestMapping(value = "/uploadFile2",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String handleFileUpload(@RequestPart(value = "file") MultipartFile[] file) {
      String fileName = "";
      for(MultipartFile f : file){
        fileName += f.getOriginalFilename()+"---";
      }
      return fileName;
    }
  }
  public static void main(String[] args) {
    SpringApplication.run(FeignUploadFirstApplication.class, args);
  }
}

3.2 feign_upload_second服務(wù)消費者

feign遠(yuǎn)程調(diào)用接口

@FeignClient(name = "file",url = "http://localhost:8100",configuration = FeignSupportConfig.class)
public interface UploadService {
  @RequestMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  String handleFileUpload(@RequestPart(value = "file") MultipartFile file);
  
  @RequestMapping(value = "/uploadFile2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  String handleFileUpload(@RequestPart(value = "file") MultipartFile[] file);
}

上傳文件接口

@RestController
public class UploadController {
  @Autowired
  UploadService uploadService;
  
  @RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
    return uploadService.handleFileUpload(file);
  }
  
  @RequestMapping(value = "/uploadFile2",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  public String handleFileUpload2(@RequestPart(value = "file") MultipartFile[] file) {
    return uploadService.handleFileUpload(file);
  }
}

3.3 測試

經(jīng)過測試發(fā)現(xiàn),無法上傳多個文件。經(jīng)過檢查,發(fā)現(xiàn)源碼里底層是有對MultipartFile[]類型的支持的,源碼中有個類叫SpringManyMultipartFilesWriter,是專門針對文件數(shù)組類型進(jìn)行操作的,但是配置到項目里的SpringFormEncoder類里卻沒有對文件數(shù)組類型的判斷,以致不能支持文件數(shù)組的上傳

SpringManyMultipartFilesWriter源碼

public class SpringManyMultipartFilesWriter extends AbstractWriter {
  private final SpringSingleMultipartFileWriter fileWriter = new SpringSingleMultipartFileWriter();
  public SpringManyMultipartFilesWriter() {
  }
  public void write(Output output, String boundary, String key, Object value) throws Exception {
    if (value instanceof MultipartFile[]) {
      MultipartFile[] files = (MultipartFile[])((MultipartFile[])value);
      MultipartFile[] var6 = files;
      int var7 = files.length;
      for(int var8 = 0; var8 < var7; ++var8) {
        MultipartFile file = var6[var8];
        this.fileWriter.write(output, boundary, key, file);
      }
    } else if (value instanceof Iterable) {
      Iterable<?> iterable = (Iterable)value;
      Iterator var11 = iterable.iterator();
      while(var11.hasNext()) {
        Object file = var11.next();
        this.fileWriter.write(output, boundary, key, file);
      }
    }
  }
  public boolean isApplicable(Object value) {
    if (value == null) {
      return false;
    } else if (value instanceof MultipartFile[]) {
      return true;
    } else {
      if (value instanceof Iterable) {
        Iterable<?> iterable = (Iterable)value;
        Iterator<?> iterator = iterable.iterator();
        if (iterator.hasNext() && iterator.next() instanceof MultipartFile) {
          return true;
        }
      }
      return false;
    }
  }
}

SpringFormEncoder源碼

public class SpringFormEncoder extends FormEncoder {
  public SpringFormEncoder() {
    this(new Default());
  }
  public SpringFormEncoder(Encoder delegate) {
    super(delegate);
    MultipartFormContentProcessor processor = (MultipartFormContentProcessor)this.getContentProcessor(ContentType.MULTIPART);
    processor.addWriter(new SpringSingleMultipartFileWriter());
    processor.addWriter(new SpringManyMultipartFilesWriter());
  }
  public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
    if (!bodyType.equals(MultipartFile.class)) {
      super.encode(object, bodyType, template);
    } else {
      MultipartFile file = (MultipartFile)object;
      Map<String, Object> data = Collections.singletonMap(file.getName(), object);
      super.encode(data, MAP_STRING_WILDCARD, template);
    }
  }
}

從上面SpringFormEncoder的源碼上可以看到SpringFormEncoder類構(gòu)造時把SpringManyMultipartFilesWriter實例添加到了處理器列表里了,但是在encode方法里又只判斷了MultipartFile類型,沒有判斷數(shù)組類型,底層有對數(shù)組的支持但上層卻缺少了相應(yīng)判斷。那么我們可以自己去擴(kuò)展FormEncoder,仿照SpringFormEncoder源碼,只修改encode方法。

3.3 擴(kuò)展FormEncoder支持多文件上傳

擴(kuò)展FormEncoder,命名為FeignSpringFormEncoder

public class FeignSpringFormEncoder extends FormEncoder {
  /**
   * Constructor with the default Feign's encoder as a delegate.
   */
  public FeignSpringFormEncoder() {
    this(new Default());
  }  
  
  /**
   * Constructor with specified delegate encoder.
   *
   * @param delegate delegate encoder, if this encoder couldn't encode object.
   */
  public FeignSpringFormEncoder(Encoder delegate) {
    super(delegate);
    
    MultipartFormContentProcessor processor = (MultipartFormContentProcessor) getContentProcessor(ContentType.MULTIPART);
    processor.addWriter(new SpringSingleMultipartFileWriter());
    processor.addWriter(new SpringManyMultipartFilesWriter());
  }  
  
  @Override
  public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
    if (bodyType.equals(MultipartFile.class)) {
      MultipartFile file = (MultipartFile) object;
      Map data = Collections.singletonMap(file.getName(), object);
      super.encode(data, MAP_STRING_WILDCARD, template);
      return;
    } else if (bodyType.equals(MultipartFile[].class)) {
      MultipartFile[] file = (MultipartFile[]) object;
      if(file != null) {
        Map data = Collections.singletonMap(file.length == 0 ? "" : file[0].getName(), object);
        super.encode(data, MAP_STRING_WILDCARD, template);
        return;
      }
    }
    super.encode(object, bodyType, template);
  }
}

注冊配置類

@Configuration
public class FeignSupportConfig {
  @Bean
  public Encoder feignFormEncoder() {
    return new FeignSpringFormEncoder();
  }
}

經(jīng)過測試可以上傳多個文件。

到此,關(guān)于“微服務(wù)之間怎么通過feign調(diào)用接口上傳文件”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI