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

使用Feign实现微服务间文件传输

程序员文章站 2024-02-24 11:07:52
在很多时候我们会遇到微服务之间文件传输,很多时候我们可以通过序列化等方式解决(如图片等)。 最近项目中有个excel上传,以及多媒体文件上传,直接报错。 也试了2种解决...

在很多时候我们会遇到微服务之间文件传输,很多时候我们可以通过序列化等方式解决(如图片等)。

最近项目中有个excel上传,以及多媒体文件上传,直接报错。

也试了2种解决方式,都不可行。

1.写一个文件encoder解析器,会出现其他的rest请求出现encoder错误

2.springcloud feign有一个规范,不可以传输2个对象,可以是一个对象带几个参数方式。

那么我们现在需要一种方式,不配置全局的解析器,而是通过feign builder 去管理上传文件,这种方式管理起来也较为方便。

引用包

<dependency>
  <groupid>com.netflix.feign</groupid>
  <artifactid>feign-core</artifactid>
  <version>8.17.0</version>
</dependency>
<dependency>
  <groupid>com.netflix.feign</groupid>
  <artifactid>feign-jackson</artifactid>
  <version>8.17.0</version>
</dependency>
<dependency>
  <groupid>com.netflix.feign</groupid>
  <artifactid>feign-slf4j</artifactid>
  <version>8.17.0</version>
</dependency>

调用方式 

@apioperation(value = "上传excel", notes = "上传excel")
@requestmapping(value = "/imexcel", method = requestmethod.post, produces = request_headers)
public actionresult imexcel(@requestbody multipartfile file,@requestparam("operatorid") integer operatorid){
 
  if(file == null || file.isempty()|| operatorid==null)
    return new actionresult<>(resulttype.bad_request,"文件与操作用户id都不能为空");
  string filename = file.getoriginalfilename();
  if (!filename.matches("^.+\\.(?i)(xls)$") && !filename.matches("^.+\\.(?i)(xlsx)$")) {
    return new actionresult<>(resulttype.bad_request,"上传文件格式错误,请上传后缀为.xls或.xlsx的文件");
  }
  feign.builder encoder = feign.builder()
      .decoder(new jacksondecoder())
      .encoder(new feignencoder());
  fileupload complainfileupload = encoder.target(fileupload.class,label_url);
  return complainfileupload.imcomplainexcel(file,operatorid);
 
}

文件encode

import feign.requesttemplate;
import feign.codec.encodeexception;
import feign.codec.encoder;
import org.springframework.core.io.inputstreamresource;
import org.springframework.core.io.resource;
import org.springframework.http.httpentity;
import org.springframework.http.httpheaders;
import org.springframework.http.httpoutputmessage;
import org.springframework.http.mediatype;
import org.springframework.http.converter.httpmessageconverter;
import org.springframework.util.collectionutils;
import org.springframework.util.linkedmultivaluemap;
import org.springframework.web.client.resttemplate;
import org.springframework.web.multipart.multipartfile;
 
import java.io.bytearrayoutputstream;
import java.io.ioexception;
import java.io.inputstream;
import java.io.outputstream;
import java.lang.reflect.type;
import java.nio.charset.charset;
import java.util.arrays;
import java.util.list;
import java.util.map;
import java.util.map.entry;
 
/**
 * @author lxl
 */
public class feignencoder implements encoder {
 
 
  private final list<httpmessageconverter<?>> converters = new resttemplate().getmessageconverters();
  private final httpheaders multipartheaders = new httpheaders();
  private final httpheaders jsonheaders = new httpheaders();
 
  public static final charset utf_8 = charset.forname("utf-8");
 
  public feignencoder() {
    multipartheaders.setcontenttype(mediatype.multipart_form_data);
    jsonheaders.setcontenttype(mediatype.application_json);
  }
 
 
  @override
  public void encode(object object, type bodytype, requesttemplate template) throws encodeexception {
    if (isformrequest(bodytype)) {
      encodemultipartformrequest((map<string, ?>) object, template);
    } else {
      encoderequest(object, jsonheaders, template);
    }
  }
 
 
  private void encodemultipartformrequest(map<string, ?> formmap, requesttemplate template) throws encodeexception {
    if (collectionutils.isempty(formmap)) {
      throw new encodeexception("参数不能为空.");
    }
    linkedmultivaluemap<string, object> map = new linkedmultivaluemap<>();
    for (entry<string, ?> entry : formmap.entryset()) {
      object value = entry.getvalue();
      if (ismultipartfile(value)) {
        map.add(entry.getkey(), encodemultipartfile((multipartfile) value));
      } else if (ismultipartfilearray(value)) {
        encodemultipartfiles(map, entry.getkey(), arrays.aslist((multipartfile[]) value));
      } else {
        map.add(entry.getkey(), encodejsonobject(value));
      }
    }
    encoderequest(map, multipartheaders, template);
  }
 
