I am trying to make a simple file upload possible but Spring does not want to play with me.
This is the endpoint for file uploads - currently not doing a lot:
#PostMapping(WordEmbeddingApiPaths.UPLOAD_MODEL)
#RequestMapping(method=RequestMethod.POST, consumes = {"multipart/form-data"})
public ResponseEntity<WordVectorListDto> uploadModel(
#ModelAttribute("file") MultipartFile file,
// #RequestBody Object obj,
RedirectAttributes redirectAttributes) {
LOGGER.debug("POST uploadModel");
return new ResponseEntity<WordVectorListDto>((WordVectorListDto)null, HttpStatus.OK);
}
I've tried several things but it all ends up in different errors. I've just tried to use #RequestBody because I thought maybe that's the trick but then I get an exception saying:
Content type 'multipart/form-data;boundary=----WebKitFormBoundarycV8dFSvDV6U9OwJq' not supported
or
Content type 'multipart/form-data' not supported
depending on what I just tried.
If I go with #RequestPart("file") MultipartFile file I see
Required request part 'file' is not present
which is similar for #RequestParam("file").
I have no idea what's so hard on this but I hope somebody can tell me how I can get that file from the client.
Below you can see the request I've sent to the endpoint:
Is this request okay?
Web Client:
var uploader = $scope.uploader = new FileUploader({
url: 'http://localhost:8080/rest-api/dl4j/we/uploadModel'
});
uploader.onAfterAddingFile = function($modelFile) {
console.info('onAfterAddingFile', $modelFile);
var fd = new FormData();
fd.append('file', $modelFile.file);
$http.post($modelFile.url, fd, {
headers: {
'Content-Type': undefined
},
transformRequest: angular.identity
})
.then(
function (data) {
alert("upload success");
},
function (data, status) {
alert("upload error");
}
);
};
index.html
<label class="btn btn-default btn-file" style="float:right;">
Upload
<input
type="file"
style="display: none;"
name="file"
multiple
nv-file-select
uploader="uploader">
</label>
Here is how i didi it:
#RequestMapping(value="/uploadFile", method=RequestMethod.POST)
public #ResponseBody String handleFileUpload(
#RequestParam("file") MultipartFile file){
String name = "test11";
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
BufferedOutputStream stream =
new BufferedOutputStream(new FileOutputStream(new File(name + "-uploaded")));
stream.write(bytes);
stream.close();
return "You successfully uploaded " + name + " into " + name + "-uploaded !";
} catch (Exception e) {
return "You failed to upload " + name + " => " + e.getMessage();
}
} else {
return "You failed to upload " + name + " because the file was empty.";
}
}
and dont forget to register the multipart resolver:
#Bean
public MultipartResolver multipartResolver() {
org.springframework.web.multipart.commons.CommonsMultipartResolver multipartResolver = new org.springframework.web.multipart.commons.CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10000000);
return multipartResolver;
}
here is the html code ... take a look at the name/id of the input fields ..
File1 to upload:
Name1: <input type="text" name="name">
File2 to upload: <input type="file" name="file">
Name2: <input type="text" name="name">
<input type="submit" value="Upload"> Press here to upload the file!
</form>
Related
I am posting a form of type file and I am handling that POST request in spring-boot controller by returning JSON data. I checked that request with postman its working fine, But when I am using onreadystatechange method in react it is giving xhr status =0
onSubmitForm(){
console.log("entered");
var xhr = new XMLHttpRequest();
xhr.open('POST', '/images', true);
//xhr.responseType = 'text';
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
var data = new FormData();
var imageFile = document.querySelector('input[type="file"]').files[0];
console.log( imageFile);
data.append("imageFile", imageFile);
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE ){
alert(xhr.status);
if(xhr.status === 200){
alert('hi there');
}
}
}
xhr.send( data );
}
render() {
return (
<div>
<form encType="multipart/form-data" action="">
File to upload:
<input type="file" name="imageFile" accept="image/*" />
<input type="submit" value="Upload" onClick={ this.onSubmitForm.bind(this) } />
</form>
</div>
)
}
I am receiving the correct data in controller, But I am not getting JSON from POST request ??
controller
#PostMapping("/images")
public #ResponseBody JSONModel addContent( #RequestParam("imageFile") MultipartFile file,
imageApp content ) {
String encodedfile = null ;
double fileSize = file.getSize();
try {
byte[] bytes=file.getBytes();
System.out.println(fileSize/(1024*1024));
encodedfile = Base64.getMimeEncoder().encodeToString(bytes);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
content.setDate(LocalDateTime.now());
content.setLocation(UUID.randomUUID().toString() );
content.setId(UUID.randomUUID().toString() );
content.setContent(encodedfile);
return service.addContent(content);
}
service
public #ResponseBody JSONModel addContent(imageApp content) {
return new JSONModel(1, respository.save(content).getLocation());
}
Make <input type="submit" to type="button", keeping type="submit" tells the browser that page/form has to be submitted and it causing conflicting behavior which we need to prevent. So either change the type="button" or you can prevent the default behavior by taking the event in your handler and calling e.preventDefault();
I am having a problem writing a second audio file to gridfs. I am using ng-file-upload to pick a file, pass the file/binary data to gridfs, and write it to the database. The first file works fine, however unless i load a different view first, then go back to my audio view/template page i get this error everytime:
TypeError: path must be a string
at TypeError (native)
at Object.fs.open (fs.js:540:11)
at ReadStream.open (fs.js:1673:6)
at new ReadStream (fs.js:1660:10)
at Object.fs.createReadStream (fs.js:1608:10)
at NativeConnection.<anonymous> (Z:\techFilez\webApp\MyPiCloud\routes\IO\writeFile.js:42:29)
at NativeConnection.g (events.js:260:16)
at emitNone (events.js:67:13)
at NativeConnection.emit (events.js:166:7)
at open (Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\lib\connection.js:518:11)
at NativeConnection.Connection.onOpen (Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\lib\connection.js:527:5)
at Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\lib\connection.js:487:11
at Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\lib\drivers\node-mongodb-native\connection.js:71:5
at Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\node_modules\mongodb\lib\db.js:232:5
at connectHandler (Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\node_modules\mongodb\lib\server.js:333:7)
at g (events.js:260:16)
Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\node_modules\mongodb\lib\server.js:336
process.nextTick(function() { throw err; })
^
TypeError: path must be a string
at TypeError (native)
at Object.fs.open (fs.js:540:11)
at ReadStream.open (fs.js:1673:6)
at new ReadStream (fs.js:1660:10)
at Object.fs.createReadStream (fs.js:1608:10)
at NativeConnection.<anonymous> (Z:\techFilez\webApp\MyPiCloud\routes\IO\writeFile.js:42:29)
at NativeConnection.g (events.js:260:16)
at emitNone (events.js:67:13)
at NativeConnection.emit (events.js:166:7)
at open (Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\lib\connection.js:518:11)
at NativeConnection.Connection.onOpen (Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\lib\connection.js:527:5)
at Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\lib\connection.js:487:11
at Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\lib\drivers\node-mongodb-native\connection.js:71:5
at Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\node_modules\mongodb\lib\db.js:232:5
at connectHandler (Z:\techFilez\webApp\MyPiCloud\node_modules\mongoose\node_modules\mongodb\lib\server.js:333:7)
at g (events.js:260:16)
[nodemon] app crashed - waiting for file changes before starting...
I have done several things:
i am making sure to use fs.unlink(myFile) delete from filesystem, as well as close the connection to mongoose after every successful file write.
I am using a variable
var myFile = file.path (which is the file path from ng-file-upload) - after the successful write i have tried setting myFile to null to no avail.
It works when i switch views or refresh the page, but since im going for the single page app thing i would like to avoid this.....
here is my code on the server side:
/**
* Created by foolishklown on 8/27/2016.
*/
var mongoose = require('mongoose'),
path = require('path'),
Grid = require('gridfs-stream'),
fs = require('fs'),
User = require('../../models/user');
module.exports = function(file, userId, fileType, res) {
var fileId;
//console.log('called the write file for gridfs'.green);
//console.log('file is: ', file);
var conn = mongoose.createConnection('mongodb://localhost/media', (error) => {
if(error) {
console.error('Error connecting to mongod media instance'.red);
process.exit(1);
} else {
console.info('Connected successfully to mongod media instance in the write file!'.blue);
}
});
// The following line is designating a file to grab/read, and save into mongo
// in our case it will be something from ng-file-upload that the user wants to upload
var myFile = file.path;
// Connect gridFs and mongo
Grid.mongo = mongoose.mongo;
conn.once('open', function () {
console.log('connection open, ready for I/O!');
var gfs = Grid(conn.db);
// This write stream is how well write to mongo
var writeStream = gfs.createWriteStream({
// Name the file the way you want it stored in mongo
filename: file.originalFilename,
type: fileType
});
// Create a read stream, so that we can read its data, and then with that well use the
// write stream to write to the DB via piping the writestream
var readStream = fs.createReadStream(myFile)
.on('end', () => {
writeToUserDb(userId, fileType, readStream.id);
res.status(200).send({id: readStream.id, type: fileType, user: userId});
})
.on('error', () => {
res.status(500).send('error in writing with gridfs');
})
.pipe(writeStream);
//fs.createReadStream(myFile).pipe(writeStream);
writeStream.on('close', function (file) {
console.log(file.filename + 'written to DB');
/**
setTimeout(1000, () => {
fs.unlink(myFile);
});
*/
fs.unlink(myFile);
myFile = null;
conn.close();
});
});
function writeToUserDb(uid, type, fileId) {
var userConn = mongoose.createConnection('mongodb://localhost/mean-auth', (error) => {
if(error) {
console.error('Error connecting to the mean-auth instance'.red);
process.exit(1);
} else {
console.info('Connected to the mean-auth instance!'.blue);
console.info('Attempting to find user: ' + uid + ', filetype: ' + type + ', streamID: ' + fileId + '!'.green);
User.findById(uid, (err, doc) => {
if(err) {
console.error('Error finding user with id: ', uid);
process.exit(1);
} else {
console.log('original doc: ', doc);
doc.addMedia(type, fileId);
doc.save();
console.log('new doc: ', doc);
}
})
}
});
userConn.close();
}
};
alas, it was not an issue on the back end at all, it was an angular issue with ng-file upload.......
my html:
<div class="row">
<div class="col-lg-10">
<h1>MyPi Audio</h1>
</div>
<div class="col-lg-2">
<form name="form">
<input type="file" id="file1" name="file" ng-files="getTheFiles($files)"
ng-keep="false"
accept="'audio/*'">
<input type="button" class="btn btn-default" ng-click="uploadFiles('/dashboard/uploadAudio', 'audio', myCont.currentUser)" value="Upload">
</form>
</div>
</div>
<div class="row">
<div ng-repeat="audio in myCont.media.audio" style="width:100%;background-color:#999999;border:1px solid deeppink;">
<img ng-src="{{audio.url}}" />
<span>{{audio.name}}</span>
</div>
</div>
Inside the controller, building form data was not resetting......
the OLD code:
var formdata = new FormData();
$scope.getTheFiles = function ($files) {
angular.forEach($files, function (value, key) {
//console.log('key: ' + key + ', val: ' + $files[key]);
formdata.append(key, value);
});
}
};
After the response was sent back from the server, i simply called $scope.getTheFiles(null), and added a check for null conditions in the function which reset the form data
var formdata = new FormData();
$scope.getTheFiles = function ($files) {
if($files == null) {
formdata = new FormData();
} else {
angular.forEach($files, function (value, key) {
//console.log('key: ' + key + ', val: ' + $files[key]);
formdata.append(key, value);
});
}
};
I answered my question in case anyone else has the same issue in the future
I'm editing this post to show my latest attempt per suggestions below.
I have been searching the forums trying to find a solution. I have an ASP.NET MVC Application in which I use Angular. I am trying to use danialfarid/ng-file-upload to allow users to upload PDFs which then get saved to the database as binary data (not my idea, but I have to do it that way).
I have the following (taken from the examples) in my HTML:
File:<input type="file" ngf-select ng-model="picFile" name="file" accept="image/*" ngf-max-size="2MB" required ngf-model-invalid="errorFile"><br />
<img ngf-thumbnail="picFile" class="thumb"> <button ng-click="picFile = null" ng-show="picFile">Remove</button><br />
<button type="button" class="btn btn-primary" ng-click="uploadPic(picFile)">Upload</button>
And this in my Angular controller:
$scope.uploadPic = function (files) {
file.upload = Upload.upload({
url: '/SSQV4/SSQV5/Document/UploadEMRDocument',
data: {file: files}
})
}
My MVC Controller:
namespace SSQV5.Controllers
{
public class DocumentController : ApiController
{
public async Task<IHttpActionResult> UploadEMRDocument()
{
try
{
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var f = provider.Contents.First(); // assumes that the file is the only data
if (f != null)
{
var filename = f.Headers.ContentDisposition.FileName.Trim('\"');
filename = Path.GetFileName(filename);
var buffer = await f.ReadAsByteArrayAsync();
//buffer now contains the file content,
//and filename has the original filename that was uploaded
//do some processing with it (e.g. save to database)
}
else
{
return BadRequest("Attachment failed to upload");
}
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return Ok();
}
}
}
This code never hits the MVC Controller at all. I'm obviously missing something, but I haven't the slightest clue as to what it could be. Any assistance is greatly appreciated!
You need to extract the file content out of the form data.
Below is how I do this (using ng-file-upload in the same manner as you from the front end) to upload attachments in my application.
public async Task<IHttpActionResult> UploadAttachment()
{
// Check if the request contains multipart/form-data.
try
{
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var f = provider.Contents.First(); // assumes that the file is the only data
if (f != null)
{
var filename = f.Headers.ContentDisposition.FileName.Trim('\"');
filename = Path.GetFileName(filename);
var buffer = await f.ReadAsByteArrayAsync();
//buffer now contains the file content,
//and filename has the original filename that was uploaded
//do some processing with it (e.g. save to database)
}
else
{
return BadRequest("Attachment failed to upload");
}
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return Ok();
}
When you configure the upload, you specify the URL where the file will be posted to:
file.upload = Upload.upload({
url: 'myMVC/MyMethod',
data: {file: file}
})
<input id="uploadImageId" type="file" accept="image/*" onChange="angular.element(this).scope().uploadPic(this);">
Controller:
$scope.uploadPic = function(input) {
if (input.files && input.files[0]) {
ContactService.uploadContactImage.upload({
fileName : input.files[0].name
}, input.files[0], function(data) {
});
}
}
};
Service:
uploadContactImage : $resource('/services/api/contacts/uploadContactImage/:fileName',
{
fileName : '#fileName'
}, {
upload : {
method : 'POST',
headers: {'Content-Type': 'multipart/form-data; boundary='}
}
})
API:
#POST
#Path("/uploadContactImage/{fileName}")
#Produces("application/json")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Map<String, Object> uploadContactImage(
#PathParam("fileName") String fileName,
#FormDataParam("file") InputStream uploadedInputStream,
#FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
#Context HttpServletRequest httpServletRequest) throws GaException {
BufferedImage image = ImageIO.read(uploadedInputStream);
ImageIO.write(image, "jpg", new File("/../../fileName));
}
Output:
java.lang.IllegalArgumentException: image == null!
at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(ImageTypeSpecifier.java:925)
at javax.imageio.ImageIO.getWriter(ImageIO.java:1591)
at javax.imageio.ImageIO.write(ImageIO.java:1520)
Please guide me in this
Did I miss any param's to be passed in header? Or how can I pass #FormDataParam in angularjs?
The solution for me was to include mimepull-1.9.4.jar. http://mvnrepository.com/artifact/org.jvnet.mimepull/mimepull/1.9.4
This is part of my servlet:
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
#SuppressWarnings("deprecation")
Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(req);
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterator;
try {
iterator = upload.getItemIterator(req);
Picture pic = null;
PictureAccess access = null;
while(iterator.hasNext()){
FileItemStream item = iterator.next();
pic = new Picture( blobs.get(item.getFieldName()).getKeyString() );
access = new PictureAccess();
access.addPictures(pic, user.getEmail() );
}
} catch (FileUploadException e) {
e.printStackTrace();
}
res.sendRedirect("/user/picture/upload.jsp");
}
In my client side, I used JavaScript to change the names of the file:
<script type="text/javascript">
function uploadFile() {
if (window.File && window.FileList) {
var fd = new FormData();
var files = document.getElementById('fileToUpload').files;
for ( var i = 0; i < files.length; i++) {
fd.append("file" + i, files[i]);
}
var xhr = new XMLHttpRequest();
xhr.open("POST", document.getElementById('uploadForm').action);
xhr.send(fd);
alert('already saved');
document.getElementById('uploadForm').value = '';
} else {
document.getElementById('uploadForm').submit(); //no html5
}
}
</script>
In my html/jsp:
<form id="uploadForm" enctype="multipart/form-data" method="post" action="<%= blobstoreService.createUploadUrl("/user/uploadPics") %>">
<input type="file" name="fileToUpload" id="fileToUpload" multiple="multiple" size="5"/>
<input type="button" onclick="uploadFile();" value="Upload" />
</form>
The above codes works in development machine but not working when deployed. What are the possible error and solution to the problem? How can I see the error in appengine?
Deprecated method getUploadedBlobs(..) does not support multiple='true'. Try using getUploads(..) instead.