I need to upload a MultipartFile to a third-party service via my own backend service. The parameter in the multipart form is 'nameA' but the third-party service need its param name is 'nameB'.
Normally I can solve it in two ways:
Change the param name of frontend to 'nameB'
Change the param name of MultipartFile to 'nameB' in backend service.
But I cannot change the frontend now, so I want to finger out how to modify the param name of MultipartFile in backend service.
The controller of backend service is:
#PostMapping("/url")
public Response method(#RequestParam("nameA") MultipartFile file) {
return Service.method(file);
}
In Feign Client for uploading file to third-party service:
#PostMapping(value = "/url1/url2", consumes = MULTIPART_FORM_DATA_VALUE)
Response method(#RequestParam("nameB") MultipartFile file);
However the use of specify the param with 👆 #RequestParam doesn't work.
So does anyone know how to modify the param name of MultipartFile? Thanks a lot!
That is completely unrelated to your controllers spring annotations and instead depends on how you would upload that file to the 3rd party service. Since you mentioned uploading it, I assume you need to create a new HTTP multipart request in your backend service that would upload the file to the 3rd party service. When creating that request, you will be able to specify the name of the multipart part.
You can set a name of the MultipartFile in the FeignClient as you need, this is a sample from my project:
Сontroller API (receiving side):
#RestController
#RequestMapping("/files")
public class FilesController {
#PostMapping(path = "/upload")
#ResponseStatus(HttpStatus.CREATED)
public FileDescriptor upload(#RequestPart(value = "data") MultipartFile multipartFile) {
...
}
}
Feign client (sending side):
#FeignClient(value = "file-service", configuration = FeignConfig.class)
public interface ContentStorageFeign {
#ResponseBody
#PostMapping(value = "/files/upload", produces = MediaType.APPLICATION_JSON_VALUE)
FileDescriptor create(#RequestPart(value = "data") MultipartFile multipartFile);
}
And this is my FeignConfig:
#Configuration
public class FeignConfig {
#Bean
public Decoder decoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return new ResponseEntityDecoder(new SpringDecoder(messageConverters));
}
#Bean
public Encoder encoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
But if you need to create a new request(from a file received from somewhere) and rename this file before sending, this is another problem.
Related
I want to consume both JSON & files in an API for my spring boot application (using Spring WebFlux). I tried multiple ways as was recommended over the internet (Article1, Article2, Article3), but it's not working for me. The API is not being hit and getting status 415 (Unsupported Media Type) from Postman.
Spring-Boot version : 2.5.3
Spring-Web/Webflux version : 5.3.9
Employee :
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Employee {
private String name;
private int age;
private int mobileNo;
private List<MultipartFile> files;
}
Controller :
#PostMapping(path = "employee", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
public Mono<Response> processEmployeeDocs(#RequestPart Employee request, #RequestPart List<MultipartFile> files) {
Employee employee = new Employee(request.getName(), request.getAge(), request.getMobileNo(), files);
return employeeService.processDocs(employee);
}
Instead of using MultipartFile use FilePart to consume multipart content as per the spring docs
Below is the working code snippet
#PostMapping(path = "employees", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
public Mono<ResponseEntity<Response>> processEmployeeDocs(#RequestPart Employee request, #RequestPart Flux<FilePart> files) {
log.info("processing request {}", request);
return files.flatMap(part -> part.transferTo(Paths.get("temp", File.separator, part.filename())))
.then(employeeService.processDocs(request))
.then(Mono.just(ResponseEntity.ok().build()));
}
The above code will save the files in "temp" directory. You can use those files for processing your logic.
Postman Request
I'm using MultipartFile to upload files in web application. Is it possible to give the ability to the user(publisher) to add some description and tags to the uploaded file, which will be used for searching later?
You can use two Media Types:
consumes = { MediaType.APPLICATION_JSON_VALUE,MediaType.MULTIPART_FORM_DATA_VALUE })
And we need to pass the given parameter as Tag and Multipart file. Here, make sure we can pass only String + file not POJO + file.
#RequestPart(“tag”) String tag, #RequestPart(“file”) List<MultipartFile> file
Then convert the String to Json using ObjectMapper in Service layer.
Tag tagPojo = new ObjectMapper().readValue(tag, Tag.class);
So it would look like:
#PostMapping(value = "/send", consumes = { MediaType.APPLICATION_JSON_VALUE,MediaType.MULTIPART_FORM_DATA_VALUE })
public void upload(#RequestPart(“tag”) String tag, #RequestPart(“file”) List<MultipartFile> file) {
Tag tagPojo = new ObjectMapper().readValue(tag, Tag.class);
}
You can add the field in POST request's body:
#PostMapping(value = "/upload", consumes = MULTIPART_FORM_DATA_VALUE)
ResponseEntity<Void> uploadFile(
#RequestParam("files") List<MultipartFile> files,
#RequestParam("descr") String description
);
and send a request for example:
So I'm making a RESTful API backend for a JavaScript frontend and want to upload a file to Google Cloud Storage. I have this function to handle the file upload:
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST, consumes = { "multipart/form-data" })
public ResponseEntity<?> uploadFile(#ModelAttribute("file") FileDTO fileDTO) {
FunctionResponse uploadResponse = cloudStorage.uploadObject(fileDTO.getFile());
if (uploadResponse.successful()) {
return new ResponseEntity<>(uploadResponse.getMessage(), HttpStatus.OK);
} else {
return new ResponseEntity<>(uploadResponse.getMessage(), HttpStatus.BAD_REQUEST);
}
}
My fileDTO class looks like this:
public class FileDTO implements Serializable {
private MultipartFile file;
public MultipartFile getFile() {
return file;
}
}
However whenever I try and access the MultipartFile it always throws a java.lang.NullPointerException.
I have tried various SO answers from threads with similar problems but none have worked so far.
I have multipart.enabled=true in my application.properties.
Any help would be appreciated and let me know if you need any more information.
When a multipart/form-data request arrives, the data must be obtained by using #RequestParam, not #ModelAttribute + if the value you need of the request is a file, then, it should be deserialized into a MultipartFile object.
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST, consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadFile(#RequestParam(name = "file") MultipartFile file) {
// your handle ...
}
The UI for my webapp has the ability to either upload a file(csv), or send the data as json in request body. However either a file upload, or a json request would be present in the request and not both. I am creating a spring rest controller which combine file upload and also accepts the request json values as well.
With the below endpoint tested from postman, I am not getting exception:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
#RestController
public class MovieController {
private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
#PostMapping(value="/movies", consumes = {"multipart/form-data", "application/json"})
public void postMovies( #RequestPart String movieJson, #RequestPart(value = "moviesFile") MultipartFile movieFile ) {
// One of the below value should be present and other be null
LOGGER.info("Movies Json Body {}", movieJson);
LOGGER.info("Movies File Upload {}", movieFile);
}
}
Appreciate any help in getting this issue solved?
Note: I was able to build two separate endpoint for file upload and json request, but that won't suffice my requirement. Hence I'm looking for a solution to combine both
Try something like:
#RequestMapping(value = "/movies", method = RequestMethod.POST, consumes = { "multipart/form-data", "application/json" })
public void postMovies(
#RequestParam(value = "moviesFile", required = false) MultipartFile file,
UploadRequestBody request) {
In RequestBody you can add the parameters you want to send.
This will not send the data as JSON.
Edit:- I forgot to add the variable for the Multipart file and I mistakenly used the RequestBody which is reserved keyword in spring.
Hope it helps.
I would suggest to create two separate endpoints. This splits and isolates the different functionality and reduces the complexity of your code. In addition testing would be easier and provides better readability.
Your client actually has to know which variable to use. So just choose different endpoints for your request instead of using different variables for the same endpoint.
#PostMapping(value="/movies-file-upload", consumes = {"multipart/form-data"})
public void postMoviesFile(#RequestPart(value = "moviesFile") MultipartFile movieFile ) {
LOGGER.info("Movies File Upload {}", movieFile);
}
#PostMapping(value="/movies-upload", consumes = {"application/json"})
public void postMoviesJson( #RequestPart String movieJson) {
LOGGER.info("Movies Json Body {}", movieJson);
}
I have a dev server which revolves angular 2 at localhost: 4200, and tomcat with Spring on localhost: 8080.
I try to upload a file to the server in the following manner:
angular code:
uploadAvatar(file:File){
let xhr = new XMLHttpRequest()
xhr.open("POST",`http://localhost:8080/api/upload/avatar`)
xhr.setRequestHeader("Content-Type","multipart/form-data")
xhr.send(file)
}
Controller code Spring:
#RequestMapping(value = "/api/upload/avatar", method = RequestMethod.POST)
public String uploadFile(MultipartFile file){
log.info(file);
return file.getName();
}
But after trying to download a file error appears in the java-console:
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request;
nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
How do I fix this error?
Thank you.
UPDATE 1
The "duplicate" is used with Spring MVC + JSP, I'm trying to download a file via Ajax. And the version of the decision does not help me given there.
UPDATE 2
Spring Boot(v1.4.3.RELEASE)
I use the java configuration, if you want I will give an example of a full configuration.
I found the solution to my problem, I postorabs below describe in detail what I did for this.
As the presentation I use Angular 2, sending the file takes place in the following manner.
uploadFile(file:File){
let form = new FormData();
form.append("file",file)
let xhr = new XMLHttpRequest()
xhr.open("POST",`${URL}/api/upload/avatar`)
xhr.send(form)
}
Content-Type and the boundary in this case, are automatically linked.
The following manipulations need to be done on the server Stronie:
Add two bean:
#Bean(name = "commonsMultipartResolver")
public MultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
#Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize("10MB");
factory.setMaxRequestSize("10MB");
return factory.createMultipartConfig();
}
The controller looks like this:
#RequestMapping(value = "/api/upload/avatar", method = RequestMethod.POST,consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFile(#RequestPart MultipartFile file){
log.info(file);
return file.getOriginalFilename();
}