I've created a Restful service with Spring MVC as shown below. I called it using Postman. I placed a breakpoint on 'return "hello World"'. There's no hit on the breakpoint with the error message "Required request part 'file' is not present".
However, if I comment out the '#RequestParam("file")' annotation, the breakpoint is hit with the parameter "file" being null.
What could have gone wrong? Very puzzled.
#RestController
#RequestMapping("/dp")
public class DpWebService implements IDpWebService {
#Override
#Bean
public MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("");
}
#Override
#Bean
public MultipartResolver multipartResolver() {
org.springframework.web.multipart.commons.CommonsMultipartResolver multipartResolver = new org.springframework.web.multipart.commons.CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
#Override
#RequestMapping(path = "/send", method = RequestMethod.POST, consumes = "multipart/form-data")
public String sendManifest(#RequestParam("file") MultipartFile file) {
return "Hello World";
}
}
Postman
Postman Header
Check your POSTMAN request Configuration. I think you have not changed the input type to File from Text. Uploading images, check the images. Hover the mouse over that area in Postman and select File from the drop-down menu.
Having Beans defined in your RestController is not a good design. Please separate out a Configuration class with #Configuration annotation and define your beans. The reasons being: Single Responsibility Principle - each class should only do about one thing.
https://java-design-patterns.com/principles/#single-responsibility-principle
#RequestParam might not be working for you because of the nature of the data that is contained in the file that you are sending through the request. RequestParam is likely to be used with name-value form fields. For complex data like json/xml it is advisable to use #RequestPart instead.
Instead of the #RequestParam annotation use the #RequestPart annotation.
Annotation that can be used to associate the part of a
"multipart/form-data" request with a method argument.
Try using it like :
#RestController
#RequestMapping("/dp")
public class DpWebService implements IDpWebService {
#Override
#Bean
public MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("");
}
#Override
#Bean
public MultipartResolver multipartResolver() {
org.springframework.web.multipart.commons.CommonsMultipartResolver multipartResolver = new org.springframework.web.multipart.commons.CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
#Override
#RequestMapping(path = "/send", method = RequestMethod.POST, consumes = "multipart/form-data")
public String sendManifest(#RequestPart("file") MultipartFile file) {
return "Hello World";
}
}
Also make sure that the request from the postman is getting triggered correctly :
Remove any un wanted request params from postman.
Make sure that under 'Body' tab the form-data is selected. Also make
sure that when selected the file in the key the name is provided as
'file' and type is also selected as file instead of text.
This is my working example.
#PostMapping("/uploadFile")
public UploadFileResponse uploadFile(#RequestParam("file") MultipartFile file) {
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath().path("/downloadFile/")
.path(fileName).toUriString();
return new UploadFileResponse(fileName, fileDownloadUri, file.getContentType(), file.getSize());
}
application.properties
## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB
## File Storage Properties
# Please change this to the path where you want the uploaded files to be stored.
file.upload-dir=C://Users//abc//Documents//
Related
This is related to an existing spring boot question raised by me(Request Body is not properly encoded and hidden when using spring form encoder in Feign Client).
According to this question, we can add either content type in headers or add during request mapping itself as consumes.
So what I did was added content type in headers in the client configuration class
public class EmailClientConfiguration {
#Bean
public RequestInterceptor requestInterceptor(Account<Account> account) {
return template -> {
template.header("Content-Type", "application/x-www-form-urlencoded");
};
}
#Bean
public OkHttpClient client() {
return new OkHttpClient();
}
#Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
#Bean
public Decoder feignDecoder() {
return new JacksonDecoder();
}
#Bean
public Encoder feignFormEncoder () {
return new SpringFormEncoder(new JacksonEncoder());
}
}
and I see in the headers the content type is correctly set as application/x-www-form-urlencoded when the request is sent. But the request body is still sent in json format and also not hidden.
Request Body:
Map<String, String> requestBody = new HashMap<>();
requestBody.put("username", "xyz");
requestBody.put("email", "xyz#gmail.com");
requestBody.put("key", "xxx");
Request Body received in server end:
{"{\n \"key\" : \"xxx\",\n \"email\" : \"xyz#gmail.com\",\n \"username\" : \"xyz\"\n}"
When I add consumes in my request mapping as application/x-www-form-urlencoded
#FeignClient(name = "email", url = "localhost:3000",
configuration = EmailClientConfiguration.class)
public interface EmailClient {
#PostMapping(value = "/email/send", consumes = "application/x-www-form-urlencoded")
ResponseDto sendEmail(#RequestBody Map<String, String> requestBody);
}
it works fine(request body is hidden in server end and also properly encoded). And when I removed the header in the configuration class and adding only consumes works fine without no issues but the vice versa has this problem.
I searched in internet for this and couldn't find any answer.
Feign encodes the request body and parameters before passing the request to any RequestInterceptor (and rightly so). If you do not declare consumes = "application/x-www-form-urlencoded", SprinFormEncoder doesn't know that you're trying to send form data, so it delegates serialization to the inner JacksonEncoder which only does JSON (see for yourself by printing template.body() before setting the header).
Handling such a well-supported header in the interceptor doesn't seem like a good idea, when you already have consumes. If you insist on doing so, you have to provide your own encoder which doesn't rely on the header value and always outputs form-urlencoded data.
I an creating an endpoint with spring boot...i can upload image to folder and save it via postman everythink works good.
i have a problem with get method when i am adding the value #RequestMapping value = "getImage/{imageName:.+}" in postman i add http://localhost:8080/api/images/getImage/{burger+png}
is that corect ???
#RequestMapping(value = "api/images")
public class ImageController {
#Autowired
public ImageService imageService;
#PostMapping(value ="upload")
public ResponseEntity uploadImage(#RequestParam MultipartFile file){
return this.imageService.uploadToLocalFileSystem(file);
}
#GetMapping(
value = "getImage/{imageName:.+}",
produces = {MediaType.IMAGE_JPEG_VALUE,MediaType.IMAGE_GIF_VALUE,MediaType.IMAGE_PNG_VALUE}
)
public #ResponseBody byte[] getImageWithMediaType(#PathVariable(name = "imageName") String fileName) throws IOException {
return this.imageService.getImageWithMediaType(fileName);
}
}
what should be the correct request url ???
It seems like it's reaching the backend fine, but failing to find path. Usually API endpoints end with parameters with a slug or query param. You can try either of the following to see if it works:
http://localhost:8080/api/images/getImage/burger.png
http://localhost:8080/api/images/getImage?imageName=burger.png
Keep in mind, you want to make sure that file exists at the path it's mentioning at the very top of the trace in the JSON response. This may depend on how you uploaded the file and with what name.
I have written a method with get request mapping it gives list of users. As jakson binding dependency is there is gives response in JSON. I've also dependency for XML which is Jackson Dataformat XML.So, if Accept is application/json it returns the response in JSON and if it is in application/xml it returns in XML.But by default it gives JSON response. SO, I wanted to add Accept header if not present and make it's default value as application/xml.
#GetMapping(path="/getAll")
public List<User> getUsers(#RequestHeader(value= "Accept" ,required=false, defaultValue="application/xml") String Accept)
{
return service.findAll();
}
But in above case, the header is not setting.
In order to do so, you need to modify your controller method to return ResponseEntity<List<User>> as following:
#GetMapping(path="/getAll")
public ResponseEntity<List<User>> getUsers(#RequestHeader(value= "Accept" ,required=false, defaultValue="application/xml") String Accept) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setLocation(location);
responseHeaders.set("Accept", "Value");
return new ResponseEntity<List<User>>(service.findAll(), responseHeaders, HttpStatus.CREATED);
}
If you just want to respond XML from your spring boot application, use the following custom webMvcConfiguration. Setting a default Accept header just to respond XML does not seem like a good idea.
#Configuration
public class WebMvcConfiguration {
#Bean
public WebMvcConfigurer myWebMvcConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_XML);
}
};
}
}
if you are already using a custom WebMvcConfigurerAdapter, just override the configureContentNegotiation(...) method as above.
I am getting a problem while implementing multipart file upload using spring boot 1.5.2.
Here is the situation, I have a mapping to handle file upload process.While I start the spring server, it starts without any error. The problem is that I would either able to upload the file perfectly fine or I would get null on all attribute in FileBucket object.
This situation would stay forever if I do not shutdown the server.
If it could upload, it would upload fine for the rest of the time.
If not, it won't work until I restart the server(likely more than one
time)
Here is the mapping.
#RequestMapping(value = {"/api/upload"}, method = RequestMethod.POST)
public ResponseEntity<Map<String, Integer>> upload(#Valid FileBucket fileBucket, BindingResult result) throws IOException {
Session session = sessionFactory.openSession();
User user = (User) session.load(User.class, getUserId());
Map<String, Integer> model = new HashMap<String, Integer>();
if (result.hasErrors()) {
System.out.println("validation errors");
System.out.println(result);
session.close();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
} else {
int documentId = saveDocument(fileBucket, user);
model.put("documentId", documentId);
session.close();
return new ResponseEntity<Map<String, Integer>>(model, HttpStatus.OK);
}
}
And the FileBucket object
public class FileBucketConversation {
private MultipartFile file;
public MultipartFile getFile() {
return file;
}
public void setFile(MultipartFile file) {
this.file = file;
}
}
I have tried few ways to implement file upload, and still having the same situation.
Using StandardServletMultipartResolver.
#Bean(name = "multipartResolver")
public StandardServletMultipartResolver resolver() {
return new StandardServletMultipartResolver();
}
Using CommonsMultipartResolver v1.3.2.
#Bean(name="multipartResolver")
public CommonsMultipartResolver multipartResolver () {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(MAX_FILE_SIZE);
return resolver;
}
overriding MultipartFilter
#Bean
#Order(0)
public MultipartFilter multipartFile() {
MultipartFilter multipartFilter = new MultipartFilter();
multipartFilter.setMultipartResolverBeanName("multipartResolver");
return multipartFilter;
}
Enable spring.http.multipart in properties file
spring.http.multipart.enabled=true
spring.http.multipart.max-file-size=20Mb
spring.http.multipart.max-request-size=20Mb
I really have no clue where to start looking. The problem happen occasionally, it do not happen every time I start the server but most of the time. Hoping some one could help me.
Thank you.
I had the same problem, this is my solution:
In application.yml:
spring:
http:
multipart:
enabled: false
In configuration:
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
...
#Bean
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
In RestController:
#PostMapping(value = "api/upload", consumes = "multipart/form-data")
public void enablePurchase(#RequestHeader HttpHeaders headers,
FileBucketConversation fileBucketConversation) {
...
}
Important:
In your client don't use any header to define the Content-type or boundary. I'm using Angular 4 and when I remove these headers from my code it works (I only set the User token):
/* DON'T USE THIS:
let boundary = "ABDCE";
headers.append("Content-type", "multipart/form-data;boundary=" + boundary);
headers.append("enctype", "multipart/form-data;boundary=" + boundary);
headers.append("boundary", boundary);
*/
I hope this help you.
Update for Spring Boot 2 and Spring 5
spring.http.multipart.enabled is deprecated, so forget about it.
Registering my own beans for MultipartConfigElement or MultipartResolver broke it for me.
I ended up with a #PostMapping method with a single parameter, #RequestPart(name = "file") MultipartFile file. Adding a parameter #RequestHeader HttpHeader headers helped me make sure the client was sending the required header Content-Type with a boundary.
I'm using a Node client with the form-data library and node-fetch. Here's the client code:
const formData = new FormData();
const fileMetadata = {filename: fileName, contentType: 'image/png', knownLength: fs.statSync(pathToFile)};
const formData.append('file', fs.createReadStream(pathToFile), fileMetadata);
fetch(url, {method: 'POST', body: formData});
I have my project with SpringBoot 1.5.1 gradle.
I need to response with pdf file with "OK" or some other statuses.
So problem is when I request with "Postman" code invokes 2 times.
when I request with "curl" code invokes 1 time.
Obviously I want to invoke it 1 time.
I have an application class with:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And my controller with:
#RequestMapping(value = "/report/{reportTemplate:.+}", method = POST)
#ResponseBody
public ResponseEntity createReport(HttpEntity<List<ParametersEntity>> httpEntity,
#PathVariable String reportTemplate) throws IOException {
byte[] data = ...;// my data
return ResponseEntity
.ok()
.contentLength(data.length)
.contentType(MediaType.APPLICATION_PDF)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=output.pdf")
.contentType(MediaType.parseMediaType(MediaType.APPLICATION_PDF_VALUE))
.body(data);
}
createReport is in #RestController class.
Also I have application.properties file in my src/main/resources/
server.port: 10500
management.port: 10501
management.address: 127.0.0.1
Your code looks OK. Nothing wrong with it.
If you are in DEBUG mode when invoking the request from the POSTMAN it may confuse it and send the request for a second time. I gues it depends on some configurations. But while you have no problems with CURL you must search the problem in POSTMAN probably not your code.