Uploading image to database using AJAX is not uploading as blob - java

I have a webpage that is supposed to upload an image to the database with a name to describe the image. Think uploading a logo and the name of the companies logo.
When I select the image file and submit it uploads to the database and I can return that information to the webpage in a list. However, it is not encoded in the manner that I was expecting. I would like the image file to be uploaded as a blob so that I may convert the blob to Base64 and pass it to my web application.
This is what the blob code looks like if I manually upload the images using MySQLs gui.
"iVBORw0KGgoAAAANSUhEUgAACWAAAAnHCAYAAAAIV..." which I'm able to convert to Base64 later.
When I use my ajax web page to upload an image however, I receive
"QzpcZmFrZXBhdGhcU3ByaW5nLnBuZw==".
My question is, how can I have ajax upload it as a blob instead so that my Java application can properly call the blob and convert it to Base64?
ajax.js
$(function (){
var $skills = $('#skills');
var $logo = $('#logo');
var $techName = $('#techName');
$.ajax({
type: 'GET',
url: '/api/technologyList',
success: function(skills) {
$.each(skills, function(i, skill) {
$('#skills-list').append('<tr><td> ' + skill.logo + '</td>' + '<td>' + skill.techName + '</td></tr>')
})
}
})
$('#addSkill').on('click', function () {
var skill = {
techName: $techName.val(),
logo: $logo.val()
}
$.ajax({
type: 'POST',
url:'/api/technologyList',
data: skill,
contentType: "multipart/form-data",
processData: false,
success: function (newSkill) {
$('#skills-list').append('<tr><td> '+ newSkill.logo+ '</td>' +
'<td> '+ newSkill.techName + '</td></tr>')
console.log(skill)
}
})
})
})
addSkill.html
<table id="skills-list">
<tr>
<th>Logo</th>
<th>Technology</th>
</tr>
</table>
<form id="skillForm">
<input type="text" id="techName"/> <br>
<input type="file" enctype="multipart/form-data" id="logo"/>
<button id="addSkill">Add!</button>
</form>
HomeController
#GetMapping(value = "/technology")
public String technologyList(Model theModel) throws IOException {
try {
List<Skills> userSkillsList = skillsService.findSkillList("wmangram");
List<byte[]> logo = skillsService.findLogos();
List<String> base64List = new ArrayList<>();
boolean isBase64 = false;
for (int i = 0; i < logo.size(); i++) {
if (Base64.isBase64(logo.get(i))) {
String base64Encoded = new String((logo.get(i)), "UTF-8");
base64List.add(base64Encoded);
}
else {
byte[] encodeBase64 = Base64.encodeBase64(logo.get(i));
String base64Encoded = new String(encodeBase64, "UTF-8");
base64List.add(base64Encoded);
}
}
theModel.addAttribute("userSkills", userSkillsList);
theModel.addAttribute("userImages", base64List);
return "technology";
}
catch (NullPointerException nexc) {
return "nullexception";
}
}

You have to use a FormData object to upload multipart/form-data1 via ajax.
$('#addSkill').on('click', function () {
var skill = new FormData();
skill.append("techName", $techName.val());
skill.append("logo", $logo.prop("files")[0]);
$.ajax({
type: 'POST',
url:'/api/technologyList',
data: skill,
contentType: false, //don't set this, it will be set automatically and properly
processData: false,
success: function (newSkill) {
$('#skills-list').append('<tr><td> '+ newSkill.logo+ '</td>' +
'<td> '+ newSkill.techName + '</td></tr>')
console.log(skill)
}
})
})
Looking at the java code it doesn't look like it can handle a file upload, so this answer is only for the client side code.
This isn't strictly true but you wouldn't want to have to do it any other way.

The problem was that I wasn't handling the file in a manner that let the program read the files contents. Instead it was just receiving the fake file path with the file name.
Fixed by utilizing #RequestParam and MultipartFile then assigning to the object before passing to the DAO.
RESTController.java
#PostMapping("/technologyList")
public String uploadMultipartFile(#RequestParam("logo") MultipartFile file, #RequestParam("techName")String techName) {
User user = userService.findByUsername("wmangram");
try {
// save file to MySQL
Skills newSkill = new Skills(techName, file.getBytes(), user);
skillsService.createTechnology(newSkill);
return "File uploaded successfully! -> filename = " + file.getOriginalFilename();
} catch (Exception e) {
return "FAIL! Maybe You had uploaded the file before or the file's size > 500KB";
}
}

