Sending file to Spring Boot REST using Axios - spring

I am trying to send a csv file to my java spring boot backend. The code to send my file is below:
var url = 'http://localhost:3001/UploadFile';
var file = this.state.file;
var formData = new FormData();
formData.append("file", file);
axios.post(url, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
And the code to accept my file from Spring Boot:
#CrossOrigin
#RequestMapping("/UploadFile")
#ResponseBody
public void uploadFile(#RequestParam("file") MultipartFile file) {
}
However, it doesn't seem to work. I keep getting an error saying that the 'Current request is not a multipart request'. Any ideas?

It's not sufficient to specify content-type in frontend you need to do it in controller as well.
You should tell to spring controller what it should consume and also it would be nice to set RequestMethod as POST like this:
#CrossOrigin
#RequestMapping("/UploadFile")
#ResponseBody
public void uploadFile(#RequestParam("file") MultipartFile file, method = RequestMethod.POST, consumes = "multipart/form-data") {
}

Related

Unable to upload file due to Content-Type "multipart/form-data" not set for request body of type StandardMultipartFile

I have a remote service A which does the file upload. I have service B which calls the upload API of service A through FeignClient to upload a file
The method definition in Service A is something like
ResponseEntity<?> upload(#RequestPart("file") MultipartFile file) { }
And the method in Service B is
#FeignClient(url = "http://localhost:5000/")
public interface uploadService {
#RequestMapping(method = RequestMethod.POST, value = "/serviceA/upload")
#Headers("Content-Type: multipart/form-data")
void uploadFile(#RequestPart("file") MultipartFile file);
}
I am getting the error
Content-Type "multipart/form-data" not set for request body of type StandardMultipartFile
I have tried most of the suggestions on https://github.com/spring-cloud/spring-cloud-netflix/issues/867 and
https://github.com/OpenFeign/feign-form but nothing works for me
I was able to solve this issue by simply adding consumes = "multipart/form-data" in the RequestMapping. The reason was that I was mixing spring based annotations with open feign annotations. #Headers("Content-Type: multipart/form-data") works with Open feign. Here I am using spring-cloud-openfeign which provides abstraction to Open feign and make it easy to integration with spring framework components.
#FeignClient(url = "http://localhost:5000/")
public interface uploadService {
#RequestMapping(method = RequestMethod.POST, value = "/serviceA/upload" consumes = "multipart/form-data" )
void uploadFile(#RequestPart("file") MultipartFile file);
}
If you have trouble just within the test just use org.springframework.mock.web.MockMultipartFile where you can set contentType as one of argument in construtor.

Storing an image using ajax request inside Postgresql in Spring application

I am trying to store an image in postgresql db from my spring application but I am stuck with multiple problems and confusion.
First let me give you the overview of my spring application code:
var documentData = new FormData();
function update(){
var fname=document.getElementById("fname").value;
var lname=document.getElementById("lname").value;
var password=document.getElementById("password").value.trim();
var email=document.getElementById("email").value;
documentData.append('fname',fname);
documentData.append('lname',lname);
documentData.append('password',password);
documentData.append('email',email);
documentData.append('profilePic',$('#profilePic').attr('src'));
alert($('#profilePic').attr('src'));
$
.ajax({
type : 'PUT',
url : baseUrl + "/restApi/UpdateUser",
data : JSON
.stringify({
documentData
}),
success: function(){
location.reload(true);
},
error : function(e) {
},
dataType : "json",
contentType : "application/json"
});
}
}
$(function () {
$(":file").change(function () {
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = imageIsLoaded;
reader.readAsDataURL(this.files[0]);
}
});
});
function imageIsLoaded(e) {
$('#profilePic').attr('src', e.target.result);
$('#viewPic').attr('src',e.target.result);
};
I have this controller
#RequestMapping(value = "/restApi/UpdateUser", method = RequestMethod.PUT, headers = "Accept=application/json")
public ServiceResponse modifyUser(#RequestBody Object user)
{
return setDataPut("http://localhost:7020/UpdateUser",user,getUserObject().getUsername(),getUserObject().getPassword());
}
In my setDataPut method I am sending response with GSON
WebResource webResource = client
.resource(path);
ClientResponse response = webResource.type("application/json").accept("application/json")
.put(ClientResponse.class, gson.toJson(object));
In model class I took byte[] type variable and in db I made column with type bytea
Now In above gson service the call is made to rest services hosted.
#CrossOrigin
#RequestMapping(value = "/ModifyUser", method = RequestMethod.PUT, headers = "Accept=application/json")
public ServiceResponse modifyUser(#RequestBody User user) {
/*Code which deals with storing User data*/
}
So I have taken all data through model User class.
Now earlier it was working perfectly until I wanted to store image also.
Nothing is getting saved no error.
Confusion: If I am sending image with some data then should I change content type or add enctype as "multipart/form-data". But If I use multipart then what should be changed in headers. Like #produces #consumes. Major doubt is whether I need to convert the image in binary code before sending?
Problem: I am having trouble in storing image in postgresql through ajax request. Please look through my code what is the problem.
You are asking quite a lot in one question here. Essentially, you are asking how to upload files from the browser/client to the Spring-based server, how to handle that upload in the Spring-based server in order to save it into a Postgresql database and associate it with my User entity so that I can fetch it again later.
So, let's have a go at answering all of that for you.
Let's start on the client-side. This code will upload the chosen file to an existing resource:-
index.html
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script>
function upload() {
var data = new FormData();
data.append('file', jQuery('#file')[0].files[0]);
jQuery.ajax({
url: '/userImage/userId',
data: data,
cache: false,
contentType: false,
processData: false,
method: 'POST',
type: 'POST', // For jQuery < 1.9
success: function(data){
alert(data);
}
});
}
</script>
</head>
<body>
<div>
<h1>New File</h1>
<input type="file" id="file" name="file"/>
<button onclick="upload()">Upload</button>
</div>
</body>
</html>
Now, turning our attention to the Spring-bsed server side. To abstract away the implementation of exactly how to store the uploaded file in the database (and how to update it, and how to fetch it, and how to delete it and so on) I would use Spring Content otherwise you have a lot of code to write that Spring Content already implements for you.
So, add the following dependencies:
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-jpa</artifactId>
<version>0.1.0</version> // 0.0.11 for Spring Boot 1 dependencies
</dependency>
Configure the database schema creation in one of your config classes:
Config.java
#Configuration
#EnableJpaStores // enable JPA-based storage
public class PostgresqlTestConfig {
...dataSource and entityManager, etc beans...
#Value("/org/springframework/content/jpa/schema-drop-postgresql.sql")
private Resource dropReopsitoryTables;
#Value("/org/springframework/content/jpa/schema-postgresql.sql")
private Resource dataReopsitorySchema;
#Bean
DataSourceInitializer datasourceInitializer() {
ResourceDatabasePopulator databasePopulator =
new ResourceDatabasePopulator();
databasePopulator.addScript(dropReopsitoryTables);
databasePopulator.addScript(dataReopsitorySchema);
databasePopulator.setIgnoreFailedDrops(true);
DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(dataSource());
initializer.setDatabasePopulator(databasePopulator);
return initializer;
}
}
Associate content with your User entity:
User.java
#Entity
public class User {
...existing fields...
#ContentId private String contentId;
private String mimeType;
}
Create a UserStore:
UserImageStore.java
public interface UserImageStore extends AssociativeStore<User, String> {
}
Update your controller to handle the upload of files, store them in the database and associating that stored image on your entity:
UserController.java
#Autowired
private UserImageStore store;
...
#RequestMapping(value="/userImage/{userId}", method = RequestMethod.POST)
public ResponseEntity<?> setContent(#PathVariable("userId") Long id, #RequestParam("file") MultipartFile file)
throws IOException {
User user = // fetch your existing user here
user.setMimeType(file.getContentType());
String originalFilename = file.getOriginalFilename();
InputStream is = file.getInputStream();
OutputStream os = ((WritableResource)store.getResource(originalFilename)).getOutputStream();
IOUtils.copyLarge(is, os);
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
// associate content (this will update the #ContentId field)
store.associate(user, originalFilename);
// save updated content-related info
save(user);
return new ResponseEntity<Object>(HttpStatus.OK);
}
return null;
#RequestMapping(value="/userImage/{userId}", method = RequestMethod.GET)
public ResponseEntity<?> getContent(#PathVariable("userId") Long id) {
User user = // fetch your existing user here
Resource r = store.getResource(user.getContentId());
HttpHeaders headers = new HttpHeaders();
headers.setContentLength(r.getContentLength());
headers.set("Content-Type", user.getMimeType());
return new ResponseEntity<Object>(r, headers, HttpStatus.OK);
}
return null;
}
That's about it. So what's going to happen here is that when your app starts it sees the dependency on spring-content-jpa and then it sees your UserImageStore. Assumes that you want to store images (BLOBs) in jpa and injects a JPA implementation of the UserImageStore interface meaning that you don't need to write it yourself. Spring Content hides the implementation but exposes a relatively simply interface (based on Spring Resource actually) that is #Autowired into your controller making that implementation simple.
Anyways, let me know if you are using Spring Data or Spring Boot and I can update this answer so that it is more relevant for you.
HTH

Angular 4 and Spring Rest: How to post FormData containing File and model object in a single request

I would like to send a File object along with custom model object in a single request.
let formData:FormData = new FormData();
let file = this.fileList[0];
formData.append('file', file, file.name);
formData.append('address', JSON.stringify(customObj));
...
this.http.post(fileServeUrl, formData)
My backend is in Spring Rest as below
#RequestMapping(value = "/fileServe",
produces = {"application/json"},
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE},
method = RequestMethod.POST)
ResponseEntity<Image> uploadFile(#RequestPart("file") MultipartFile imageData, #RequestPart("address") Address address) throws IOException {...}
I was able to receive the data if I pass simple String along with File though.
formData.append('file', file, file.name);
formData.append('address', addressText);
Backend
#RequestMapping(value = "/fileServe",
produces = {"application/json"},
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE},
method = RequestMethod.POST)
ResponseEntity<Image> uploadFile(#RequestPart("file") MultipartFile imageData, #RequestPart("address") String addressText) throws IOException {...}
I tried #RequestBody for my custom object but even that didn't work. Any advise please.
The problem with #Requestbody and #RequestPart annotation is that spring use the HttpMessageConverter to take convert the incoming json message into the your object. As you send form data with a file and a text value spring can not convert it into your object. I am afraid you have to pass the value of address seperatetly.
#RequestMapping(value = "/fileupload", headers = ("content-type=multipart/*"), method = RequestMethod.POST)
public ResponseEntity<AjaxResponseBody> upload(#RequestParam("file") MultipartFile file, #RequestParam String name, #RequestParam String postCode) {
AjaxResponseBody result = new AjaxResponseBody();
HttpHeaders headers = new HttpHeaders();
if (!file.isEmpty()) {
try {
Address address = new Address();
address.setName(name);
result.setMsg("ok");
return new ResponseEntity<AjaxResponseBody>(result, headers, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<AjaxResponseBody>(HttpStatus.BAD_REQUEST);
}
} else {
return new ResponseEntity<AjaxResponseBody>(HttpStatus.BAD_REQUEST);
}
}
Expept if you find a way your client app send a file with MimeType of image/jpg and and an address of application/json which allow spring to parse the json and map to your Address object which i couldn't do it.

AngularJS Post request to Web service for downloading a file

I need to send JSON to a web service using HTTP POST method in AngularJS to download a file.
AngularJS:-
$http
.post(
'url',
'My Json data ')
.success(function(response) {
console.log('file downloading');
})
.error(
function(response) {
console
.log('Error while downloading file');
});
Spring Controller:-
#RequestMapping(value = "/url", method = RequestMethod.POST)
#ResponseBody
public void getfile(#RequestBody List<ABC> abc, HttpServletResponse response)
throws JRException, IOException, SQLException {
//My code here
response.reset();
response.setContentType("application/x-pdf");
response.setHeader("Content-disposition", "attachment; filename=ABC.pdf");
final OutputStream outStream = response.getOutputStream();
JasperExportManager.exportReportToPdfStream(jasperPrint,outStream);
outStream.flush();
outStream.close();
I have to call this from angular using POST request. How to acheive so?
EDIT
I was able to meet the requirements by referring this thread.
please see this very helpful function to download a file using POST request. Function is dependant on jQuery . the implementation creates a html form inline with hidden field and then submit it

Spring MVC and multipart handling

I am using Spring MVC 4, and I have a controller with the below mapping/method:
#RequestMapping(value = "/me/bio", method = RequestMethod.POST, consumes = { "multipart/form-data" })
#ResponseBody
public JsonResponse<Boolean> saveProfileBio1(Account account, #RequestPart("file") MultipartFile file, #RequestPart("profile") #Valid ProfileBio profileBio) throws ValidationException, IOException {
...//code here
}
When I submit a multipart form data request it fails with HTTP 400 Bad request with the error " org.springframework.web.multipart.support.MissingS ervletRequestPartException: Required request part 'profile' is not present"
Below is the raw request:
------WebKitFormBoundarynU961NKt3K534rCg
Content-Disposition: form-data; name="profile"
{"profileName":"Zack Smith","profileDescription":"xxx","profileWebLink" :"www.abc","profilePictureUrl":"https://s3.amazonaws.com/xxx-images/default.png","profileTitle":"CTO1"}
------WebKitFormBoundarynU961NKt3K534rCg
Content-Disposition: form-data; name="file"; filename="2013-11-16 21.19.59.jpg"
Content-Type: image/jpeg
As you can see the request clearly has the "profile" part. From my debugging, the issue is that the "profile" request part does not have the "Content-type" set, and DefaultMultipartHttpServletRequest has the below method that requires it to be set and if it returns null the entire request fails with the above error.
#Override
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
String contentType = getMultipartContentType(paramOrFileName);
if (contentType != null) {
HttpHeaders headers = new HttpHeaders();
headers.add(CONTENT_TYPE, contentType);
return headers;
}
else {
return null;
}
}
Trouble is is that I can't seem to find a way to set the content-type on a FormData submit in the browser for each part and seems to be something I can't set, and Spring seems to require it.
Any tips on how to fix this or if this is a bug?
Thanks
I see two options to solve the issue:
On the client: Add the JSON as Blob to FormData, as mentioned here. Background: Blob allows setting the content type (example with angular js):
var formData = new FormData();
formData.append('profile', new Blob([angular.toJson(profile)], {
type: "application/json"}
));
Alternativly on the server (not recommended): overwrite the getMultipartHeaders method of DefaultMultipartHttpServletRequest and configure this in spring. If you are using CommonsMultipartResolver you need to overwrite it as well (due to missing dependency injection point):
new DefaultMultipartHttpServletRequest() {
#Override
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
// your code here
}
}
I was just battling this issue and my solution was to stop using #RequestPart and use #RequestParam instead. If I'm understanding the doc for #RequestPart correctly, it only works out of the box for a few types (such as MultipartFile) but others require an HttpMessageConverter. Also make sure you have a MultipartResolver bean declared. Recommend that it return a CommonsMultipartResolver.

Resources