Upload multiple files each with additional information in spring - javascript

I'm new to spring boot and js and i'm trying to upload multiple files each having additionl information like description etc.
Objects :
public class BookDto {
private String title;
private String author;
private List<PageDto> pageDtoList;
// getters setters
}
public class PageDto {
private String description;
private MultipartFile file;
// getters setters
}
Controller :
public class UploadController{
#PostMapping("/upload")
public ResponseEntity createGenre(#RequestBody BookDto bookDto){
// do something with data
}
}
Html :
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" multiple onclick="postRequest(event)">
<input type="submit" value="Upload" name="submit">
</form>
JS :
function postRequest(event){
event.preventDefault();
var files = [].slice.call(event.target.files);
var pageList = [];
for (var i=0; i<files.length; i++) {
pageList.push(new Page( i + "some description",files[i]));
}
var newBook = new Book();
newbook.pageList = pageList;
book.author = "author1";
book.title = "title1";
$.ajax({
type: "POST",
data: // i don't really know what to put here,
url: "/upload",
success: function (response) {
// success
},
error: function (result) {
// error
}
});
}
function Book(title, author, chapter, pageList){
this.title = title;
this.author = author;
this.pageList = pageList;
}
function Page(description, file) {
this.description = description;
this.file = file;
}
I would like to know if it is possible to upload files as described by the objects or do i have to upload them seperately.

In order to create a book instance from a request, considering you have a route such as :
#PostMapping("/createBook")
public ResponseEntity createBook(#RequestBody BookDto bookDto){
// do something with data
}
you can from client proceed to following :
const book = {
author: "George Orwell",
title: "1984",
pageDtoList: []
};
$.ajax({
type: "POST",
data: book,
url: "/createBook",
success: function (response) {
// success
},
error: function (result) {
// error
}
});
You can then add pages with same logic, but with a file set to null temporary, and upload file after, given a page id.
#PostMapping("/addPageToBook")
public ResponseEntity addPage(#RequestParam("bookid") String bookId,
#RequestBody PageDto pageDto){
// add a page instance to your book
}
And after you can set page content :
#PostMapping("/setPageContent")
public ResponseEntity setPage(#RequestParam("bookid") String bookId,
#RequestParam("pageid") String pageId,
#RequestParam("file") MultipartFile content){
// set content to page, ensure book dto is updated with last page instance
}
You will need to use https://developer.mozilla.org/en-US/docs/Web/API/FormData for AJAX uploading, but maybe (depending on your use case) a simple file input button + submit can be enough.

Related

Make Ajax call to C# Controller Method

