I am using Anuglar, Ionic and Cordova in my current project, and I'm trying to POST FormData containing an image file to my server. Right now I'm using the cordova camera plugin to return a file path to the image on the device (ex: file://path/to/img). Once I have the file path I want to append the image file to a FormData object using the images file path. Here is my code right now.
var fd = new FormData();
fd.append('attachment', file);
fd.append('uuid', uuid);
fd.append('userRoleId', userRole);
The code above works when appending a file that is taken from an <input type='file'> but doesn't work when just given the file path on the device.
Basically the FormData is showing like this right now:
------WebKitFormBoundaryasdf
Content-Disposition: form-data; name="attachment";
file://path/to/img
and I want it to look like this
------WebKitFormBoundaryasdf
Content-Disposition: form-data; name="attachment"; filename="jesus-quintana.jpg"
Content-Type: image/jpeg
I found many different ways to upload the image using cordova FileTransfer and by converting the image to a base64 and then uploading it. But I couldn't find any simple ways of just grabbing the file by using the path and posting it within a form. I'm not very familiar with the File Api so any help would be appreciated
After some fiddling around I manage to figure out a pretty simple solution.
First I added the cordova file plugin then I use the code below
var fd = new FormData();
window.resolveLocalFileSystemURL(attachment.img, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var imgBlob = new Blob([ this.result ], { type: "image/jpeg" } );
fd.append('attachment', imgBlob);
fd.append('uuid', attachment.uuid);
fd.append('userRoleId', 12345);
console.log(fd);
//post form call here
};
reader.readAsArrayBuffer(file);
}, function(e){$scope.errorHandler(e)});
}, function(e){$scope.errorHandler(e)});
So I'm just creating a form and using FileReader to insert an ArrayBuffer to the Blob object and then append that to the form data. Hopefully this helps someone else looking for an easy way to do this.
You do need to send file content. With the HTML5 FileAPI you need to create a FileReader object.
Some time ago, I developed an application with cordova and I had to read some files, and I made a library called CoFS (first, by Cordova FileSystem, but it's working in some browsers).
It's on beta state, but I use it and works well. You can try to do some like this:
var errHandler = function (err) {
console.log("Error getting picture.", err);
};
var sendPicture = function (file) {
var fs = new CoFS();
fs.readFile(file, function (err, data) {
if (err) {
return errHandler(err);
}
var fd = new FormData();
fd.append('attachment', new Blob(data));
fd.append('uuid', uuid);
fd.append('userRoleId', userRole);
console.log("Data of file:" + data.toString('base64'));
// Send fd...
});
};
navigator.camera.getPicture(sendPicture, errHandler);
Sorry my poor english.
Your post was extremly helpful to fix my problem. I'm using Ionic 4 and trying to upload an image using standard http and file client. The key code for reference is here:
return this.file.readAsArrayBuffer(path, file).
then(blob => {
const imgBlob = new Blob([blob], { type: 'image/jpeg' } );
formData.append('image[file]', imgBlob);
return this.http.post(url, formData, headers).subscribe();
});
Hope it helps someone out there as it did for me.
Related
I'm trying to upload a file, but i want to normalize it's name fisrt, it works on other browsers, but in IE11, i searched and i found out that this method (normalize) is not supported, so i'm using polyfill unorm. so normalizing works fine now, but we can't change the fileName directly, we need to create a new file. But we can't use new File because it's not supported too. So I used new Blob, but the problem is that i don't get the filename on the server side, it's always blob.
The code for other browsers :
var fileName = file.name.normalize('NFD').replace(/[\u0300-\u036f]/g, "");
var newFile = new File([file], fileName, { type: file.type });
newFile.label = 'FICHIER';
The code for IE11
fileName = unorm.nfd(file.name);
newFile = new Blob([file], { type: file.type });
newFile.label = 'Fichier';
newFile.name= fileName;
To generate the request to the server, i use formdata :
fd = new FormData();
fd.append("id", param);
fd.append(file.label || "uploadedFile", file, file[paramName]);
Can you tell me what should i do to get the filename or if there is another way to do this.
The Blob object doesn't contain the name property, so, we can't change name via the Blob object.
After getting the file data, I suggest you could append a new parameter to log the new file name, then, when submits the form or save the uploaded file, you could send the file data and the new file name to the server.
Besides, here is another thread about upload file using FormData, please refer to it:
Angular File Upload
File Upload using AngularJS
I have been working on a project, I download a zip file from internet through XMLHttpRequest (type: blob ) and then I try to read its content using JSzip.So every zip has a json which I am interested in say manifest.json. So I was successful to read it with this code.
var read_zip = new JSZip();
res=xhr.response;
read_zip.loadAsync(xhr.response).then(function (zip) {
return zip.file("manifest.json").async("string");
}).then(function (text) {
obj = JSON.parse(text);
console.log(text);});
after this I made some changes to 'obj', Now I want to replace the existing manifest with this modified 'obj' json contents and save it.
I was trying this code
var write_zip = new JSZip();
write_zip.loadAsync(xhr.response).then(function (zip) {
zip.file("manifest.json" , obj) ;
zip.generateAsync({type:"blob"})
.then(function (blob) {
saveAs(blob, "hello.zip");
});});
but I am getting this error
Uncaught (in promise) Error: Can't read the data of 'manifest.json'.
Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc)
?
sorry, I am new to this.
It looks like you're trying to write an object into zip which is not supported. According to the documentation on JsZip the value to be written needs to be of type:
String/ArrayBuffer/Uint8Array/Buffer/Blob/Promise/Nodejs stream
See: JSZip#file(name, data [,options])
I am currently a developing an application that is using a PDFTron webviewer. Is there anyway to save the edited pdf edited with pdftron webviewer to the server?
There is a feature of pdftron that saves annotations to the server, but I need to save the whole pdf with the edits to the server.
You could call the following function from a WebViewer config file to get the PDF data as a blob. Once you have the blob you can refer to one of the many examples online of how to upload a blob to a server.
function uploadPDF() {
var docViewer = readerControl.docViewer;
var options = {
xfdfString: docViewer.getAnnotationManager().exportAnnotations()
};
var doc = docViewer.getDocument();
doc.getFileData(options).then(function(data) {
var arr = new Uint8Array(data);
var blob = new Blob([arr], {
type: 'application/pdf'
});
// upload blob here
});
}
I'm trying to upload an image from my app to the server, I wanted to use the ngCordova file transfer plugin but I'm not happy with the progress informations that this plugin gives you, so I decided to go with Flowjs, but I can't manage to create a proper HTML5 File object from the file url I have.
This is my code
window.resolveLocalFileSystemURL(photo, function(fileEntry){
fileEntry.file(function(file){
$scope.flow.addFile(file);
$scope.flow.upload();
$scope.flow.on('fileProgress', function (file, chunk){
console.log(file);
console.log(chunk);
});
$scope.flow.on('error', function(a,b){
console.log(a);
console.log(b);
});
});
}, function(err){
console.log(err);
});
Where photo is the path to the fileSystem for the file.
I get a 400(bad Request) error when I try doing this upload, I'm sure the server side is correct because I use it with many other Flowjs applications.
I think that the object returned by fileEntry.file() is not a proper HTML5 file object, maybe creating a Blob from the file url could solve the problem, but I haven't understand how to create it.
I'd like to get my code to work though, before trying to create a Blob, but if that's the only solution, well...
Ok, I've found a solution that works, dunno if it's the best possibile, any improvment is well appreciated.
window.resolveLocalFileSystemURL(photo, function(fileEntry){
fileEntry.file(function(file){
var reader = new FileReader();
reader.onload = function(){
var blob = new Blob([reader.result], {type: 'application/octet-stream'});
blob.name = 'image.jpg';
$scope.flow.addFile(blob);
$scope.flow.upload();
$scope.flow.on('fileProgress', function (file, chunk){
console.log(file);
console.log(chunk);
});
$scope.flow.on('error', function(a,b){
console.log(a);
console.log(b);
});
};
reader.readAsArrayBuffer(file);
});
}, function(err){
console.log(err);
});
I looked around and just couldn't find what I'm looking for without additional plugins/libraries. I want to upload an image and process it serverside via JQuery AJAX, but I can't figure out how to pass and process it. Any help is much appreciated!
Though Diodeus is correct, it isn't quite that difficult. Just maddening.
HTML5 does expose what is called the FileReader API, which is still relatively new and unsupported on legacy browsers, but which will make your job easier. I have a small app which accepts images on the client side and, using the FileReader API, converts them to base-64 for uploading to the server.
The following is the function I call upon a user's uploading an image. App.FileReader is an instantiation of the HTML5 FileReader, which is declared simply like:
App.FileReader = window.FileReader ? new FileReader : null;
Upon upload, I read the image as a dataURL using the FileReader, and push the data into an unused tag. The FileReader itself retains the read data, which is why it is a good idea to only instantiate one FileReader at a time.
if (input.files && input.files[0]) {
if (App.FileReader) {
App.FileReader.onload = function (e) {
$('#createMomentImagePreview').attr('src', e.target.result);
}
App.FileReader.readAsDataURL(input.files[0]);
$this.uploadedImage = true
}
else {
$('#createMomentImagePreview').attr('src', 'http://d33w6ffaa49e6z.cloudfront.net/media/ImageLoaded.png');
$this.uploadedImage = true
}
}
This is the AJAX call for uploading to the server, where data represents the read file, or "App.FileReader.result":
$.ajax({
url: '/image',
type: 'POST',
data: {
image: App.FileReader.result
}
}).done(function(data){
callback(data);
}).fail(function() {
console.log("Image upload failed!")
alert("Sorry, there was an error uploading your image to the database.")
})
Server-side (and I'm using Node with Express, so this might not apply), I can convert the base64 string to a Buffer (Blob) and send that up to S3 using Knox's putBuffer. This is waaaaaay simpler than actually authenticating with S3 AND trying to get it to play nice with your binary data.
if (req.body.image.match(/^data:image\/png;base64,/)) {
var image = new Buffer(req.body.image.replace(/^data:image\/png;base64,/,""), "base64");
}
else if (req.body.image.match(/^data:image\/jpeg;base64,/)) {
var image = new Buffer(req.body.image.replace(/^data:image\/jpeg;base64,/,""), "base64");
}
awsClient.putBuffer(image, '/' + imagePath + '.jpg', headers, function(err, stream) {
if (err) {
console.log(err);
return false
}
res.send(200, imagePath + '.jpg')
image = null;
});
In either case, once you have the base64 data on your server you've made significant progress. You could use ImageMagick to handle processing as well. For what it's worth, I hope this helps you out in some way!