creating an image file object from an image web url - javascript

I would like to take an image's URL and use that to get the image file object and send it to my server. i have looked around and have gotten this far but the code does not work properly. When i send it to my server it does not upload as an image file but simply uploads as a file without the .JPG extension or any image file extension.
var byteNumbers = new Array(this.imgUrl.length);
for (var i = 0; i < this.imgUrl.length; i++)
{
byteNumbers[i] = this.imgUrl.charCodeAt(i);
}
this.img = new File(byteNumbers, "imgFromUrl", { type: "image/jpeg" });
This gets the file and shows me the file details on my log such as lastmodified, filename and such. but when i upload it to my server i just get a file with no extension as mentioned above.
The file is being uploaded by being attached to a form and sent to the server.This is because i'm sending other data as well.
this.http.post(this.serverUrl + "face/detect/", data, { headers: this.headers })
.toPromise()
.then(response => response.json())
.catch(error => error);

You would need to specify the extention in the file name
var byteNumbers = new Array(this.imgUrl.length);
for (var i = 0; i < this.imgUrl.length; i++)
{
byteNumbers[i] = this.imgUrl.charCodeAt(i);
}
this.img = new File(byteNumbers, "imgFromUrl.jpg", { type: "image/jpeg" });
Tell me if this solves the problem.

Related

Converting dataURI to file for upload via REST does not work but normal filefield upload works fine

I have implemented a solution that accepts a single file upload (image for profile) in a Django Rest Framework backend. This route api/people/id/upload_image. Only accepts a parameter with an image. and can be used via HTTP POST.
When uploading via a fileinput field in eg. Postman, the default Django API or via browser fetch() in my Vue.js application is no problem. So it seems as long as it is a default form-upload field it is doing its job.
But in my frond-end (vuejs 3) I am using an image-cropper. Users can upload an image and via the javascript cropper the image can be cropped. This is important for the UI because I need a square image. The cropper uses HTMLCanvasElement.toDataURL() as export format.
And what seemed to be not that difficult gets me stuck for days now. I just can't find a way to convert and POST the cropped image in such a way that is accepted by the upload_image API backend. I am using Fetch() for sending this POST call.
I am not a javascript expert so I get my knowledge via internet and I tried it in several ways; with first creating a BLOB from the dataURI, and by creating a File before feeding it to dataForm and send it as :body in Fetch()
The dataURI seems OK because I am also replacing the cropped image directly in the HTML. And that looks totally fine. The API is responding with an 'HTTP 200 OK'. But the old image is not being replaced.
So my assumption is that there is something wrong with the image send to the API, because via normal fileupload everything works fine. How should I convert this dataURI in a proper way so it can be send and accepted by the API endpoint. And how should the API call look like: headers, body..
this is my last attempt in converting and sending the cropped image: (dataURIimage is OK)
uploadPhoto(context, dataURIimage) {
const blob = dataURItoBlob(dataURIimage);
const resultFile = new File([blob], "picture", {
type: "image/png"
});
const formData = new FormData();
formData.append("image", resultFile);
const headerToken = "Token" + " " + this.getters.getToken;
const url =
"https://workserver-7e6s4.ondigitalocean.app/api/people/" +
this.getters.getProfiel.id +
"/upload_image/";
fetch(url, {
method: "POST",
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': headerToken,
},
body: formData,
})
.then(function(response) {
console.log(response.status)
if (response.ok) {
return response.json();
}
})
.then(function(data) {
console.log(data.image);
})
.catch(function(error) {
alert(error + " " + ":ERROR");
});
},
function dataURItoBlob(dataURI) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
var byteString = atob(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: mimeString });
}
Martijn dekker

FileSaver.js doesn't download PDF with Safari