Related

Upload images from Angular to SpringBoot

I was trying for hours to send images from Angular to SpringBoot. Now I'm getting this error:
org.springframework.web.multipart.MultipartException: Current request is not a multipart request
Frontend(Angular) code looks like this:
saveProduct(productSave: ProductSave, mainImg: File, imageList: File[]): Observable<any>
{
const formData = new FormData();
const formListData = new FormData();
formData.append('mainImg', mainImg, mainImg.name);
imageList.forEach( img => formListData.append('imageList', img));
return this.httpClient.post<ProductSave>(this.saveUrl, {productSave, mainImg, imageList});
}
mainImg and imageList are images uploaded from user, and initialized like so:
mainImg = event.target.files[0];
imageList.push(event.target.files[0]);
My backend (SpringBoot) code looks like this:
#PostMapping("/save")
public void saveProduct(#RequestBody ProductSave productSave, #RequestParam("mainImg")MultipartFile main, #RequestParam("imageList")MultipartFile[] multipartFiles)
{
System.out.println(productSave.getProduct().getName());
}
I really don't have idea how to send those images, I was trying to look around stack but I faild.
Thanks for any help!
The problem is in the Spring Boot Controller method Arguments.
In multipart request, you can send the JSON Request body. Instead, you will have to send, key-value pairs.
So, you have 2 ways to do what you want to do -
Send JSON String and then deserialize it.
Spring Boot API Sample :-
#PostMapping("/save")
public String create(#RequestPart("file")MultipartFile multipartFile, #RequestPart("files") List<MultipartFile> multipartFiles, #RequestPart("jsonString") String jsonString) {
/** To convert string to POJO
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
ProductSave productSave = this.objectMapper.readValue(jsonString,ProductSave.class); **/
return jsonString;
}
HTML/Javascript Sample: -
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<input type="file" id="file">
<button onclick="submitData()">Submit Data</button>
<script type="text/javascript">
function submitData() {
const formData = new FormData();
const fileField = document.querySelector('input[id="file"]');
formData.append('jsonString', JSON.stringify({document: {data: 'value'}}));
formData.append('file', fileField.files[0]);
Array.from(fileField.files).forEach(f => formData.append('files', f));
fetch('http://localhost:8080/save', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
</body>
And in front end:-
JSON.stringify(product);
Send Files as Byte Arrays, You don't need to use form data in frontend in this case.
You can convert file object to byte arrays in frontend using:-
const fileToByteArray = async (file) => {
return new Promise((resolve, reject) => {
try {
let reader = new FileReader();
let fileByteArray = [];
reader.readAsArrayBuffer(file);
reader.onloadend = (evt) => {
if (evt.target.readyState === FileReader.DONE) {
const arrayBuffer = evt.target.result;
const array = new Uint8Array(arrayBuffer);
array.forEach((item) => fileByteArray.push(item));
}
resolve(fileByteArray);
};
} catch (e) {
reject(e);
}
});
};
in the application.properties try setting up the following -
spring.servlet.multipart.max-file-size=1024KB
spring.servlet.multipart.max-request-size=1024KB
spring.http.multipart.enabled=false
Play around with the max-file-size.

Spring MVC output CSV to highcharts

I am trying to populate highcharts chart using a csv file that I will generate in java back end and send to the from end using spring mvc.
First my controller class which I am almost positive is the issue but I don't know how to correctly send the csv file:
#Controller
public class ChartController {
#RequestMapping(value = "/index", method = RequestMethod.GET)
public String indexHandler() {
return "index";
}
#RequestMapping(value = "/out", method = GET)
public String chartHandler() {
String fileName = "test.csv" //note: I have also tried moving this
//file to my WEB-INF location and it doesn't make a difference
InputParser input = new InputParser();
for (GenericDataObject gdo : input.getDataObjects()) {
CSVOutput.writeCSV(fileName,gdo);
}
return "index";
}
}
The csv file is successfully created as I intend so that's not an issue
here is my Java script for highcharts
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/data.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<some more high charts for font and color that I will leave out because its not currently being used>
</head>
<body>
<div id='container' style="width: 100%; height: 600px;"></div>
<script type="text/javascript">
$(document)ready.function() {
var groupId = [];
var date = [];
var val = [];
var options = {
chart: {
renderTo: 'container',
type: 'line'
},
title: {
text: 'test'
},
xAxis: {
title: {
text: 'group and date'
},
categories: [groupId, date]
},
yAxis: {
title: {
text: 'data'
}
},
series: [{
data: val
}]
};
$.get('http://localhost:8080/web-data-app/out', function(data)) {
alert("success");
var lines = data.split('\n')
$.each(lines, function(lineNo, line) {
var items = line.split(',');
groupId.push(items[1]);
date.push(items[2]);
val.push(items[4]);
});
var cahrt = new Highcharts.Chart(options);
});
});
</script>
</body>
</html>
As of right now I get the outline of highcharts in my container, as well as the success alert so i know that much is working. however no data is being displaying withing the chart.
You can use httpResponse and send your content there. Like this
#RequestMapping(value = "/out", method = GET)
public void chartHandler(HttpServletResponse httpResponse) {
String fileName = "test.csv" //note: I have also tried moving this
//file to my WEB-INF location and it doesn't make a difference
InputParser input = new InputParser();
for (GenericDataObject gdo : input.getDataObjects()) {
CSVOutput.writeCSV(fileName,gdo);
}
httpResponse.setContentType("text/csv");
//you can use the output stream below to pass your content
httpResponse.getOutputStream()
}

Downloading file through Ajax post request

I'm trying to download a file through Ajax post request. But I'm not able to do this, I tried a lot but I am not getting. The bellow is the code, please guide me how to do this.
Query method
$(document).on({click: function() {
var elementId = $(this).attr('id').split("_");
var air = $('#itineraryAir_${formBean.tripDetailsId}').is(':checked');
var car = $('#itineraryCar_${formBean.tripDetailsId}').is(':checked');
var hotel = $('#itineraryHotel_${formBean.tripDetailsId}').is(':checked');
if(air == true || car == true || hotel == true){
$('#itinerary_modal').modal( 'hide');
$.ajax({
url: "<%=request.getContextPath()%>/viewBooking/downloadItinerary/" +air+"/"+car+"/"+hotel+"/"+elementId[2],
type: 'POST',
dataType: 'text/json',
beforeSend: function() {
},
success: function(data) {
},
complete: function() {
}
}, '.edit_hotel');
}else{
var result="Select itinerary."
showMsg($("#ItineraryMsg"),result,"alert-danger");
}
}
}, '.it_download');
In the above code, I'm getting Element-Id, air, car and hotel details and passing all these value through URL. In spring controller class dynamically I am generating PDF file and the file is generating, But I am not able to display for user to download it.
Server side code
//the below line of code to generate pdf file. It is generating pdf file but I am not able to pass to client.
public #ResponseBody void downloadInPDF(#PathVariable("air") Boolean air,
#PathVariable("car") Boolean car,
#PathVariable("hotel") Boolean hotel,
#PathVariable("tripDetailsId") String tripDetailsId,
HttpServletResponse response,HttpServletRequest request) throws IOException {
TripDetails td = tripDetailsService.get(Integer.parseInt(tripDetailsId));
Users user=userService.getByUserIdNew(String.valueOf(td.getUser().getUserId()));
Employee employee=employeeService.getEmployeeByEmployeeId(user.getEmployee().getEmployeeId());
if(hotel == true){
List<TripHotels> tripHotels = hotelService
.getHotelDetailsByTripId(td);
generatePdf.getPdfFileHotel(AppConstant.ETICKET_PATH, tripHotels, td,employee);
String filePathToBeServed = AppConstant.ETICKET_PATH + td.getTripId()
+ "_Hotel.pdf";
File fileToDownload = new File(filePathToBeServed);
InputStream inputStream = new FileInputStream(fileToDownload);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename="+td.getTripId() + "_Hotel.pdf");
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
}
}

