Upload file with JSON data in Angular5 and Spring Boot - spring-boot

I am stuck in getting the request at my REST Controller.
Below is my Controller method :
#RestController
#CrossOrigin(origins = "http://localhost:4200", allowedHeaders = "*")
public class UserController {
#Autowired
public UserService userService;
#PostMapping(value = "/fileUploadApi", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public #ResponseBody StatusUpload fileUpload(#RequestPart("uploadfile") MultipartFile file, #RequestPart("user") User userDto) throws Exception{
StatusUpload status= new StatusUpload();
status = userService.callUserServiceForFileRead(file, userDto);
return status;
}
In ApplicationConfig.java, I added the following :
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(500000000);
return multipartResolver;
}
Below is the code I have written for file upload at Angular5 end:
HTML:
<!-- Other form elements -->
<!-- Used PrimeNG Custom Upload Handler to get the File Object https://www.primefaces.org/primeng/#/fileupload -->
<label>Upload template </label>
<p-fileUpload (uploadHandler)="getFile($event)" auto="true" customUpload="true" accept=".xlsx,application/msexcel" previewWidth="0" [showUploadButton]="false" [showCancelButton]="false">
</p-fileUpload>
<!-- Form Submit Button -->
<button type="button" (click)="submit()">SUBMIT</button>
COMPONENT :
user: User = new User;
// Set other form element data in user object.
uploadFile: File;
getFile(event) {
this.uploadfile= event.files[0];
}
submit() {
this.userService.saveUserData(this.user,this.uploadFile);
}
SERVICE :
options = {
headers: new HttpHeaders({ 'Content-Type': '' })
};
constructor(private http: HttpClient) { }
saveUserData(user:User, uploadFile:File){
var formData: FormData = new FormData();
formData.append('user',JSON.stringify(user));
formData.append('uploadfile',uploadFile);
return this.http.post<StatusUpload>(baseUrl + "/fileUploadApi", formData, this.options);
}
When I use the above Service, it gives me no response/no Exception don't know why. The request even didn't reach my RESTController.
I read a different approach in some post & used it as below :
saveUserData(user: User, uploadFile: File) {
var formData: FormData = new FormData();
Observable.fromPromise(new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
formData.append('user', new Blob([JSON.stringify(user)],
{
type: "application/json"
}));
formData.append('uploadfile', uploadFile);
xhr.open("POST", baseUrl + "/fileUploadApi", true);
xhr.send(formData);
}));
}
With above, I get org.springframework.web.multipart.support.MissingServletRequestPartException.
Can anyone please help to get this code working. I need to use the http.post() in my Angular service, not the XMLHttpRequest.send() one.

You need to subscribe to the observable.
An HttpClient method does not begin its HTTP request until you call subscribe() on the observable returned by that method. This is true for all HttpClient methods.
Docs

Related

Sending file using multipart/form-data and SpringBoot on backend

I'm trying to send file from vue.js using axios and receive it in spring-based backend.
Here's my frontend part:
uploadMap(context, map){
console.log('uploading map...')
const formData = new FormData();
formData.append("file", map);
axios.post("/info/map/upload", formData)
.then(function (result) {
console.log(result);
}, function (error) {
console.log(error);
});
},
and on backend:
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/info/uploadFile")
public void uploadFile(#RequestParam("file") MultipartFile file) throws IOException {
System.out.println("file uploaded");
String basePath = "/Users/admin/software/app1/uploads/";
String filePath = basePath + file.getOriginalFilename();
File dest = new File(filePath);
file.transferTo(dest);
}
Backend part works when using Insomnia/Postman for testing. But when I want to invoke my frontend code, I get on backned:
2020-02-23 17:38:52.038 WARN 61799 --- [nio-5000-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=----WebKitFormBoundary2cAmVUpGnPfkQax3' not supported]
How should I deal with that?
Thanks in advance 🤓
Add dependency to your Maven config:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
Then register new bean with special name:
#Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}

MethodArgumentTypeMismatchException in Spring

I have this method to retrieve data from my database :
#GetMapping(path="/login/in", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User loginA(#RequestBody LoginCredential newLogin)
{
logger.debug(newLogin);
return repository.findByEmailAddress(newLogin.getEMail()).get(0).getUser();
}
And I'm trying to use this method like this :
var request = new XMLHttpRequest();
let url='http://localhost:8080/login/in';
let data=JSON.stringify({ email:this.state.email,passwordHash:this.state.passwordHash});
request.open('GET', url, true);
request.setRequestHeader("Content-Type", "application/json");
request.send(data);
It gives me error - 400
And Spring says :
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: com.mua.cse616.Model.User com.mua.cse616.Controller.LoginCredentialController.loginA(com.mua.cse616.Model.LoginCredential)]
How to resolve this?
I think the main problem is that you're using #GetMapping and sending body #RequestBody LoginCredential newLogin at the same time. You should user #RequestBody with #PostMapping or #PutMapping but not #Getmapping.
So, try to change your request to POST. That would solve the exception.
#PostMapping(path="/login/in", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User loginA(#RequestBody LoginCredential newLogin)
{
logger.debug(newLogin);
return repository.findByEmailAddress(newLogin.getEMail()).get(0).getUser();
}
var request = new XMLHttpRequest();
let url='http://localhost:8080/login/in';
let data=JSON.stringify({ email:this.state.email,passwordHash:this.state.passwordHash});
request.open('POST', url, true);
request.setRequestHeader("Content-Type", "application/json");
request.send(data);

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

Sending file to Spring Boot REST using Axios

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") {
}

Error handling on controller SpringMVC

I am developing an application in jax-rs and spring mvc.
I want to notify my client each time when an default error is occured like
400, 403, 404, 405 and 415.
Controller
#Controller
#RequestMapping("/customer")
public class CustomerController {
#Autowired
CustomerService customerService;
// ........xxxxxx..............xxxxxxx................xxxxxxx.............//
#CrossOrigin
#RequestMapping(value = "/",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String fetchCustomer() throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(customerService.fetchAllCustomer());
}
// ........xxxxxx..............xxxxxxx................xxxxxxx.............//
}
Client
$http({
method: "GET",
contentType: "application/json",
url: baseUrl + '/customer'
}).success(function (response) {
console.log(response);
// you can also use
console.log(JSON.stringify(response);
}).error(function (response) {
console.log(response);
});
When i request a service from client i want to send response back with status code and custom message.
Example
When i defind method = post on controller and from client i send request as get service should return message like
error:{
Status Code: 405,
Message: Invalid Method
url: error/405
}
Check this out for reference.
Define a method for handling the specific error scenario and annotate it as #ExceptionHandler. The exception in your scenario (request method not supported) is HttpRequestMethodNotSupportedException.class. You can create more generic handler methods using Throwable, Exception etc.
In order to prevent duplication of error handling across controllers, one convenient way is to define all handlers in single class and use #ControllerAdvice on that. This way, all handlers will be applied to all controllers.
Do not return a String but return a org.springframework.http.ResponseEntity.
You can add status codes to this object
ResponseEntity<String> responseEntity = new ResponseEntity<String>("This is a response", HttpStatus.INTERNAL_SERVER_ERROR);
return responseEntity;
So your method signature will also change as below
#RequestMapping(value = "/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ResponseEntity<String> fetchCustomer() throws JsonProcessingException {
try {
String str = new ObjectMapper().writeValueAsString(customerService.fetchAllCustomer());
return new ResponseEntity<String>(str, HttpStatus.OK);
}
catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
If there is an error, you can either use controller advice or catch the exception and update the ResponseEntity appropriately

Resources