当前位置: 移动技术网 > IT编程>开发语言>Java > 使用Feign扩展包实现微服务间文件上传

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

2019年07月19日  | 移动技术网IT编程  | 我要评论

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

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

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网