I am trying to upload multiple files at a time using Spring Boot and AngularJS.
Spring Boot file
#RequestMapping(value = "/fileUpload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE )
public #ResponseBody boolean UploadFile(#RequestParam(value = "file", required = true) MultipartFile[] files){
Here I am getting the value of MultipartFile[] files as MultipartFile[0]#17615 instead of an array of MultipartFiles. The length of the array is 0.
HTML File
<input type="file" file-model="myFile" name="filePath" ng-model="filePath" on-click="false" multiple required>
AngularJS file
app.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files);
});
});
}
};
}]);
app.service('fileUpload', ['$http', 'growl', '$state', function ($http, growl, $state) {
this.uploadFileToUrl = function(file, uploadUrl){
var fd = new FormData();
for(let i=0;i<file.length;i++){
fd.append('file', file[i]);
}
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(data, status, headers) {
.....
})
.error(function(error){
.....
});
}
}]);
app.controller('uploadauditfilectrl', ['$scope', 'fileUpload', '$cookieStore', function($scope, fileUpload, $cookieStore){
$scope.uploadFile = function(){
var file = this.myFile;
var uploadUrl = "/fileUpload/";
fileUpload.uploadFileToUrl(file, uploadUrl);
};
}]);
Can anyone please tell me the mistake I am doing.
Related
I am implementing the file upload function.
When I upload a file, it shows what I uploaded, puts in the contents, and posts.
By the way, there was a problem below while uploading the files.
While trying to find a solution, many people asked me to add enctype="multipart/form-data but I added that one earlier.
And yet I don't understand that kind of error.
error
2021-07-30 23:08:21.239 ERROR 20744 --- [nio-8080-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Current request is not a multipart request] with root cause
front
<template>
<div class="scale">
<button><b-icon-arrow-left></b-icon-arrow-left></button><span>게시글 생성하기</span>
<form enctype="multipart/form-data" method="post" >
<div class="d-flex flex-row">
<button style="display:inline-block; margin-right:5%; margin-left:2%" #click.prevent="clickInputTag()" id='addimage'><b-icon-plus class="h1"></b-icon-plus></button>
<input hidden ref="plus" name="file" id="file" type="file" accept="image/*" #change.prevent="uploadImage($event)" multiple>
<div id="image_container"></div>
</div>
<div>
<b-textarea v-model="content" placeholder="Tall textarea" rows="8"></b-textarea>
<b-button #click="articleCreate()" >전송</b-button>
</div>
</form>
</div>
</template>
import axios from 'axios'
export default {
name:'ArticleCreate',
data(){
return{
content:"",
files: new FormData(),
}
},
methods:{
clickInputTag() {
this.$refs['plus'].click()
},
uploadImage(event) {
console.log(event.target.files)
const photoFile = document.getElementById("file")
this.files.append("file", photoFile.files[0]);
for (var image of event.target.files) {
var reader = new FileReader();
reader.onload = function(event)
{
var img = document.createElement("img");
img.setAttribute("src", event.target.result);
img.setAttribute("width","25%");
document.querySelector("div#image_container").appendChild(img);
};
reader.readAsDataURL(image);
}
console.log(this.files, typeof this.files)
},
articleCreate(){
axios({
url:'http://127.0.0.1:8080/article',
method:'post',
headers: {
'x-access-token': `${localStorage.getItem('token')}`,
},
params: {
content: this.content,
files: this.files,
}
})
.then(res=>{
this.$router.push({ name:'article'})
console.log(res.data)
console.log(this.files)
})
.catch(err=>{
console.log(`${localStorage.getItem('token')}`)
console.log(this.files)
console.log(this.content)
console.log(err)
})
},
}
}
back
#PostMapping("/article")
#ApiOperation(value = "게시글 작성")
public Object postArticle(#RequestParam String content, #RequestParam(required = true) List<MultipartFile> files) throws IOException {
Authentication user = SecurityContextHolder.getContext().getAuthentication();
ResponseEntity response = null;
if(user.getPrincipal() == "anonymousUser"){
response = new ResponseEntity<>("Fail", HttpStatus.UNAUTHORIZED);
return response;
}else {
UserDetails user2 = (UserDetails) user.getPrincipal();
Optional<User> userOpt = userDao.findByEmail(user2.getUsername());
Long articleId = articleDao.save(Article.builder()
.articleid(null)
.id(userOpt.get().getUid())
.createdtime(null)
.updatedtime(null)
.review(content)
.build()
).getArticleid();
List<String> pathName = saveFiles(files);
System.out.println(pathName.get(0));
for(int i = 0; i < files.size(); ++i) {
imageDao.save(Image.builder()
.imageid(null)
.articleid(articleId)
.imgURL(pathName.get(i))
.build()
);
}
response = new ResponseEntity<>("게시글 작성 완료", HttpStatus.OK);
return response;
}
}
Try adding content-type: multipart/form-data in header of your axios call.
headers: {
'x-access-token': `${localStorage.getItem('token')}`,
'content-type': 'multipart/form-data'
}
Also your axios post data should be in FormData type
var dataBody = new FormData();
dataBody .append('content', this.content);
dataBody .append('files', this.files);
change params to data and set it to dataBody
axios({
url:'http://127.0.0.1:8080/article',
method:'post',
headers: {
'x-access-token': `${localStorage.getItem('token')}`,
'content-type': 'multipart/form-data'
},
data: dataBody
}
Finally, at your backend:
Add consumes = { MediaType.MULTIPART_FORM_DATA_VALUE } in #PostMapping.
Replace #RequestParams with #RequestPart.
#PostMapping("/article", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
#ApiOperation(value = "게시글 작성")
public Object postArticle(#RequestPart String content, #RequestPart(required = true) List<MultipartFile> files)
I am trying to post a file (single file or multiple files) along with some JSON data using AngularJS and Spring MVC.
I tried as shown below:
JS:
(function () {
'use strict';
var myApp = angular.module('app');
myApp.controller('filesWithJSONController', function ($scope, fileUploadService) {
$scope.uploadFile = function () {
var file = $scope.myFile;
var uploadUrl = myApplnURL + '/showInfo/getInformationTest';", //Url of web service
var fd=new FormData();
angular.forEach($scope.files,function(file){
fd.append('file',file);
});
fd.append('properties', new Blob([JSON.stringify({
"name": "root",
"password": "root"
})], {
type: "application/json"
}));
promise = fileWithJSONService.sendInformation(fd,uploadUrl);
promise.then(function (response) {
$scope.serverResponse = response;
}, function () {
$scope.serverResponse = 'An error has occurred';
})
};
});
})();
(function () {
'use strict';
var myApp = angular.module('app');
myApp.service('fileWithJSONService', function ($http, $q) {
this.sendInformation = function (fd, uploadUrl) {
var deffered = $q.defer();
var config = {
headers : {
'Content-Type': undefined
}
}
$http.post(uploadUrl, fd, config).then(function (response) {
console.log("response " + response);
}, function (errResponse) {
console.error('Error in request' + errResponse);
deferred.reject(errResponse);
});
...
Spring Controller:
#Controller
#RequestMapping("/showInfo")
public class InfoController{
#RequestMapping(value = "/getInformationTest", method = RequestMethod.POST, consumes = {"multipart/form-data"})
#ResponseBody
public String sendInformationTest(#RequestPart("properties") ConnectionProperties properties, #RequestPart("file") List<MultipartFile> multiPartFileList){
System.out.println("In spring controller");
//business logic
}
With the above code, it is showing the multiPartFileList size as zero in Spring Controller.
But if I change the code to take only one file instead of multiple files, it is showing the file information successfully. Any input?
try with:
var fd = new FormData();
fd.append('file', file);//replace with forEach
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,//overrides Angular's default serialization, leaving our data intact.
headers: {'Content-Type': undefined}//lets the browser detect the correct Content-Type as multipart/form-data, and fill in the correct boundary.
})
.success(function(){})
.error(function(){});
Backend - Spring:
#RequestMapping(value ="/upload", method = RequestMethod.POST)
public ResponseEntity handleFileUpload(#RequestParam("file") MultipartFile[] files){
//add the others params & logic
}
I want to upload to server multiple files. Here is my Angular code:
app.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function () {
scope.$apply(function () {
modelSetter(scope, element[0].files);
});
});
}
};
}]);
And part of the controller:
var fd = new FormData();
fd.append('files', $scope.file);
fd.append('ticketDto', JSON.stringify(data));
$http({
method: 'POST',
url: url,
headers: {
'Content-Type': undefined
},
data: fd,
arrayKey: '',
transformRequest: function (data, headersGetterFunction) {
return data;
}
})
The problem is that in controller:
public void createTicket(#RequestParam(value = "files", required = false) List<MultipartFile> files,
Authentication authentication,
#RequestParam(value = "ticketDto", required = false) String name)
'files' list is always empty (I also tried MultipartFile[] instead of list). It works only if in directive return not all files, but one, for example:
scope.$apply(function () {
modelSetter(scope, element[0].files[0]);
});
instead of
scope.$apply(function () {
modelSetter(scope, element[0].files);
});
Finally I got it. The solution is to add all files to the FormData apart, not as list or array:
angular.forEach($scope.file, function (value, key) {
fd.append('files', value);
})
And then in Spring controller:
public void createTicket(#RequestParam(required = false) MultipartFile[] files)
The array contains all appended files.
I have been struggling to upload a file using Angular JS to POST to my Java Play Framework RESTful backend as a lot of examples I have found don't apply to me.
I am confident my Angular JS code works fine but I will share it anyway:
file-upload.html
<div data-ng-controller="uploadCtrl">
<input type="file" file-model="myFile" id="theFile" name="theFile" />
<button data-ng-click="uploadFile()">Upload</button>
</div>
file-upload-ctrl.js
"use strict";
angular.module("pamm").controller("uploadCtrl", ["$scope", "fileUploadRepository",
function($scope, fileUploadRepository) {
$scope.uploadFile = function () {
var file = $scope.myFile;
console.dir("Ctrl: " + file);
fileUploadRepository.uploadFile(file);
}
}]);
file-upload-repo.js
"use strict";
angular.module("pamm").service("fileUploadRepository", ["$q", "$log", "$rootScope", "contextEvent", "fileUploadDao",
function ($q, $log, $rootScope, contextEvent, fileUploadDao) {
var fileUploadCache = [];
(function init() {
$rootScope.$on(contextEvent.CLEAR_CONTEXT, function clearContext() {
fileUploadCache = [];
$log.info("fileUploadRepository: context cleared");
})
})();
this.uploadFile = function (file) {
var waitingDialog = $$dialog.waiting("Please wait - Uploading file");
var deferred = $q.defer();
fileUploadDao.uploadFile(file).then(function (uploadedFile) {
fileUploadCache.push(uploadedFile);
$log.debug("Repo: " + uploadedFile);
waitingDialog.close();
deferred.resolve(uploadedFile);
}, function (error) {
deferred.reject(error);
waitingDialog.close();
});
return deferred.promise;
};
}]);
The layer up from Repo
"use strict";
angular.module("pamm").service("fileUploadDal", ["$http", "$q", "$log", function ($http, $q, $log) {
this.http = (function serviceCaller() {
return {
/**
* #returns {promise}
*/
POST: function (apiPath, fd) {
var deferred = $q.defer();
$http.post(apiPath, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).then(function (results) {
deferred.resolve(results.data);
}, function (e) {
deferred.reject(e);
});
return deferred.promise;
}
}
})();
$log.info("fileUploadDal:serviceCaller Instantiated");
}]);
Java
Http.MultipartFormData file = request().body().asMultipartFormData();
Where can I go next with this? Hence I can't access the name of the HTML input because of the way my Angular is written.
What can I do to file to save it the file to a directory I desire.
Thanks,
Have you tried this:
import java.io.File;
File fileFromRequest = request.body().asRaw().asFile();
fileFromRequest.renameTo(new File("path/to/dir","filename.png"));
public ServiceResult execute(final Http.MultipartFormData request)
{
final String DIRECTORY = "C:\\SAVE_DIRECTORY\\SUB_FOLDER\\uploads";
List<Http.MultipartFormData.FilePart> targetFiles = request.getFiles();
for (int i = 0; i < targetFiles.size(); i++)
{
File file = targetFiles.get(i).getFile();
String fileName = targetFiles.get(i).getFilename();
file.renameTo(new File(DIRECTORY, fileName));
}
return new ServiceResult(Json.toJson(targetFiles));
}
I have developed web service using Play framework. I have created POST rest service in Play framework as below.
In routes file:
POST /imageUpload controllers.Application.imageUpload()
In Application.java:
public static Result imageUpload() {
ObjectNode result = Json.newObject();
MultipartFormData body = request().body().asMultipartFormData();
FilePart file = body.getFile("file");
File trainImg = file.getFile();
File temp = new File("/Play_Framework/temp.jpg");
trainImg.renameTo(temp);// moved the image to another folder to check the correct image.
.
.
.
return ok(result);
}
In the service, I have written the code to save the image which is received from the request.
I have called this service from the Ajax as given below.
In my index.html file:
var formData = new FormData();
var blob = new Blob([imageData], { type: "image/jpg"});
formData.append("file", blob, "capturedImage.jpg");
$.ajax({
url: 'http://localhost:9000/imageUpload',
data: formData,
crossDomain: true,
processData: false,
contentType: false,
async: false,
cache: false,
dataType: "json",
type: 'POST',
success: function(data){
alert(data);
var responseStr = JSON.stringify(data);
alert("Response str -- "+responseStr);
}
});
When I upload the file as Multipart form data, I can receive it as multipart data in the server side. But it is stored as "multipartBody7906599875117091855asTemporaryFile".
If I get this file's mimitype - "content/unknown".
When I debug, I got the below multipart form data path "/var/folders/f3/r3rfqfl949z5pf2cprwn95dm0000gn/T/multipartBody7906599875117091855asTemporaryFile".
How do parse this as "jpg" file? Can anyone help me to do this?
The following worked for me:
Application.java
public class Application extends Controller {
public static Result index() {
return ok(index.render("SO answer 30229421"));
}
public static Result imageUpload() {
ObjectNode result = Json.newObject();
Http.MultipartFormData body = request().body().asMultipartFormData();
Http.MultipartFormData.FilePart file = body.getFile("file");
File trainImg = file.getFile();
File temp = new File("/tmp/temp.jpg");
trainImg.renameTo(temp); // moved the image to another folder to check the correct image.
result.put("path", temp.getAbsolutePath());
return ok(result);
}
public static Result imageDownload(String path) {
return ok(new File(path));
}
index.scala.html
#(message: String)
<html>
<body>
<h1>#message</h1>
<input type="file" id="imageData"/>
<button onclick="send()">Send image</button>
<div id="image">
</div>
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script>
function send() {
var imageData = $('#imageData')[0].files[0];
var formData = new FormData();
var blob = new Blob([imageData], { type: "image/jpg"});
formData.append("file", blob, "capturedImage.jpg");
$.ajax({
url: 'http://localhost:9000/imageUpload',
data: formData,
crossDomain: true,
processData: false,
contentType: false,
async: false,
cache: false,
dataType: "json",
type: 'POST',
success: function(data){
$('#image').html('<img src="http://localhost:9000/imageDownload?path=' + data.path + '"/>');
}
});
}
</script>
</body>
</html>
The only difference, is that because you didn't include how you get the imageData value, I used this version (as per this SO answer):
var imageData = $('#imageData')[0].files[0];