Feigin默认是不支持文件上传和表单提交的,需要做一些配置才能支持。
1、feign依赖
图中红色为form支持必须的jar。
2、添加自定义Encoder类:
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Set; import org.springframework.web.multipart.MultipartFile; import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.form.spring.SpringFormEncoder;
import lombok.val;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; import static java.util.Collections.singletonMap; /**
* @author roger yang
* @date 10/22/2019
*/
public class MultipartEncoder extends SpringFormEncoder { @SuppressWarnings("unchecked")
@Override
public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException {
if (bodyType.getClass().equals(ParameterizedTypeImpl.class) && ((ParameterizedTypeImpl) bodyType).getRawType().equals(Map.class)) {
val data = (Map<String, Object>) object;
Set<String> nullSet = new HashSet<>();
for (Map.Entry<String, Object> entry : data.entrySet()) {
if (entry.getValue() == null) {
nullSet.add(entry.getKey());
}
}
for (String s : nullSet) {
data.remove(s);
}
super.encode(data, MAP_STRING_WILDCARD, template);
return;
} else if (bodyType.equals(MultipartFile.class)) {
val file = (MultipartFile) object;
val data = singletonMap(file.getName(), object);
super.encode(data, MAP_STRING_WILDCARD, template);
return;
} else if (bodyType.equals(MultipartFile[].class)) {
val file = (MultipartFile[]) object;
if (file != null) {
val data = singletonMap(file.length == 0 ? "" : file[0].getName(), object);
super.encode(data, MAP_STRING_WILDCARD, template);
return;
}
}
super.encode(object, bodyType, template);
}
}
为什么要自定义呢?因为SpringFormEncoder这个类的源码里只对MultipartFile做了特殊处理,并未对MultipartFile[]数组进行处理,在传递多个文件时会报错。
@Override
public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException {
if (!bodyType.equals(MultipartFile.class)) {
super.encode(object, bodyType, template);
return;
} val file = (MultipartFile) object;
val data = singletonMap(file.getName(), object);
super.encode(data, MAP_STRING_WILDCARD, template);
}
另外,我们为了让文件和表单一起在http的body里一起传输,所有我们将他们都封装到map里统一处理。
3、feign接口类:
import java.util.Map; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import com.longge.common.GlobalResponse; /**
* @author: yangzhilong
* @description: Notification api
* post with MULTIPART_FORM_DATA_VALUE
* you client feign config:
* -------------------------------------------------------------------
* import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean; import com.longge.api.NotificationService; import feign.codec.Encoder;
import com.longge.config.MultipartEncoder; @FeignClient(value = "notification-service", configuration = NotificationServiceFeign.FeignSimpleEncoderConfig.class)
public interface NotificationServiceFeign extends NotificationMultipartService {
class FeignSimpleEncoderConfig { @Bean
public Encoder encoder(){
return new MultipartEncoder();
}
}
}
* ---------------------------------------------------------------------
* @date: 17:24 2019/7/3
**/
@RequestMapping(value = "/v1/api")
public interface NotificationMultipartService { /**
* common email channel ,use to send common email
*
* @param data
* map key have:
* attachfile --> MultipartFile[]
* com.longge.dto.EmailDto.FieldName --- > value
*
* eg:
* Map<String, Object> data = new HashMap<>();
data.put("attachfile", new MultipartFile[] {file});
data.put("from", emailDto.getFrom());
data.put("to", emailDto.getTo());
data.put("bodyText", emailDto.getBodyText());
data.put("subject", emailDto.getSubject());
* @return GlobalResponse<Long> email id
*/
@RequestMapping(value = "/send_mail", method = RequestMethod.POST, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
GlobalResponse<String> sendMail(Map<String, Object> data);
}
其中为了支持 form请求,需要对此feign进行单独的配置:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean; import com.longge.api.NotificationMultipartService; import feign.codec.Encoder; @FeignClient(value = "notification-service", configuration = NotificationServiceFeign.FeignSimpleEncoderConfig.class)
public interface NotificationServiceFeign extends NotificationMultipartService {
class FeignSimpleEncoderConfig {
@Bean
public Encoder encoder() {
return new MyEncoder();
}
}
}
4、服务实现类
@RequestMapping(value = "/v1/api/send_mail", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = {MediaType.APPLICATION_JSON_VALUE})
public GlobalResponse<String> sendMail(@RequestParam(value = "attachfile", required = false) MultipartFile[] attachfile, EmailDto emailDto) {
// TODO
}
5、EmailDto属性
private String from;
private String to;
private String subject;
private String bodyText;
private String bodyHtml;