1. 程式人生 > >spring cloud實戰與思考(二) 微服務之間通過fiegn上傳多個文件1

spring cloud實戰與思考(二) 微服務之間通過fiegn上傳多個文件1

jar 多文件 上傳文件 ret nmap spa 不同 port 問題

需求場景:

  1. 微服務之間調用接口一次性上傳多個文件。
  2. 上傳文件的同時附帶其他參數。
  3. 多個文件能有效的區分開,以便進行不同處理。

  Spring cloud的微服務之間接口調用使用Feign。原裝的Feign不支持文件的傳輸。需要借助“Feign-form”庫才行。但是貌似“Feign-form”庫(至少是3.0.3版本)只支持單文件上傳。在接口中使用多文件參數時會報異常:

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.0.jar:na] at feign.form.FormEncoder.encode(FormEncoder.java:87) ~[feign-form-3.0.3.jar:3.0.3] at feign.form.spring.SpringFormEncoder.encode(SpringFormEncoder.java:62) ~[feign-form-spring-3.0.3.jar:3.0.3] at feign.ReflectiveFeign$BuildEncodedTemplateFromArgs.resolve(ReflectiveFeign.java:
351) ~[feign-core-9.5.0.jar:na] at feign.ReflectiveFeign$BuildTemplateByResolvingArgs.create(ReflectiveFeign.java:213) ~[feign-core-9.5.0.jar:na] at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:72) ~[feign-core-9.5.0.jar:na] at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:
103) ~[feign-core-9.5.0.jar:na] at com.sun.proxy.$Proxy96.insertWithFiles(Unknown Source) ~[na:na]

  

在網上搜索一番後,參考博客https://blog.csdn.net/ytzzh0726/article/details/79467843”,將”Feign-form”庫中的”SpringFormEncoder”類改動一下,就可以支持多文件的上傳。下面是具體實現方法:

微服務提供方Controller接口:

@ResponseBody
@RequestMapping(value="/psts/add/insertWithFiles", method = RequestMethod.POST)
public Object insertWithFiles(@RequestParam("baseInfo") String baseInfo, @RequestPart(value = "files") MultipartFile[] photoFiles) {
}

服務消費方pom引用Feign-form依賴:

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

服務消費方聲明一個“FeignSpringFormEncoder”類(這個類copy”SpringFormEncoder”接口):

import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.form.ContentType;
import feign.form.FormEncoder;
import feign.form.MultipartFormContentProcessor;
import feign.form.spring.SpringManyMultipartFilesWriter;
import feign.form.spring.SpringSingleMultipartFileWriter;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Map;

public class FeignSpringFormEncoder extends FormEncoder {
    public FeignSpringFormEncoder() {
        this(new Default());
    }

    public FeignSpringFormEncoder(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);
//        }

        //修改為下面的代碼
        if (bodyType.equals(MultipartFile.class)) {
            MultipartFile file = (MultipartFile) object;
            Map<String, Object> 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<String, Object> data = Collections.singletonMap(“files”, object);
                super.encode(data, MAP_STRING_WILDCARD, template);
                return;
            }
        }
        super.encode(object, bodyType, template);
    }
}

“FeignSpringFormEncoder”作為bean提供給框架,代替“SpringFormEncoder”的“encode()”接口

@Configuration
public class FeignMultipartSupportConfig {

    @Bean
    @Primary
    @Scope("prototype")
    public Encoder multipartFormEncoder() {
//        return new SpringFormEncoder();
        return new FeignSpringFormEncoder();
    }

    @Bean
    public feign.Logger.Level multipartLoggerLevel() {
        return feign.Logger.Level.FULL;
    }
}

  以上方案測試可行。到目前為止需求1“多文件上傳”和需求2“非文件類型參數上傳”都已經滿足了。下面來看看怎麽對文件數組中的文件進行區分。服務提供方接收到的“MultipartFile”有兩個接口“getName()”和“getOriginalFilename()”分別對應文件在http頭的“Metadata”名稱和文件原始名稱。因為使用文件數組上傳的功能,前一個名稱被固定為“files”,不能用於區分文件。看來只能通過對文件原始名稱進行約定來區分文件。但是如果這些文件是用戶上傳的,這就要求用戶上傳文件前對文件名稱按照約定修改。顯然這種接口方式對用戶很不友好。限於篇幅,下一篇微博來探討一下這個問題的解決方法。

spring cloud實戰與思考(二) 微服務之間通過fiegn上傳多個文件1