Spring Feign大文件上传踩坑记

原创阿虎
发布于:2022-05-10 
标签: 

Spring Feign大文件上传踩坑记

https://github.com/OpenFeign/feign-form

引入依赖

<dependencies>
  <dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form</artifactId>
    <version>3.8.0</version>
  </dependency>
  <dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form-spring</artifactId>
    <version>3.8.0</version>
  </dependency>
</dependencies>

编写Feign Client

/**
 * @author hushow
 * @Descrption
 * @date 2022/03/23 10:25
 */
@FeignClient(name = "${hushow.feign.service-name:ability-service}", contextId = "ability-lfile", configuration = StorageLocalFeignService.FeignUploadConfig.class)
public interface StorageLocalFeignService {

    class FeignUploadConfig {
        @Bean
        public Encoder springEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
            return new SpringFormEncoder(new SpringEncoder(messageConverters));
        }

        @Bean
        Request.Options feignOptions() {
            return new Request.Options(2000, TimeUnit.SECONDS, -1, TimeUnit.SECONDS, true);
        }
    }

    /**
     * 文件上传
     *  hushowly
     * @param bucketName
     * @param file
     * @param fileName
     * @param relativeUrl
     * @param isPrivate
     * @return
     */
    @PostMapping(value = "/ability/v1/lfile/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    Result<StorageFileResp> uploadFile(@RequestParam(value = "bucketName", required = true) String bucketName,
                                       @RequestPart(value = "file", required = true) MultipartFile file,
                                       @RequestParam(value = "fileName", required = true) String fileName,
                                       @RequestParam(value = "relativeUrl", required = false) String relativeUrl,
                                       @RequestParam(value = "isPrivate", required = false) Boolean isPrivate);

客户端调用

        StreamMultipartFile streamMultipartFile = new StreamMultipartFile(key, fileName, inputStream, fileSize, mimeTypeStr);
        Result<StorageFileResp> response = storageLocalFeignService.uploadFile(bucketName, streamMultipartFile, fileName, key, isPrivate);

上传和下载时超时的坑

尝试上传大文件后,出现OOM坑

经分析,feign暂时不支持大文件,github有人上报,至今未修复

https://github.com/OpenFeign/feign-form/issues/101

分析源码后,发现竟读取字节数组到内存中! 本打算自己理写实现,发现feign上层根本未考虑Stream方式设计,所以暂时未打通feign上传方案,使restTemplate方案进行了代替

java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3236)
    at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
    at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
    at java.io.OutputStream.write(OutputStream.java:75)
    at feign.form.multipart.Output.write(Output.java:65)
    at feign.form.multipart.Output.write(Output.java:53)
    at feign.form.multipart.AbstractWriter.write(AbstractWriter.java:37)
    at feign.form.MultipartFormContentProcessor.process(MultipartFormContentProcessor.java:87)
    at feign.form.FormEncoder.encode(FormEncoder.java:105)
    at feign.form.spring.SpringFormEncoder.encode(SpringFormEncoder.java:84)