  private boolean ismultipartfile(object object) {
    return object instanceof multipartfile;
  }
 
  private boolean ismultipartfilearray(object o) {
    return o != null && o.getclass().isarray() && multipartfile.class.isassignablefrom(o.getclass().getcomponenttype());
  }
 
  /**
   * 设置头
   * @param file
   * @return
   */
  private httpentity<?> encodemultipartfile(multipartfile file) {
    httpheaders filepartheaders = new httpheaders();
    filepartheaders.setcontenttype(mediatype.application_octet_stream);
    try {
      resource multipartfileresource = new multipartfileresource(file.getoriginalfilename(), file.getsize(), file.getinputstream());
      return new httpentity<>(multipartfileresource, filepartheaders);
    } catch (ioexception ex) {
      throw new encodeexception("cannot encode request.", ex);
    }
  }
 
  /**
   * 映射
   * @param map
   * @param name
   * @param files
   */
  private void encodemultipartfiles(linkedmultivaluemap<string, object> map, string name, list<? extends multipartfile> files) {
    httpheaders filepartheaders = new httpheaders();
    filepartheaders.setcontenttype(mediatype.application_octet_stream);
    try {
      for (multipartfile file : files) {
        resource multipartfileresource = new multipartfileresource(file.getoriginalfilename(), file.getsize(), file.getinputstream());
        map.add(name, new httpentity<>(multipartfileresource, filepartheaders));
      }
    } catch (ioexception ex) {
      throw new encodeexception("cannot encode request.", ex);
    }
  }
 
  /**
   * {@link httpentity} {@code content-type}
   * {@code application/json}
   *
   * @param o
   * @return
   */
  private httpentity<?> encodejsonobject(object o) {
    httpheaders jsonpartheaders = new httpheaders();
    jsonpartheaders.setcontenttype(mediatype.application_json);
    return new httpentity<>(o, jsonpartheaders);
  }
 
  /**
   * {@link org.springframework.web.client.resttemplate}
   *
   * @param value
   * @param requestheaders
   * @param template
   * @throws encodeexception
   */
  private void encoderequest(object value, httpheaders requestheaders, requesttemplate template) throws encodeexception {
    bytearrayoutputstream outputstream = new bytearrayoutputstream();
    httpoutputmessage dummyrequest = new httpoutputmessageimpl(outputstream, requestheaders);
    try {
      class<?> requesttype = value.getclass();
      mediatype requestcontenttype = requestheaders.getcontenttype();
      for (httpmessageconverter<?> messageconverter : converters) {
        if (messageconverter.canwrite(requesttype, requestcontenttype)) {
          ((httpmessageconverter<object>) messageconverter).write(
              value, requestcontenttype, dummyrequest);
          break;
        }
      }
    } catch (ioexception ex) {
      throw new encodeexception("cannot encode request.", ex);
    }
    httpheaders headers = dummyrequest.getheaders();
    if (headers != null) {
      for (entry<string, list<string>> entry : headers.entryset()) {
        template.header(entry.getkey(), entry.getvalue());
      }
    }
    /*
    使用bytearray方式传输
     */
    template.body(outputstream.tobytearray(), utf_8);
  }
 
  /**
   * {@link org.springframework.http.httpoutputmessage}
   * {@link org.springframework.http.converter.httpmessageconverter}
   */
  private class httpoutputmessageimpl implements httpoutputmessage {
 
    private final outputstream body;
    private final httpheaders headers;
 
    public httpoutputmessageimpl(outputstream body, httpheaders headers) {
      this.body = body;
      this.headers = headers;
    }
 
    @override
    public outputstream getbody() throws ioexception {
      return body;
    }
 
    @override
    public httpheaders getheaders() {
      return headers;
    }
 
  }
 
 
  static boolean isformrequest(type type) {
    return map_string_wildcard.equals(type);
  }
 
 
  static class multipartfileresource extends inputstreamresource {
 
    private final string filename;
    private final long size;
 
    public multipartfileresource(string filename, long size, inputstream inputstream) {
      super(inputstream);
      this.size = size;
      this.filename = filename;
    }
 
    @override
    public string getfilename() {
      return this.filename;
    }
 
    @override
    public inputstream getinputstream() throws ioexception, illegalstateexception {
      return super.getinputstream(); //to change body of generated methods, choose tools | templates.
    }
 
    @override
    public long contentlength() throws ioexception {
      return size;
    }
 
  }
 
}

feign调用接口

@requestline("post /punish/imexcel")
actionresult<list<string>> impunishexcel(@param("file") multipartfile file, @param("operatorid") integer operatorid);

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