i try to define a feign-client like this:
@FeignClient(value = "epolicy", path = "/epolicy/download", url = "http://10.1.118.45:8001")
public interface EPolicyApi {
@RequestMapping(method = RequestMethod.POST)
byte[] download(@RequestParam("policyNo") String policyNo, @RequestParam("idNo") String idNo);
}
my http response's MediaType is application/msdownload。
should i customize a HttpMessageConvter or do some other job?
Comment From: Aloren
Spring converts response into byte array via ByteArrayHttpMessageConverter, but it supports application/octet-stream and / media types, which means all :) So as far as I know, code that you provided should work as is.
Comment From: lexburner
@Aloren you are right,i use 'byte[]' as the return type ,that is ok. but i am not very sure if i want to recevice customize MediaType like application/toString or some uncommonly used MediaType like application/msdownload. is that the best way to use byets[] to recevice as your suggested?
wish your reply to my question,thanks.
Comment From: beldon
I have the same problem. Is there a good solution?
Comment From: beldon
Yes, I do it. Here is my code,including uploading and downloading. I use feign-form to upload file. feign-form
Feign-form maven dependencies
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>2.2.1</version>
</dependency>
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
public class InMemoryMultipartFile implements MultipartFile {
private final String name;
private final String originalFileName;
private final String contentType;
private final byte[] payload;
public InMemoryMultipartFile(File file) throws IOException {
this.originalFileName = file.getName();
this.payload = FileCopyUtils.copyToByteArray(file);
this.name = "file";
this.contentType = "application/octet-stream";
}
public InMemoryMultipartFile(String originalFileName, byte[] payload) {
this.originalFileName = originalFileName;
this.payload = payload;
this.name = "file";
this.contentType = "application/octet-stream";
}
public InMemoryMultipartFile(String name, String originalFileName, String contentType, byte[] payload) {
if (payload == null) {
throw new IllegalArgumentException("Payload cannot be null.");
}
this.name = name;
this.originalFileName = originalFileName;
this.contentType = contentType;
this.payload = payload;
}
@Override
public String getName() {
return name;
}
@Override
public String getOriginalFilename() {
return originalFileName;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return payload.length == 0;
}
@Override
public long getSize() {
return payload.length;
}
@Override
public byte[] getBytes() throws IOException {
return payload;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(payload);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
new FileOutputStream(dest).write(payload);
}
}
@FeignClient(value = "material", configuration = MaterialClient.MultipartSupportConfig.class)
public interface MaterialClient {
@PostMapping("/uploadFile")
@Headers("Content-Type: multipart/form-data")
ResponseMO uploadFile(@RequestPart("file") MultipartFile file);
@GetMapping("/oss/downFile")
MultipartFile downFile(@RequestParam("key") String key);
class MultipartSupportConfig {
@Autowired
ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
@Primary
@Scope("prototype")
public Encoder multipartFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
@Bean
@Primary
@Scope("prototype")
public Decoder decoder() {
Decoder decoder = (response, type) -> {
if (type instanceof Class && MultipartFile.class.isAssignableFrom((Class) type)) {
Collection<String> contentTypes = response.headers().get("content-type");
String contentType = "application/octet-stream";
if (contentTypes.size() > 0) {
String[] temp = new String[contentTypes.size()];
contentTypes.toArray(temp);
contentType = temp[0];
}
byte[] bytes = StreamUtils.copyToByteArray(response.body().asInputStream());
InMemoryMultipartFile inMemoryMultipartFile = new InMemoryMultipartFile("file","", contentType,bytes);
return inMemoryMultipartFile;
}
return new SpringDecoder(messageConverters).decode(response, type);
};
return new ResponseEntityDecoder(decoder);
}
}
}
Comment From: lexburner
@beldon Your rewritten behavior is consistent with my thoughts.But my final practice is to use byte[] as the return value which supported by ByteArrayHttpMessageConverter.Then use the service turns byte[] to file or other type. In my opinion,your inMemoryMultipartFile may be in line with OO design.but MultipartSupportConfig almost will not be reused.:)
Comment From: lexburner
@beldon Your code inspired me , thanks anyway.
Comment From: rainbow702
@beldon
could you please paste your server side code here? I'm wondering how you send the InMemoryMultipartFile to the clint. :handshake:
Comment From: denisgmarques
Yes, I do it. Here is my code,including uploading and downloading. I use feign-form to upload file. [feign-form]
Thank you! It helped me a lot!
Just an improvement, we can get filename from headers using a method like this:
public static String getFilenameFromHeader(Response resp) {
String headerName = "content-disposition";
Map<String, Collection<String>> headers = resp.headers();
Collection<String> values = headers.get(headerName);
String value = null;
if (values != null && values.isEmpty() == false) {
String[] array = new String[values.size()];
values.toArray(array);
value = array[0];
return value.substring(value.indexOf("filename=") + 10, value.length() - 1);
}
return value;
}
And call it in decoder bean implementation:
@Bean
@Primary
@Scope("prototype")
public Decoder decoder() {
Decoder decoder = (response, type) -> {
if (type instanceof Class && MultipartFile.class.isAssignableFrom((Class) type)) {
Collection<String> contentTypes = response.headers().get("content-type");
String contentType = "application/octet-stream";
if (contentTypes.size() > 0) {
String[] temp = new String[contentTypes.size()];
contentTypes.toArray(temp);
contentType = temp[0];
}
byte[] bytes = StreamUtils.copyToByteArray(response.body().asInputStream());
String filename = FeignUtils.getFilenameFromHeader(response);
InMemoryMultipartFile inMemoryMultipartFile = new InMemoryMultipartFile(filename, filename, contentType,
bytes);
return inMemoryMultipartFile;
}
return new SpringDecoder(messageConverters).decode(response, type);
};
return new ResponseEntityDecoder(decoder);
}