Stream videos in client app using Ajax and java Restlet

Hi I want to stream videos in client app but videos are located in server app. I am using java Restlet and Jquery Ajax to connect client app to server app. Through Ajax call i am connecting to Restlet. I don't know how to send response to ajax after streaming video from server side, how ajax receives response and how to play video in browser. Can any one help me to handle this.
Here is my code
Html:
<button id="playVideo" class="btn-primary">PlayVideo</button>
<video id="videoTab" height="300" width="500" style="display: none" controls ></video>
Ajax Call to server
$('#playVideo').click(function (){
var jsonObj = {};
jsonObj.userId = "siva";
jsonObj.file = "sample.mp4";
//console.log("json obje :"+ JSON.stringify(jsonObj))
// Rest call to play videos.
$.ajax({
type : 'GET',
url : config.streamVideo,
//dataType : 'json',
data : JSON.stringify(jsonObj),
contentType : "application/json",
mimeType : "video/mp4",
processData : false,
crossDomain : true,
success : function(result) {
//console.log("login result : " + JSON.stringify(result));
if (result) {
console.log("success.....");
srcPath = "data:video/mp4;"+result;
$('#videoTab').attr('src', srcPath);
$('#videoTab').css('display', 'block');
$('#videoTab').attr('autoplay', true);
} else {
alert('failed...');
}
},
error : function(){
alert('error')
}
});
});
RestletCode:
#Get
public InputRepresentation handleRequest(Representation entity) throws IOException, ResourceException {
// Set response headers
Series<Header> responseHeaders = (Series<Header>) getResponse().getAttributes().get("org.restlet.http.headers");
if (responseHeaders == null) {
responseHeaders = new Series<Header>(Header.class);
getResponse().getAttributes().put("org.restlet.http.headers", responseHeaders);
}
responseHeaders.add(new Header("Access-Control-Allow-Origin", "*"));
logger.debug("+++++++++++++++++++Entered in play video restlet +++++++++++++++");
// Convert Rest type request to Servlet request
httpServletRequest = ServletUtils.getRequest(getRequest());
// Get Servlet context object.
sc = httpServletRequest.getServletContext();
// Get input file path.
logger.debug("------->getRealPath " + sc.getRealPath("/"));
String filePath = sc.getRealPath("/") + "WEB-INF\\data\\videos\\sample.mp4";
final File file = new File(filePath);
if (file.exists()) {
logger.debug("Requested file path : " + file.getAbsolutePath());
logger.debug("inputRepresentation :" + inputRepresentation);
fis = new FileInputStream(file);
inputRepresentation = new InputRepresentation(new InputStream() {
private boolean waited = false;
#Override
public int read() throws IOException {
waited = false;
// read the next byte of the FileInputStream, when reaching the
// end of the file, wait for 2 seconds and try again, in case
// the file was not completely created yet
while (true) {
byte[] b = new byte[1];
if (fis.read(b, 0, 1) > 0) {
return b[0] + 256;
} else {
if (waited) {
return -1;
} else {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
logger.error("Exception while streaming video : ", ex);
}
waited = true;
}
}
}
}
}, MediaType.VIDEO_MP4);
} else {
logger.debug("Requested file not found : " + filePath);
}
//logger.debug("inputRepresentation :");
return inputRepresentation;
}
Thanks in advance
After reading your comment, here is my understanding of what you should do.
I would not send json to a resource in order to get something, I would just send a simple GET request.
You need:
a resource that returns the file of a video according to its identifier. For the matter of illustration, let's say its url template is /videos/{videoid}
a web page that contains the links, and the empty video player
some javascript that set the "src" attribute video player with the url defined above: /videos/{videoid}. The way you compute the videoid is your own business.
Here is the server code:
the Restlet application, that defines the URI templates
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
// attaches the resource that represents a video, according to its identifier
router.attach("/videos/{videoid}", VideoServerResource.class);
// ... other instructions
return router;
}
the video server resource:
public class VideoServerResource extends ServerResource {
private File video;
#Override
protected void doInit() throws ResourceException {
String videoId = getAttribute("videoid");
// Compute path
String path = "/tmp/" + videoId + ".mp4";
video = new File(path);
// takes care of not found status responses.
setExisting(video.isFile());
}
#Get("mp4")
public File represent() {
return video;
}
}
Here is the client code. This is a sample Web page, with an empty video player. When clicking on the button, the video player is asked to play the http://example.com:9000/videos/testvideo video. In your case, the value testvideo is simply deduced from the link the user click on.
<!DOCTYPE html>
<html>
<head>
<script src="/static/jquery.js"></script>
<script>
$('#playVideo').click(function (){
srcPath = "http://127.0.0.1:9000/videos/testvideo";
$('#videoTab').attr('src', srcPath);
$('#videoTab').css('display', 'block');
$('#videoTab').attr('autoplay', true);
});
</script>
</head>
<body>
<button id="playVideo" class="btn-primary">PlayVideo</button>
<video id="videoTab" height="300" width="500" style="display: none" controls ></video>
</body>
</html>
I hope this will help you.