I have an issue with FileSaver.js, I can not download a PDF (or PNG or excel file) on Safari, but it works on any other web browser. I get the error in the console : 'Failed to load resource: The network connection was lost.'
What is weird is that, this PDF file doesn't get downloaded if Tomcat serves it, but if it is Apache that serves the file, the download works fine.
Here is a sample of code (I am working with angular 1.5.8):
$http.get(url, { responseType: 'arraybuffer' })
.success(function (response) {
var file = new Blob([response], {type: 'application/pdf'});
fileSaverService(file, filename);
});
I had a similar issue and I was using axios to make a call (it was post request in my case) to the download service. The code below worked for me:
axios.post(url, downloadRequest, {responseType:'blob'})
.then(response =>{
var filename = 'example.zip';
var blob = new Blob([response.data], {type:"application/octet-stream"});
saveAs(blob , filename);
})
.catch(error => {
console.error(error);
});
I had a issue with base64,Pdf was not able to open.
This i have resolved converting base64 to bin and than converted into Uint8Array(byteNumbers).
Check below snippet which has worked for me.
const byteCharacters = atob(b64);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
var blob = new Blob([byteArray], {type: "application/pdf"});
saveAs(blob, "sample.pdf");

Angular 2 Image resize before upload

Im looking to resize an image before it is uploaded to a server, at the moment i am using ng2-imageupload like this:
<input id="media" class="inputfile" type="file" name="media" image-upload
(imageSelected)="selected($event)"
[resizeOptions]="resizeOptions" (change)="onChange($event)">
export class WpMediaFormComponent {
file: File;
resizeOptions: ResizeOptions = {
resizeMaxHeight: 768,
resizeMaxWidth: 438
};
selected(imageResult: ImageResult) {
console.log(imageResult);
this.dataBlob = this.dataURItoBlob(imageResult.resized.dataURL);
let blob = this.dataURItoBlob(imageResult.resized.dataURL);
}
This then returns an object, like this:
dataURL:"data:image/jpeg;base64, DATA URI HERE"
type:"image/jpeg;"
I can then convert this object to a blob using this function:
dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = decodeURI(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
Before doing this I was uploading the image to the server using this code:
onChange(event: EventTarget) {
let eventObj: MSInputMethodContext = <MSInputMethodContext> event;
let target: HTMLInputElement = <HTMLInputElement> eventObj.target;
let files: FileList = target.files;
this.file = files[0];
console.log(this.file);
//this.update.emit(this.file);
}
Does anyone have idea how I can feed the blob returned from dataURItoBlob method into the file upload onChange event?
Im a little lost here.
So I figured it out with the help of #Brother Woodrow, and this thread:
How to convert Blob to File in JavaScript
Here is my updated code, not the only thing I had to change was the selected method:
selected(imageResult: ImageResult) {
// create a blob
let blob: Blob = this.dataURItoBlob(imageResult.resized.dataURL);
// get the filename
let fileName: string = imageResult.file.name;
// create a file
this.file = new File([blob], fileName);
console.log(this.file);
// event emitter send to container then to http post
this.update.emit(this.file);
}
I can now upload 3MB and they are pushed to the server around 150kB in seconds which is great for the user especially as this app will mostly be used by mobile devices.
You'll need to convert the Data URI to a Blob, then send that back to your server. This might be helpful: Convert Data URI to File then append to FormData
Once you have the blob, it should be easy enough to use FormData and the Angular HTTP class to upload it to your server for further processing.
let formData = new FormData();
formData.append(blob);
this.http.post('your/api/url', fd).subscribe((response) => console.log(reponse);

Process Incoming XHR2 Data (Blob)

I send multiple files chunked into Blob's over XHR2 to a Node.js/Express server.
How can I receive them on the server while making sure they are put together correctly? In their right order and to the right file when multiple files are uploaded "at once".
Following is the code (both front- and backend) I have so far but doesn't account for multiple uploads yet.
Frontend:
// 'files' is of type FileList, directly from file input.
for (var i = 0, length = files.length; i < length; i++) {
var file = files[i];
var bytes = 51200; // 50 KB
var size = file.size;
var start = 0;
var end = bytes;
while (start < size) {
sendBlob(file.slice(start, end), file.name, file.type);
start = end;
end = start + bytes;
}
}
// sendBlob()
var sendBlob: function (data, filename, filetype) {
var xhr = new XMLHttpRequest();
xhr.open('POST', this.url, false);
xhr.setRequestHeader('X_FILENAME', filename);
xhr.setRequestHeader('Content-Type', filetype);
xhr.send(data);
};
Backend:
app.post('/', function (req, res) {
var body = '';
req.on('data', function (data) {
body += data;
});
req.on('end', function () {
var filename = req.headers['x_filename'];
var newPath = __dirname + '/upload/' + filename;
fs.writeFile(newPath, body, function (err) {
res.send({
filename: filename
});
});
});
});
Very small text files are stored correctly but images seem to always get messed up and end up with a bigger file size. Bigger text files are written correctly but there the first chunk seems to be missing.
Your upload logic is naive. Here are some things you should do to ensure correctness :
You have to maintain and communicate the chunk id/number between client and server so that order can be maintained.
var sendBlob: function (data, filename, filetype, chunkid)
//set chunkid in header or in data.
In your server you are accepting any post request and appending it to the body. You should maintain variables for filename and filetype and match it with incoming request before appending it.
Files[Name] = { //Create a new Entry in The Files Variable for each new file
Filetype : "",
FileSize: 0,//size of Data in buffer
Data: "", //buffer for storing data
Downloaded: //chunks recieved
}
Append to Data only when you check it. (Extra file size could be due to this)
In your fs.writeFile you should set encoding as binary, image and video files are binary encoded and writing them into default utf-8 encoding may corrupt them.
fs.writeFile(newPath, body, 'binary', function (err){...});
(optional) For each chunk received by server it should send an acknowledgement back to client so that it knows which chunk is dropped and must be sent.

How to upload/POST multiple canvas elements

I have to create an image uploader for a future project (No flash, IE10+, FF7+ etc.) that does image resizing/converting/cropping on the clientside and not on the server.
So I made a javascript interface where the user can 'upload' their files and get resized/cropped in the browser directly, without ever contacting the server. The performance is OK, not that good, but it works.
The endresult is an array of canvas elements. The user can edit/crop the images after they got resized, so I keep them as canvas instead of converting them to jpeg. (Which would worsen the initial performance)
Now this works fine, but I don't know what's the best way to actually upload the finished canvas elements to the server now. (Using a asp.net 4 generic handler on the server)
I have tried creating a json object from all elements containing the dataurl of each canvas.
The problem is, when I got 10-40 pictures, the browser starts freezing when creating the dataurls, especially for images that are larger than 2 megabyte.
//images = array of UploadImage
for (var i = 0; i < images.length; i++) {
var data = document.getElementById('cv_' + i).toDataURL('image/jpg');
images[i].data = data.substr(data.indexOf('base64') + 7);
}
Also converting them to a json object (I am using json2.js) usually crashes my browser. (FF7)
My object
var UploadImage = function (pFileName, pName, pDescription) {
this.FileName = pFileName;
this.Name = pName;
this.Description = pDescription;
this.data = null;
}
The upload routine
//images = array of UploadImage
for (var i = 0; i < images.length; i++) {
var data = document.getElementById('cv_' + i).toDataURL('image/jpg');
images[i].data = data.substr(data.indexOf('base64') + 7);
}
var xhr, provider;
xhr = jQuery.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener('progress', function (e) {
console.log(Math.round((e.loaded * 100) / e.total) + '% done');
}, false);
}
provider = function () {
return xhr;
};
var ddd = JSON.stringify(images); //usually crash here
$.ajax({
type: 'POST',
url: 'upload.ashx',
xhr: provider,
dataType: 'json',
success: function (data) {
alert('ajax success: data = ' + data);
},
error: function () {
alert('ajax error');
},
data: ddd
});
What would be the best way to send the canvas elements to the server?
Should I send them all at once or one by one?
Uploading files one by one is better. Requires less memory and as soon as one file ready to upload, the upload can be started instead of waiting while all files will be prepared.
Use FormData to send files. Allows to upload files in binary format instead of base64 encoded.
var formData = new FormData;
If Firefox use canvas.mozGetAsFile('image.jpg') instead of canvas.toDataUrl(). Allow to avoid unnecessary conversion from base64 to binary.
var file = canvas.mozGetAsFile('image.jpg');
formData.append(file);
In Chrome use BlobBuilder to convert base64 into blob (see dataURItoBlob function
accepted
After playing around with a few things, I managed to figure this out myself.
First of all, this will convert a dataURI to a Blob:
//added for quick reference
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
From this question):
var blob = dataURItoBlob(canvas.toDataURL('image/jpg'));
formData.append(blob);
And then send the formData object. I'm not sure how to do it in jQuery, but with plain xhr object it like so:
var xhr = new XMLHttpRequest;
xhr.open('POST', 'upload.ashx', false);
xhr.send(formData);
On server you can get files from Files collection:
context.Request.Files[0].SaveAs(...);

Categories