欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用Feign扩展包实现微服务间文件上传

程序员文章站 2024-02-24 10:11:10
在spring cloud 的feign组件中并不支持文件的传输,会出现这样的错误提示: feign.codec.encodeexception: class [...

在spring cloud 的feign组件中并不支持文件的传输,会出现这样的错误提示:

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的扩展包实现这个功能。

一. 示例介绍

使用Feign扩展包实现微服务间文件上传

我们调用feign_upload_second的上传文件接口上传文件,feign_upload_second内部使用feign调用feign_upload_first实现文件上传。

二 、单文件上传

2.1 feign_upload_first服务提供者

文件上传的服务提供者接口比较简单,如下所示:

@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服务消费者

增加扩展包依赖

<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实现文件上传的配置类

@configuration
public class feignsupportconfig {
 @bean
 public encoder feignformencoder() {
  return new springformencoder();
 }
}

feign远程调用接口

@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进行测试,可以正常上传文件

三、多文件上传

既然单个文件可以上传,那么多文件应该也没问题吧,我们对上面的代码进行修改

3.1 feign_upload_first服务提供者

文件上传的服务提供者接口比较简单,如下所示:

@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服务消费者

feign远程调用接口

@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 测试

经过测试发现,无法上传多个文件。经过检查,发现源码里底层是有对multipartfile[]类型的支持的,源码中有个类叫springmanymultipartfileswriter,是专门针对文件数组类型进行操作的,但是配置到项目里的springformencoder类里却没有对文件数组类型的判断,以致不能支持文件数组的上传

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类构造时把springmanymultipartfileswriter实例添加到了处理器列表里了,但是在encode方法里又只判断了multipartfile类型,没有判断数组类型,底层有对数组的支持但上层却缺少了相应判断。那么我们可以自己去扩展formencoder,仿照springformencoder源码,只修改encode方法。

3.3 扩展formencoder支持多文件上传

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

经过测试可以上传多个文件。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。