网上百度了很多办法均太过简单,大多都是携带几个简单的参数和一个File对象,如果将File和其他参数封装到一个对象或者DTO内就不行了
网上的例子 MultipartFile
Feign 无法直接传递文件参数,需要在client端引入几个依赖
io.github.openfeign.form:feign-form:3.0.3
io.github.openfeign.form:feign-form-spring:3.0.3
1. 创建服务端
方式与普通的文件上传方法一致
-
@RestController
-
@RequestMapping(
"/producer/upload")
-
class UploadProducer {
-
-
@PostMapping(value =
'/upload', consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
-
String upload(
@RequestPart(value =
"file") MultipartFile file) {
-
// ...
-
return
file
.originalFilename
-
}
-
}
2. 创建client
-
io
.github
.openfeign
.form
:feign-form
:3.0.3
-
io
.github
.openfeign
.form
:feign-form-spring
:3.0.3
-
@FeignClient(name =
'upload', url =
'${upload.base-url}', path =
'/producer/upload')
-
interface UploadClient {
-
-
@RequestMapping(value =
'/upload', method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE
-
, produces = MediaType.APPLICATION_JSON_VALUE)
-
String upload(
@RequestPart(
"file") MultipartFile file)
-
}
划重点
-
@Configuration
-
class MultipartSupportConfig {
-
@Autowired
-
private ObjectFactory<HttpMessageConverters> messageConverters
-
-
// new一个form编码器,实现支持form表单提交
-
@Bean
-
Encoder feignFormEncoder() {
-
return
new
SpringFormEncoder(new SpringEncoder(messageConverters))
-
}
-
}
3. 创建Controller,调用client接口
-
@RestController
-
@RequestMapping(
"/")
-
class RecordController {
-
@Autowired
-
private UploadClient uploadClient
-
-
@RequestMapping(value =
'/upload', method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-
String upload(
@RequestParam(
"file") MultipartFile file) {
-
return
uploadClient
.uploade(file)
-
}
-
}
可以看到网上的例子只能传递简单的表单参数 下面我有种需求 如果携带一个对象呢? 对象信息如下
-
package com.smdk.dsminio.vo;
-
-
import lombok.Data;
-
import lombok.ToString;
-
import org.springframework.web.multipart.MultipartFile;
-
import java.io.Serializable;
-
-
/**
-
* @author 神秘的凯
-
*date 2020-03-17 17:16
-
*/
-
@Data
-
@ToString
-
public
class MultipartFileParam implements Serializable {
-
public
static
final
long serialVersionUID=
1L;
-
// 用户id
-
private String uid;
-
//任务ID
-
private String id;
-
//总分片数量
-
private
int chunks;
-
//当前为第几块分片
-
private
int chunk;
-
//当前分片大小
-
private
long size =
0L;
-
//文件名
-
private String name;
-
//分片对象
-
private MultipartFile file;
-
// MD5
-
private String md5;
-
//BucketID
-
private Long bucketId;
-
//文件夹ID
-
private Long parentFolderId;
-
}
如果这样直接上传Spring的解析器解析不到MultipartFileParam 自定义对象会直接报错 另外Spring只能解析自带的Multipart 对象
报错信息
%s is not a type supported by this encoder.....省略
那么如果传递自定义的参数对象呢 那就是重写Spring的对象解析器 代码如下
Feign 调用端代码
-
package com.smdk.dsminio.apiservice;
-
import com.smdk.dsminio.config.FeignSupportConfig;
-
import com.smdk.dsminio.utils.AjaxResult;
-
import com.smdk.dsminio.vo.MultipartFileParam;
-
import org.springframework.cloud.openfeign.FeignClient;
-
import org.springframework.http.MediaType;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
-
@FeignClient(value = "OSS-STORAGE-SERVICE",configuration = FeignSupportConfig.class)
-
public
interface FileService {
-
/**
-
* produces 用于指定返回类型为JSON格式
-
* consumes 用于指定生产者数据请求类型
-
* @param multipartFileParam
-
* @return
-
*/
-
@RequestMapping(value = "//OSS-STORAGE-SERVICE-$SERVER_ID/api/uploadFileInfo", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},consumes = MediaType.MULTIPART_FORM_DATA_VALUE,method = RequestMethod.POST)
-
AjaxResult uploadFileInfo(MultipartFileParam multipartFileParam);
-
}
服务端
-
package com.smdk.dsminio.controller;
-
-
import cn.hutool.log.StaticLog;
-
import com.smdk.dsminio.redis.RedisUtil;
-
import com.smdk.dsminio.service.FileStorageService;
-
import com.smdk.dsminio.utils.AjaxResult;
-
import com.smdk.dsminio.utils.Constants;
-
import com.smdk.dsminio.vo.MultipartFileParam;
-
import org.apache.commons.fileupload.servlet.ServletFileUpload;
-
import org.apache.commons.io.FileUtils;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
-
import org.springframework.http.MediaType;
-
import org.springframework.web.bind.annotation.*;
-
-
import javax.servlet.http.HttpServletRequest;
-
import java.io.File;
-
import java.io.IOException;
-
import java.util.LinkedList;
-
import java.util.List;
-
-
/**
-
* 默认控制层
-
* Created by 神秘的凯 on 22020/11/11.
-
* version 1.0
-
*/
-
@RestController
-
@RequestMapping(value = "/api")
-
public
class FileDiskStorageController {
-
@Autowired
-
private FileStorageService fileStorageService;
-
-
/**
-
* 上传文件
-
*
-
* @param param
-
* @param request
-
* @return
-
* @throws Exception
-
*/
-
@RequestMapping(value = "/uploadFileInfo", method = RequestMethod.POST)
-
public AjaxResult uploadFileInfo(MultipartFileParam multipartFileParam) {
-
StaticLog.info(
"上传文件start。");
-
try {
-
// 方法1
-
//storageService.uploadFileRandomAccessFile(param);
-
// 方法2 这个更快点
-
boolean uploadResult= fileStorageService.uploadFileByMappedByteBuffer(multipartFileParam);
-
if (uploadResult){
-
return AjaxResult.success(uploadResult,
"上传完毕");
-
}
else {
-
return AjaxResult.fail(
"分片上传中...正在上传第"+multipartFileParam.getChunk()+
"块文件块,剩余"+(multipartFileParam.getChunks()-multipartFileParam.getChunk())+
"个分块");
-
}
-
}
catch (IOException e) {
-
e.printStackTrace();
-
StaticLog.error(
"文件上传失败。{}", multipartFileParam.toString());
-
return AjaxResult.fail(
"文件上传失败");
-
}
-
}
-
}
核心重写feignFormEncoder 方法类 下面的的路由配置请忽略
-
package com.smdk.dsminio.config;
-
-
import com.smdk.dsminio.vo.MultipartFileParam;
-
import feign.Request;
-
import feign.RequestInterceptor;
-
import feign.RequestTemplate;
-
import feign.codec.EncodeException;
-
import feign.codec.Encoder;
-
import feign.form.spring.SpringFormEncoder;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Primary;
-
import org.springframework.context.annotation.Scope;
-
import java.io.IOException;
-
import java.lang.reflect.Type;
-
-
import
static java.lang.String.format;
-
public
class FeignSupportConfig{
-
//转换参数请求模型封装
-
@Bean
-
@Primary
-
@Scope("prototype")
-
public Encoder feignFormEncoder() {
-
return
new SpringFormEncoder(
new Encoder() {
-
@Override
-
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
-
if (bodyType == String.class) {
-
template.body(Request.Body.bodyTemplate(object.toString(),
null));
-
}
else
if (bodyType ==
byte[].class) {
-
template.body(Request.Body.encoded((
byte[]) object,
null));
-
-
}
else
if (bodyType == MultipartFileParam.class) {
-
MultipartFileParam multipartFileParam = (MultipartFileParam) object;
-
try {
-
template.body(Request.Body.encoded(multipartFileParam.getFile().getBytes(),
null));
-
}
catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
else
if (object !=
null) {
-
throw
new EncodeException(format(
"%s is not a type supported by this encoder.", object.getClass()));
-
}
-
}
-
});
-
}
-
@Bean
-
public feign.Logger.
Level multipartLoggerLevel() {
-
return feign.Logger.Level.FULL;
-
}
-
//路由重构
-
@Bean
-
public RequestInterceptor cloudContextInterceptor() {
-
return
new RequestInterceptor() {
-
@Override
-
public void apply(RequestTemplate template) {
-
String url = template.url();
-
if (url.contains(
"$SERVER_ID")) {
-
url = url.replace(
"$SERVER_ID", route(template));
-
template.uri(url);
-
}
-
if (url.startsWith(
"//")) {
-
url =
"http:" + url;
-
template.target(url);
-
template.uri(
"");
-
}
-
-
}
-
-
-
private CharSequence route(RequestTemplate template) {
-
// TODO 你的路由算法在这里
-
return
"01";
-
}
-
};
-
}
-
}
可以看到我加了个判断 if (bodyType == MultipartFileParam.class) 进行自己封装的对象解析
大工告成 另外说下这个问题 品读了Spring的源码才搞定的 在此之前各种尝试和百度已经测试过无数前辈提供的方法 搞了两天才搞定这个bug 哎! 两天啊!!!!!!!!!!!!
转载:https://blog.csdn.net/fujiakai/article/details/109821141
查看评论