These two days the boss suddenly to a task, that is, when the user concerned about our number, we should download its avatar, and then uploaded to the company's internal server. If you save the link to the avatar directly, when the user changes the avatar, it is likely that our product will have a 404 exception in obtaining the user's avatar.
Due to the technology stack used by the company for Spring Cloud (some eureka, feign) for service registration and remote invocation.
The point is that .... But using feignclient directly to remotely invoke the upload file interface on the registry will always be an error.
@PostMapping
@ApiOperation (value = "Upload file")
Public String fileUpload (@ApiParam (value = "File", required = True) @RequestParam ("Files") Multipartfile Multipartfile,
@ApiParam (value = "Usage (directory)", required = False) @RequestParam (value = "usage", required = False) String usage,
@ApiParam (value = "Synchronous (optional, default false)") @RequestParam (value = "Sync", required = false, DefaultValue = "false") Boolean sync) {
if (Multipartfile = = null) {
throw new IllegalArgumentException ("parameter exception");
}
String URL = map.get (key). Doupload (Multipartfile, usage, sync);
Return Uploadresult.builder (). URL (URL). build ();
}
The interface of the remote upload file.
@FeignClient ("Dx-commons-fileserver")
Public interface Fileserverservice {
@RequestMapping (value= "/file", method = Requestmethod.post)
Public String FileUpload (
@RequestParam ("file") Multipartfile Multipartfile,
@RequestParam (value = "usage", required = false), String usage,
@RequestParam (value = "Sync", required = false, DefaultValue = "false"), Boolean sync);
}
The normal feignclient remote calling code. However, this implementation, in the time of the call to throw the exception: Missingservletrequestpartexception, "Required request part ' file ' was not present"
Here to trace: fileserverservice.fileupload (Multipartfile, NULL, true) the source discovery sends the URL to the multipartfile as a URL stitching on the query string. So such a call must not be possible.
That's a search from Baidu. Keywords: feign upload will see a solution like this:
(translated from: http://www.jianshu.com/p/dfecfbb4a215)
Maven
<Dependency><Groupid>io.github.openfeign.form</Groupid><Artifactid>feign-form</artifactid> < version>2.1.0</version> </dependency> < dependency> <groupId> Io.github.openfeign.form</groupid> <artifactid>feign-form-spring</ artifactid> <version>2.1.0</version> </ Dependency>
Feign Config
@Configurationpublic class Feignmultipartsupportconfig {@ Bean @Primary @Scope (return new springformencoder ();} @ Bean public feign multipartloggerlevel () {return feign
Feign client
@FeignClient(name = "xxx",configuration = FeignMultipartSupportConfig.class)public interface OpenAccountFeignClient { @RequestMapping(method = RequestMethod.POST, value = "/xxxxx",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<?> ocrIdCard(@RequestPart(value = "file") MultipartFile file);}
This kind of program is very good and strong, copy it to solve the problem very well. remote invocation of file uploads is also implemented.
But the problem comes again. Because the above success is a large part of the configuration class, inside the encoder Bean. But my project requires more than just remote invocation of the uploaded interface, but also calls to other interfaces. In this case, you will find that other feignclient will throw an exception when called. It's really cue unravelling. The feeling of heartbreak. Trace Source Discovery:
The Encode method of Springformencoder calls the Encode method of the Encoder.default class when the object being transmitted is not multipartfile ....
public class Springformencoder extends Formencoder {
Private final Encoder delegate;
Public Springformencoder () {
This (new Encoder.default ());
}
Public Springformencoder (Encoder delegate) {
This.delegate = delegate;
}
@Override
public void Encode (Object object, Type bodyType, requesttemplate template) throws Encodeexception {
if (!bodytype.equals (Multipartfile.class)) {
Delegate.encode (object, BodyType, template);
Return
}
Multipartfile file = (multipartfile) object;
map<string, object> data = Collections.singletonmap (File.getname (), Object);
New Springmultipartencodeddataprocessor (). Process (data, template);
}
}
And this Encoder.default encode method determines the type of transmission is not a string or byte[], it throws an exception:
Class Default implements Encoder {
@Override
public void Encode (Object object, Type bodyType, requesttemplate template) {
if (BodyType = = String.class) {
Template.body (Object.ToString ());
} else if (BodyType = = Byte[].class) {
Template.body ((byte[]) object, NULL);
} else if (Object! = null) {
throw New Encodeexception (
Format ("%s is not a type supported by this encoder.", Object.getclass ()));
}
}
}
In this way, I have to continue to look for other methods, or can not remotely invoke other services. It's embarrassing.
That next is a variety of FQ, all kinds of Google, finally found the right answer.
The original is turned from (https://github.com/pcan/feign-client-test can download the sample code to study, so it is convenient to see the logic of the call)
Feign Client Test
A Test project, uses feign to upload Multipart files to a REST endpoint. Since Feign Library does not support Multipart requests, I wrote a custom Encoder
that enables this feature, using a HttpMessageConverter
C Hain that mimics Spring ' s RestTemplate
.
Multipart Request Types
A few request types is supported at the moment:
- Simple upload requests:one
MultipartFile
alongwith some path/query parameters:
testupload { @RequestLine ("Post/upload/{folder}Upload (@Param (" folder@Param ( "Filefile");
- Upload one File & Object (s): one
MultipartFile
alongwith some path/query parameters and one or more json-encoded object (s):
interface testupload { @RequestLine ( "Post/upload/{folder}" public Span class= "Pl-smi" >uploadinfo upload ( @Param ( "Folder" string folder, @Param ( "File") multipartfile file, @Param ( "Metadata" uploadmetadata metadata) ;}
- Upload Multiple files & objects:an array of
multipartfile
alongwith some path/query Parameters and one or more json-encoded object (s):
interface testupload { @RequestLine ( "Post/uploadarray/{folder}" Public list<uploadinfo> uploadarray ( @Param ( "Folder") string folder, @Param ( "Files" multipartfile[] files, Span class= "Pl-k" > @Param ( "Metadata") uploadmetadata metadata);}
Based on the above example code, I will also follow the above to modify my code. Because the principle is not in-depth research, so many code directly copied over to modify. There is one paragraph:
Feign.builder encoder = Feign.builder ()
. Decoder (new Jacksondecoder ())
. Encoder (New Feignspringformencoder ());
The encoder here is the example code of its own definition (my code also used this class), decoder with the Jacksondecoder, then this piece I also directly copied. Then modify the code to:
@Service
public class Uploadservice {
@Value ("${commons.file.upload-url}")
Private String http_file_upload_url;//Here configures the domain name of the upload file interface (HTTP (s)://xxxxx. Xxxxx. XX)
Public String uploadfile (multipartfile file, String usage, Boolean sync) {
Fileuploadresource Fileuploadresource = Feign.builder ()
. Decoder (new Jacksondecoder ())
. Encoder (New Feignspringformencoder ())
. Target (Fileuploadresource.class, Http_file_upload_url);
return fileuploadresource.fileupload (file, usage, sync);
}
}
Public interface Fileuploadresource {
@RequestLine ("Post/file")
String FileUpload (@Param ("file") Multipartfile file, @Param ("usage") string usage, @Param ("Sync") boolean sync);
}
The code that invokes the upload file is changed to run with the code above. But it still throws an exception. Tracking Fileuploadresource.fileupload (file, usage, sync) code, step by step to find the remote call and file upload is OK, the response is also 200. But the last decoder, throw the exception:
Unrecognized token ' http ': Was expecting (' true ', ' false ' or ' null ')
Just want to say what a fucking day!!! Can you make a mistake here?? The heart is very depressed .... No way, this method is still very powerful, because it will not affect the other remote service calls, although only here error. That only to trace the source again, found in the Jacksondecoder decode method:
@Override
Public Object decode (Response Response, type type) throws IOException {
if (response.status () = = 404) return util.emptyvalueof (type);
if (response.body () = = null) return null;
Reader reader = Response.body (). AsReader ();
if (!reader.marksupported ()) {
reader = new BufferedReader (reader, 1);
}
try {
Read the first byte to see if we had any data
Reader.mark (1);
if (reader.read () = =-1) {
return null; Eagerly returning null avoids "No content to map due to End-of-input"
}
Reader.reset ();
Return Mapper.readvalue (Reader, Mapper.constructtype (type));
} catch (Runtimejsonmappingexception e) {
if (e.getcause () = null && e.getcause () instanceof IOException) {
Throw IOException.class.cast (E.getcause ());
}
Throw e;
}
}
Which goes to: return Mapper.readvalue (Reader, Mapper.constructtype (type)); And then it throws an exception. Depressed AH. Finally do not know how to think, just try to remove this decoder, do not set decoder. That's a lucky moment. All tuned in .... So the modified Uploadservice code is:
@Service
public class Uploadservice {
@Value ("${commons.file.upload-url}")
Private String http_file_upload_url;//Here configures the domain name of the upload file interface (HTTP (s)://xxxxx. Xxxxx. XX)
Public String uploadfile (multipartfile file, String usage, Boolean sync) {
Fileuploadresource Fileuploadresource = Feign.builder ()
. Encoder (New Feignspringformencoder ())//There's no decoder added here.
. Target (Fileuploadresource.class, Http_file_upload_url);
return fileuploadresource.fileupload (file, usage, sync);
}
}
This blog is because it cost me a lot of time a day, so I have to write down, or the next time I met, it might take some time to get it done. But the example code mentioned above is really bull. It's going to have to be studied later.
I hope these records will be helpful to those small partners who have the same problems as I do and solve the problem as soon as possible.
Spring Cloud feign Client implements Multipartfile upload file function