This is the code in my javascript file called "data handling.js" located in a folder called "JS":
document.getElementById('submit-new-project').addEventListener("click", function () {
var ProjectName = document.getElementById('new-project').value;
if (ProjectName) {
$.ajax({
type: "POST",
url: "Controllers/HomeController/AddProject",
data: {"ProjectName": ProjectName},
success: function () {
hideElement("add-button-popup");
location.reload();
},
error: function (errorThrown) {
console.log(errorThrown)
}
});
}
else {
alert("Text Field Must Not Be Empty");
}
})
And here is my controller method:
public partial class HomeController : Controller
{
[HttpPost]
public static void AddProject(string ProjectName)
{
using (AutomationMethodDirectoryEntities db = new AutomationMethodDirectoryEntities())
{
try
{
PROJECT project = new PROJECT();
project.ProjectName = ProjectName;
db.PROJECTS.Add(project);
db.SaveChanges();;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
The controller method is located in "Adders.cs" which is a partial class to "HomeController.cs" in the "Controllers" folder. Both the "JS" and "Controllers" folders are at the root level of the project. I have tried many variations of this ajax call and my biggest problem is with the URL. No matter what I try, I am always getting a 404 error because the server can't find the requested URL.
I've tried changing it to:
url: "Controllers/HomeController/AddProject"
url: "/Controllers/HomeController/AddProject"
url: "../Controllers/HomeController/AddProject"
url: "Home/AddProject"
url: "/Home/AddProject"
url: "../Home/AddProject"
url: "#(Url.Action(AddProject, HomeController))"
And I've swapped "HomeController" with "Adders" in different variations.
What am I doing wrong?
Is there anything I need to add/change to my controller method?
EDIT
Here is my "RouteConfig.cs" that I haven't made any changes to yet:
namespace RTCAD
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
When calling your AddProject action, ASP.NET MVC creates an instance of the controller (using ControllerFactory), and since your method is static, it is not related to any instance, and thus, it will not be called by MVC.
Remove the static from the method declaration and use /Home/AddProject as the URL.

How to make requestbody controller works?

So actually everything is working well, except the fact that i can't get access to "inscrit" which is supposed to be the one which is sent to have the information user
This is my function in javascript and it does go into success for ajax.
function fbLogin() {
FB.login(function (response) {
if (response.authResponse) {
var inscrit = {
"prenom": response.first_name,
"nom": response.last_name,
"email": response.email
}
$.ajax({
method: 'post',
url: '/hello/facebookconnection',
contentType : 'application/json; charset=utf-8',
data: JSON.stringify(inscrit),
datatype: "json",
success: function() {
window.alert('User data sent');}
,
error: function(){
window.alert('Error in sending ajax data');}
});
} else {
document.getElementById('status').innerHTML = 'User cancelled login or did not fully authorize.';
}
}, {scope: 'email'});
}
And this is my java controller (whatever i do, the "inscrit.getEmail()" is always null)
#RequestMapping(value = "/facebookconnection", method = RequestMethod.POST)
#ResponseBody public void createUser(#RequestBody inscrit inscrit, ModelMap model) throws Exception {
//System.out.println(inscrit.getNom());
//System.out.println(inscrit.getPrenom());
System.out.println(inscrit.getEmail());
try {
user.adduser(inscrit);
} catch (Exception e) {
e.printStackTrace();
}
}
This is my inscrit class with all the getter and setter correctly set (i know that there's no problem with it)
#Entity
#Table(name="inscrit")
public class inscrit implements Serializable {
#Id
#Column(name="email")
private String email;
#Column(name="nom")
private String nom;
#Column(name="prenom")
private String prenom;
}
So i would like to know if someone could help me understand why it's not working in my controller (how to do so the data is sent properly from the ajax to the controller) :(
Thx !
EDIT : i did write the answer of that question below. The problem was the fact that response.first_name was really blank... and had to pass through the function userdata() which had the real informations. So that means i don't have any real error in my code, all the informations between the controller and my jsp page works correctly. Be free to use it as an example of how this works
#ResponseBody public void createUser(#RequestParam(value="inscrit") String inscrit) throws Exception {
//System.out.println(inscrit.getNom());
//System.out.println(inscrit.getPrenom());
Inscrit ins = JSON.parseObject(inscrit, Inscrit.class);
System.out.println(ins.getEmail());
try {
user.adduser(ins);
} catch (Exception e) {
e.printStackTrace();
} }
The class of inscrit.java is nonstandard.You should change it to Inscrit.java.
I hope this helps.
I'll answer to my own question since i found the answer, So this is for people who tries to have a facebook api and want to get informations of the profil to put into database.
My error was the fact that i thought response in login was the same asthe UserData(), which is not the case... So that's how i was supposed to do it.
function fbLogin() {
FB.login(function (response) {
if (response.authResponse) {
// Get and display the user profile data
getFbUserData();
} else {
document.getElementById('status').innerHTML = 'User cancelled login or did not fully authorize.';
}
}, {scope: 'email'});
}
// Fetch the user profile data from facebook
function getFbUserData(){
FB.api('/me', {locale: 'en_US', fields: 'id,first_name,last_name,email,link,gender,locale,picture'},
function (response) {
/*document.getElementById('fbLink').setAttribute("onclick","fbLogout()");
document.getElementById('fbLink').innerHTML = 'Logout from Facebook';
document.getElementById('status').innerHTML = 'Thanks for logging in, ' + response.first_name + '!';
document.getElementById('userData').innerHTML = '<div class="fbprofile"><p><b>FB ID:</b> '+response.id+'</p><p><b>Name:</b> '+response.first_name+' '+response.last_name+'</p><p><b>Email:</b> '+response.email+'</p><p><b>Gender:</b> '+response.gender+'</p><p><b>Locale:</b> '+response.locale+'</p></div><p><b>Picture:</b> <img src="'+response.picture.data.url+'"/></p>';*/
var inscrit = {
"prenom": response.first_name,
"nom": response.last_name,
"password": "1234",
"status": 0,
"email": response.email
}
$.ajax({
method: 'post',
url: '/hello/facebookconnection',
contentType : 'application/json; charset=utf-8',
data: JSON.stringify(inscrit),
datatype: "json",
success: function() {
document.location.href = "accueilmember";
},
error: function(){
window.alert('Error in sending ajax data');}
});
});
}
This is my controller class.This just logs the post request send to the server:(You may modify this if you need something more like adding a Response Body)
#RestController
public class EmailController {
#RequestMapping(value = "/facebookconnection", method = RequestMethod.POST)
public void createUser(#RequestBody inscrit inscrit) throws Exception {
System.out.println(inscrit.getNom());
System.out.println(inscrit.getPrenom());
System.out.println(inscrit.getEmail());
}
This is my inscrit.java class(I personally think naming the class would not affect but better follow standard naming Practices like Inscrit.java)
import java.io.Serializable;
public class inscrit implements Serializable {
private static final long serialVersionUID = -8445943548965154778L;
private String email;
private String prenom;
private String nom;
public inscrit() {
super();
}
public inscrit(String email,String prenom,String nom) {
this.setEmail(email);
this.setPrenom(prenom);
this.setNom(nom);
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
}
I did a post request from Advanced Rest Client and set the payload as
{
"email":"agdfae#gmail.com",
"prenom":"qewrwe",
"nom":"qwer"
}
the results were
qwer
qewrwe
agdfae#gmail.com
You can modify this to suit your own needs.Hope this helps
EDIT:
You will need more two classes in spring boot as below:
ServletInitializer.java
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(PostDataApplication.class);
}
}
PostDataApplication .java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class PostDataApplication {
public static void main(String[] args) {
SpringApplication.run(PostDataApplication.class, args);
}
}
PostDataApplication is the name of the application.
These four classes will do the trick (with pom.xml only no other xml configuration needed since this is springboot).Let me know if you have any problem.

Posting to ASP.NET WebApi server from AngularJS client

I'm trying to post strings from an AngularJS application (using $http) to a server built on ASP.NET WebApi, but I get 404 as soon as I add a parameter.
The client code is this
$scope.add = function () {
// ...cut...
$http({ method: "POST", url: url, data: { fileString: "test string" }}).then(
function successCallback(response) {
$log.info(response.data);
}
);
}
The server code is
[HttpPost]
public IHttpActionResult UploadExcel(string fileString) {
// cut
}
I get a 404, but if I remove the parameter on server side it works, so i can use a server side code like this
[HttpPost]
public IHttpActionResult UploadExcel() {
// cut
}
What is wrong? Should I pass the data in a different way? I tried different combination but I can't get it work.
What you want to do is send a string, not a JSON object as you are doing right now with { fileString: "test string" }. When I want to send a string, what I normally do is that I send data from Angular like this:
$http.post("/Search/QuickSearch?searchQuery="+ searchString);
And my controller I make ready to receive a string like this:
[HttpPost]
public IHttpActionResult QuickSearch(string searchQuery)
{
// cut
}
If I want to send a JSON object, I tell my controller what it should expect, like this:
[HttpPost]
public IHttpActionResult SaveActivity(ActivityEditForm form);
{
// cut
}
public class ActivityEditForm
{
public int? Id { get; set; }
[Required]
public string Title { get; set; }
public string Description { get; set; }
}
And then send my JSON from Angular like this:
$http.post("/Activity/SaveActivity", { form: activity });
I suggest you should capture the request send by Angular. By default, Angular send parameters in a json string in request body.
I'm not sure wether Asp.net can parse them from json string in body.
So, you can try to add the below codes (also need jQuery)
angular.module('yourApp').config(function ($httpProvider) {
$httpProvider.defaults.transformRequest = function(data){
if (data === undefined) {
return data;
}
return $.param(data);
}
});
The first error is in the controller, [FromBody] should be used with the input parameter.
public IHttpActionResult UploadExcel([FromBody]string fileString)
Then the data variable on the client should be a single string, so
$http({ method: "POST", url: url, data: "test string" }).then(
Anyway I found some issue with this solution later, it seems the simplest but I suggest to avoid it.
Best solution
Thank to #Squazz answer and this SO answer I strongly suggest a change in the webapi controller, client was correct. Just introduce a class to handle a single string and adapt the input parameter
// new class with a single string
public class InputData {
public string fileString { get; set; }
}
// new controller
[HttpPost]
public IHttpActionResult UploadExcel([FromBody] InputData myInput) {
string fileString = myInput.fileString;
// cut
}
This way JSON code from the client is automatically parsed and it's easy to change the data input.
Extra tip
$scope.add angular function was correct as in the question, but here is a more complete example
$scope.testDelete = function () {
var url = "http://localhost/yourAppLink/yourControllerName/UploadExcel";
var data = ({ fileString: "yourStringHere" });
$http({ method: "POST", url: url, data: data }).then(
function successCallback(response) {
console.log("done, here is the answer: ", response.data);
}, function errorCallback(response) {
console.log("an error occurred");
}
);
}

How to send multiple parameters Ajax request with Jquery in Spring MVC Application.?

Here is my relevant codes.my out put is like this.
I need to send region and tsrId as parameters to query.
here is my code
jsp
Here is my ajax request with jquery
<script type="text/javascript">
$(document).ready(function() {
var region = document.getElementById('region').value;
var tsrId = document.getElementById('tsrId').value;
$('#tsrId').autocomplete({
serviceUrl: 'getTsrId.html',
data: ({queryData : {region:region,tsrId:tsrId}}),
//delimiter: ",",
transformResult: function(response) {
return {suggestions: $.map($.parseJSON(response), function(item) {return { value: item.name, data: item.id };
})};}});});
</script>
here is the HTML form
<td>Region</td>
<td><input type="text" name="region" id="region"><div class="autocomplete-suggestions"></div></td>
<td>TSR ID</td>
<td><input type="text" name="tsrId" id="tsrId" maxlength="8"><div class="autocomplete-suggestions2"></div></td>
here is my controller
#RequestMapping(value = "/getTsrId", method = RequestMethod.GET)
public #ResponseBody List<TSRMaster> getTsrId(#RequestParam String tagName,#RequestBody QueryData queryData) {
List<TSRMaster> tsrMasterList=new ArrayList<TSRMaster>();
tsrMasterList=gpsdao.getTsrIdList(queryData.getRegion(),queryData.getTsrId());
return tsrMasterList;
}
here is my bean class for requestMapping
public class QueryData {
private String region;
private String tsrId;
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getTsrId() {
return tsrId;
}
public void setTsrId(String tsrId) {
this.tsrId = tsrId;
}
}
Please help me to sort out this issue..is there any other alternative solution, please mention that path below
thanks.
The only way I have been able to make this work so far is to call JSON.stringify() on the client, which turns a JavaScript Object into a JSON String. (To be cross browser compatible you would need json2.js)
Then you send this as a String parameter to Spring and parse it there using the Jackson library.
Sample Code:
Java Script
data: ({queryData : JSON.stringify({region:region,tsrId:tsrId}})),
Java
RequestMapping(value = "/getTsrId", method = RequestMethod.GET)
public #ResponseBody List<TSRMaster> getTsrId(#RequestParam String tagName,#RequestParam String queryData) {
ObjectMapper myMapper = new ObjectMapper();
QueryData myQueryData = myMapper.readValue(queryData, QueryData.class);
List<TSRMaster> tsrMasterList=new ArrayList<TSRMaster>();
tsrMasterList=gpsdao.getTsrIdList(myQueryData.getRegion(),queryData.getTsrId());
return tsrMasterList;
}
You can use Jackson framework for JSON java transformation. Then you can use following method to send data to the controller from the view.
Add jackson jars to the project.
jackson-core-2.0.5
jackson-databind-2.0.5
jackson-annotation-2.0.5
Add following code to WebApplicationContext.xml
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
Ajax call
$.ajax({
url: getTsrId.html,
type: 'GET',
data: "region=" + region + "&tsrId=" + tsrId,
dataType: "json",
success: function(response){
//response here
}
});
Controller
#RequestMapping(value = "/getTsrId", method = RequestMethod.GET,produces="application/json")
public #ResponseBody List<TSRMaster> getTsrId(
#ModelAttribute(value = "queryData") QueryData queryData) {
List<TSRMaster> tsrMasterList = new ArrayList<TSRMaster>();
tsrMasterList = gpsdao.getTsrIdList(queryData.getRegion(),queryData.getTsrId());
return tsrMasterList;
}

How to update MVC model clientside with Javascript?

In a ASP.NET MVC project I'm uploading image files to web server using FineUploader jQuery plugin. File uploading part works fine, but now I need to save the uploaded image names to database with the corresponding model details (Product Details with Product Image names).
Models :
public class Product
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
...
public virtual IEnumerable<ProductImage> Images { get; set; }
}
public class ProductImage
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string FileName { get; set; }
}
This is my HTML & JavaScript code to upload the file & display uploaded file in the page when the upload is completed
...
<div id="fine-uploader"></div>
<div id="divImgs">
<ul>
</ul>
</div>
....
<script>
function createUploader() {
var uploader = new qq.FineUploader({
element: document.getElementById('fine-uploader'),
debug: true,
request: {
endpoint: '#Url.Action("UploadFile", "Upload")'
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png']
},
deleteFile: {
enabled: true,
forceConfirm: true,
endpoint: '#Url.Action("DeleteFile", "Upload")'
},
callbacks: {
onComplete: function (id, fileName, responseJSON) {
if (responseJSON.success) {
$('#divImgs ul').append(
$('<li>').append(
$('<img>').click(
function DeleteFileCall() {
$.ajax({
type: "POST",
url: '#Url.Action("DeleteFile", "Upload")' + '/' + fileName,
contentType: "application/json; charset=utf-8",
dataType: "html"
});
$(this).parent().remove();
}).attr('src', responseJSON.uploadedFilePath))
);
}
}
}
});
}
window.onload = createUploader;
</script>
How can I add the uploaded file names to Model with javascript & update the database only when user saves model data.
if you're uploading a Stream body in your controller, you can pass the filename as a query string parameter and map that to a function parameter for saving later.
example from another project
[WebInvoke(UriTemplate = "/AddImage/{filename}", Method="POST")]
public PrimitiveResponse AddImage(string filename, Stream imageData)
{
//magic happens here
}

Categories