JavaScript - how to display error message from backend system based on Spring MVC

I have a web application with HTML / jQuery which ic connected with AJAX / JSON to a backend system with Java EE / Spring MVC.
In the frontend, a Person can be created by fill in the form fields and then it is submitted and this jQuery code executed:
var person = $(this).serializeObject();
$.postJSON("add/", person, function(data) {
alert("Person with ID "+data.person.id+"' added successfully");
});
In the best case, the Person is created and I'll get a Person object and I can access the values with data.person.*.
Now I want to validate the data which is sent to the backend system and in a case of an error, I want to display in the first step an alert error message.
I did this in the backend system:
#RequestMapping(value="add/", method=RequestMethod.POST)
public #ResponseBody Map<String, ? extends Object> addPerson(#RequestBody Person p, HttpServletResponse response) {
Set<ConstraintViolation<Person>> failures = validator.validate(p);
if (!failures.isEmpty()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return validationMessages(failures);
} else {
Person person = this.personService.addPerson(p);
return Collections.singletonMap("person", new SerialPerson(person.getId(), person.getName(), ...));
}
}
// internal helpers
private Map<String, String> validationMessages(Set<ConstraintViolation<Person>> failures) {
Map<String, String> failureMessages = new HashMap<String, String>();
for (ConstraintViolation<Person> failure : failures) {
failureMessages.put(failure.getPropertyPath().toString(), failure.getMessage());
System.out.println(failure.getPropertyPath().toString()+" - "+failure.getMessage());
}
return failureMessages;
}
My Person object is annotated, and I get the System.out.println(failure.getPropertyPath().toString()+" - "+failure.getMessage()); on the console, that for example, "name - must be between 1-30 chars"
But how can create an alert message in jQuery in the frontend system?
Thank you in advance for your help & Best Regards.
Update: Link to the Spring MVC AJAX example, where I found the validationMessages method. But there is also no solution how to get the error message.
SOLUTION:
I have to call:
jQuery.ajax({
'type': 'POST',
'url': "add/",
'contentType': 'application/json',
'data': JSON.stringify(person),
'dataType': 'json',
'success': function(data) {alert("success");},
'error': function(xhr) {alert(xhr.responseText);}
});
You can do something like this:
var person = $(this).serializeObject();
$.postJSON("add/", person, function(data) {
if(data.person) {
alert("Person with ID "+data.person.id+"' added successfully");
}
else {
var errors = "";
for(var key in data) if(data.hasOwnProperty(key)) {
errors += data[key] + "\n";
}
alert(errors);
}
});
You shouldn't need to send back a bad request either. Is this what you want?
UPDATE
You can use the code shown in Spring Source, but you'd have to use jQuery.ajax
jQuery.ajax({
type: 'POST',
url: "add/",
data: person,
dataType: "json",
success: function(data) {
alert("Person with ID "+data.person.id+"' added successfully");
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
var errorJSON = JSON.parse(XMLHttpRequest.responseText); //if this is JSON otherwise just alerting XMLHttpRequest.responseText will do
var errors = "";
for(var key in errorJSON) if(errorJSON.hasOwnProperty(key)) {
errors += errorJSON[key] + "\n";
}
alert(errors);
}
});

Categories