Spring Boot multipart upload getting null file object - java

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});

Related

Spring MVC Restful Multipart Form Not Working

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//

How to ignore any http content headers on Spring #RestController?

I have some webservice endpoints that should offer json data by default. Therefore configuring as follows:
#Configuration
public class ContentNegotiationConfiguration implements WebMvcConfigurer {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
}
Problem: now I want to create an endpoint that offers a file download (thus is not json).
#RestController
public class FileServlet {
#GetMapping(value = "/docs/{filename}", consumes = MediaType.ALL_VALUE, produces = APPLICATION_OCTET_STREAM_VALUE)
public Object download(#Pathvariable filename) {
File file = fileservice.resolve(filename);
return new FileSystemResource(file);
}
}
Accessing this endpoint from the browser works fine. I can download the files.
But: when using native clients that are not setting any http headers like content-type, accept-header etc, the access fails with:
WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver: Resolved
[org.springframework.web.HttpMediaTypeNotAcceptableException:
Could not find acceptable representation]
All of them result in the exception:
curl localhost:8080/docs/testfile.txt
curl -O localhost:8080/docs/testfile.txt
wget localhost:8080/docs/testfile.txt
This is probably because I set the default content type to json above in ContentNegotiationConfiguration. I cannot change that due to all the other endpoints that should be json by default.
Question: how can I explicit ignore that default json setting on that single endpoint, and always just offer the download stream?
Try custom ContentNegotiationStrategy with AntPathMatcher something like:
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// configurer.defaultContentType(MediaType.APPLICATION_JSON,MediaType.APPLICATION_OCTET_STREAM);
configurer.defaultContentTypeStrategy(
new ContentNegotiationStrategy() {
private UrlPathHelper urlPathHelper = new UrlPathHelper();
AntPathMatcher antPathMatcher = new AntPathMatcher();
#Override
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request == null) {
return null;
}
String path = this.urlPathHelper.getLookupPathForRequest(request);
if (antPathMatcher.match("/docs/*", path)) {
return Collections.singletonList(MediaType.APPLICATION_OCTET_STREAM);
} else {
return Collections.singletonList(MediaType.APPLICATION_JSON);
}
}
});
}
With the hint from #M. Deinum, I got it working as follows:
#GetMapping(value = "/docs/{filename}")
public void download(#Pathvariable filename) {
FileSystemResource file = new FileSystemResource(fileservice.resolve(filename));
rsp.setHeader("Content-Disposition", "attachment; filename=" + file.getFilename());
ResourceHttpMessageConverter handler = new ResourceHttpMessageConverter();
handler.write(file, MediaType.APPLICATION_OCTET_STREAM, new ServletServerHttpResponse(rsp));
}
That way writing directly to the stream bypassing the content negotiation, while still relying on the Spring class ResourceHttpMessageConverter for not having to implement the response writer myself.

Required MultipartFile parameter 'file' is not present when I request from Angular 4

I can do a request uploading a file from Postman, but when I do the request from Angular, it is returned the WARN "Required MultipartFile parameter 'file' is not present"
It follows my resource in API.
#PostMapping
public ResponseEntity<Conteudo> publicaConteudo(#RequestParam("file") MultipartFile file) throws JsonParseException, JsonMappingException, IOException {
/* ANYTHING */
return ResponseEntity.ok(new Conteudo());
}
And my service in Angular. I'm using JWT, but I also trying to do the request using HttpClient.
upload(file: File, conteudo: Conteudo): Promise<any> {
let formData: FormData = new FormData();
formData.append('file', file, file.name);
/* USING JWT
return this.http.post(this.conteudoUrl, formData)
.toPromise()
.then(response => response.json());
*/
let h1 = new HttpHeaders().set('Authorization', 'Bearer ' + localStorage.getItem('token'));
const req = new HttpRequest('POST',this.conteudoUrl, formData, {
headers: h1
});
return this.httpClient.request(req).toPromise();
}
And how I can do in Postman
I tried many solutions that I found, as create a Bean MultipartResolver and others, but any solve my problem and what I think more interesting is that it works fine in Postman.
In your AbstractAnnotationConfigDispatcherServletInitializer class do you have this method?:
#Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter("UTF-8");
encodingFilter.setForceEncoding(true);
return new Filter[]{new DelegatingFilterProxy("springSecurityFilterChain"),
new OpenEntityManagerInViewFilter(),encodingFilter, new MultipartFilter()};
}
maybe you havenĀ“t registered the multipart filter. Hope this help.

Multipart is null when uploading formdata to remote Tomcat server

I have a problem with multipart file. I am uploading a file from frontend (react)and pass it to my backend like this:
export function uploadExcelFile(files) {
const dataToTransfer = new FormData();
dataToTransfer.append('uploadedFile', files[0]);
return (dispatch) => {
const OPTIONS_POST_EXCEL = {
...OPTIONS_POST,
headers: {
},
body: dataToTransfer,
};
return fetch('/api/excelstuff/upload', OPTIONS_POST_EXCEL)
.then((res) => {
// do stuff to dispatch
})
.catch((err) => {
throw err;
});
};
}
The backend is implemented using Spring Boot, when it receives the formdata, the code looks like this:
#RequestMapping(value="/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<List<MyList>> uploadExcelFile(#RequestBody MultipartFile uploadedFile) {
log.warn("Is multipartfile empty? {}", uploadedFile);
return excelImporterService.uploadExcelFile(uploadedFile);
}
}
When I push my project to CloudFoundry, the RequestBody is null. But running the project locally it works! I am running the application on TomCat Server based.
I have tried including compile('commons-fileupload:commons-fileupload:1.3.3') on my gradle build. and included the following:
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSize(-1);
return commonsMultipartResolver;
}
I have included this on my configuration (yml file)
spring:
application:
name: somename
http:
multipart:
enabled: false
I hope it is a common problem and someone has solved it already, please help!
Try adding multipart/form-data as content type to your request.
Good luck!

How can I Upload small size file using angular2 and spring?

I am using angular2 and spring to upload file but when i used small size of file than i am getting error are:-
Resolved exception caused by Handler execution: org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
my code are
In angular2 code:-
function uploadRecord()
{
var formData = new FormData();
var record_id = 1234
formData.append("file", $scope.file);
formData.append("record_id",record_id); uploadRecordFile.save(formData, function(result) {
// some code here
}, onParseError);
}
Code in Resources class
#RequestMapping(value = "/uploadRecordFile",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public boolean uploadRecordFile(#RequestParam("file") MultipartFile file,#RequestParam("record_id") Long id)
throws Exception {
return true;
}
Is there any way to upload all size of files or is there any setting for it?
I am using following bean setting in my config:-
#Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver=new CommonsMultipartResolver();
resolver.setDefaultEncoding("utf-8");
return resolver;
}
When i remove above mention settings from config than files were uploading but big size files were not uploading as i need this setting i cannot change it so is there any setting from which i can upload both small and big size file?

Categories

Resources