javascript - Get datauri from URL images (Synchronous XMLHttpRequest blob requests) - javascript

I'm generating a PDF document using jsPDF, however I need to add the images submitted in the form into this document.
// You'll need to make your image into a Data URL
// Use http://dataurl.net/#dataurlmaker
var imgData = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoKCgoKBggLDAsKDAkKCgr/2wBDAQICAgICAg[...]' //snip
[...]
doc.addImage(imgData, 'JPEG', 15, 40, 180, 160)
However, as shown in the snip above I need to convert the file to DataURI, but I haven't found a library that does this, nor a reliable way to do it with pure JavaScript.
I found the following method that allows me to extract the DataURI from an URL with XMLHttpRequest.
function obtenerDataUri(url, pos, lastpos, isleft)
{
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'blob';
request.onload = function() {
var reader = new FileReader();
reader.readAsDataURL(request.response);
reader.onload = function(e){
var imgData = e.target.result.replace("data:application/xml;", "data:application/png;"), pos, lastpos, isleft;
doc.addImage(imgData, 'PNG', (isleft) ? 15 : 90, (isleft) ? pos : lastpos, 75, 75);
};
};
request.send();
}
This method doesn't work for me because it's asynchronous, it means I'm adding the content in the PDF after it's fully generated. This method doesn't work synchronously because responseType is not supported in synchronous requests(probably due the deprecation).
Note that these images are already uploaded to the server when the PDF is ready to be generated, so I can't get the DataURIs on local files.
This function is called from here:
$('#imgArea img').each(function()
{
g_bPDFGisLeft = !g_bPDFGisLeft;
var bPDFGposCheck = true;
while(bPDFGposCheck)
{
var pos = (g_iPDFGpage == 0) ? doc.autoTable.previous.finalY+8+(75*g_iPDFGimg) : (75*g_iPDFGimg);
if((pos+(75*(g_iPDFGimg+1))) >= 300)
{
doc.addPage();
g_iPDFGimg = 0;
g_iPDFGpage++;
bPDFGposCheck = true;
}
else
bPDFGposCheck = false;
}
obtenerDataUri($(this).attr('src'), pos, g_fPDFGlastPos, g_bPDFGisLeft);
g_fPDFGlastPos = pos;
if(!g_bPDFGisLeft)
g_iPDFGimg++;
});
After executing obtenerDataUri, due the fact it's doing a request, won't wait until the onload function completes. This mean the PDF generator will keep adding pages and placing the images asynchronously in the incorrect pages.
How can I get the DataURI in a synchronous way from URL images?
I appreciate any help with this issue. Please tell me if you need more details about this.

Don't return result. Instead pass it to a callback function:
function obtenerDataUrl(url)
{
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'blob';
request.onload = function() {
var reader = new FileReader();
reader.readAsDataURL(request.response);
reader.onload = function(e){
//return e.target.result;
handleResult(e.target.result);
};
};
request.send();
}
function handleResult(result) {
var imgData = result; // or take whatever you need inside result
doc.addImage(imgData, 'JPEG', 15, 40, 180, 160);
}

Related

Why can't my filereader read this data url from a xmlhttprequest

let MY_URL = "*"
let DataURL;
// basic JS way of getting the info from S3
let request = new XMLHttpRequest();
request.open('GET', MY_URL, true);
request.responseType = 'blob';
request.onload = function() {
var reader = new FileReader();
reader.readAsDataURL(request.response);
reader.onload = function(e){
DataURL = e.target.result;
};
};
request.send();
listenGetImageLoad(DataURL);
Here I am getting the Data URL of a file I am getting from the web. Next I want to Read it into a fileReader, but I keep getting the bug:
Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.
My code for the fileReader is as follows::
const listenGetImageLoad = (DataURL) => {
const imageArray = Object.keys(images)
let async = imageArray.length
for (let image in images) {
const reader = new FileReader()
reader.addEventListener("load", () => {
const imageObject = new Image()
imageObject.addEventListener("load", (event) => {...})
reader.readAsDataURL(DataURL)
}
}
This function seems to be failing at reader.readAsDataURL(DataURL). I have no idea as to why this is the case. I am so sure that I have inputted a data URL into the FileReader function too.
You have two problems
You are calling listenGetImageLoad at the wrong time. XMLHttpRequest is async. So you need to call that inside your onload event.
Even when you correct the async issue you are passing the wrong thing to readAsDataURL(). It expects a blob or file object to be passed to it, not a string which is what a data url is.
Call listenGetImageLoad inside the load event passing in the response from the request to fix your issue.
request.onload = function() {
listenGetImageLoad(request.response);
};
Note you do not need to use readAsDataURL to show an image from a blob. Just call URL.createObjectURL() passing in the blob/file to get a url the browser will be able to load. Then set the src of the image with that url.
request.onload = function() {
var url = URL.createObjectURL(request.response);
yourImage.src = url;
};

THREE js - Load texture from string data

I am uploading a bunch of png files to AWS, and fetching them as I need them. When uploading, I stream the data read through fs, and when downloading I pipe the data from AWS as a response.
Anyhow, when I get hold of the data on the client, it is returned as a string with the following structure:
�PNG
IHDR�x�� IDATx���_��[�����1���s�?��9u����h�...
How do I load this into a texture? I have tried with this (also recommended by mrdoob here)
But it results in the Image throwing an uninformative error, and onload is never called.
let image = new Image();
let texture = new THREE.Texture();
image.onerror = function(e) {
//enters here
}
image.onload = function(e) {
//never here
texture.image = texture_image;
texture.needsUpdate = true;
}
image.src = myFetchedPNGData;
What is the correct way to create a THREE.Texture out of this data?
EDIT:
Here is the request for fetching the data:
let requestt = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/json');
request.addEventListener('load', (event) => {
// Here myFetchedPNGData recides in event.target.response
}, false);
request.addEventListener('error', (event) => {
//do error handling
}, false);
onProgress && request.addEventListener('progress', (event) => {
//Handle request
}, false);
request.send(...);

Use xhr.overrideMimeType but get server response first?

I want to get a Base64 encoded file from the server in order to use it in a dataURL so I use:
xhr.overrideMimeType("text/plain; charset=x-user-defined");
So I get the unprocessed data to perform the base64 encoding on.
But I also want to get the mimetype originally returned from the server to declare my dataURL:
var dataUrl = 'data:'+mimetype+';base64,'+b64;
when I try something like the following:
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
var mimetype = xhr.getResponseHeader('content-type');
xhr.overrideMimeType("text/plain; charset=x-user-defined");
xhr.send(null);
the content-type returned is always null
Full source:
function getFileDataUrl(link,mimetype)
{
var url = location.origin+link;
var getBinary = function (url)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
if(mimetype == null)
{
mimetype = xhr.getResponseHeader('content-type');
console.log('mimetype='+mimetype);
}
xhr.overrideMimeType("text/plain; charset=x-user-defined");
xhr.send(null);
return xhr.responseText;
};
var bin = getBinary(url);
var b64 = base64Encode(bin);
var dataUrl = 'data:'+mimetype+';base64,'+b64;
return dataUrl;
}
var dataUrl = getFileDataUrl(link,null);
You can set responseType of XMLHttpRequest to "blob" or "arraybuffer" then use FileReader, FileReader.prototype.readAsDataURL() on response. Though note, onload event of FileReader returns results asynchronously. To read file synchronously you can use Worker and FileReaderSync()
var reader = new FileReader();
reader.onload = function() {
// do stuff with `reader.result`
console.log(reader.result);
}
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob";
xhr.onload = function() {
reader.readAsDataURL(xhr.response);
}
xhr.send(null);
At chromium synchronous XMLHttpRequest() is deprecated, see https://xhr.spec.whatwg.org/.
You can use Promise at main thread to get data URI of requested resource using either Worker or when FileReader load event is dispatched. Or use synchronous XMLHttpRequest() and FileReaderSync() at Worker thread, then listen for message event at main thread, use .then() to get Promise value.
Main thread
var worker = new Worker("worker.js");
var url = "path/to/resource";
function getFileDataUrl(url) {
return new Promise(function(resolve, reject) {
worker.addEventListener("message", function(e) {
resolve(e.data)
});
worker.postMessage(url);
})
}
getFileDataUrl(url)
.then(function(data) {
console.log(data)
}, function(err) {
console.log(err)
});
worker.js
var reader = new FileReaderSync();
var request = new XMLHttpRequest();
self.addEventListener("message", function(e) {
var reader = new FileReaderSync();
request.open("GET", e.data, false);
request.responseType = "blob";
request.send(null);
self.postMessage(reader.readAsDataURL(request.response));
});
plnkr http://plnkr.co/edit/gayWpkTVydmKYMnPr3jD?p=preview

Can't get EXIF info using loadImage.parseMetaData

I am using JavaScript LoadImage.parseMetaData (https://github.com/blueimp/JavaScript-Load-Image) to try and get Orientation of an image on the web, so I can rotate it.
If I hardcode the orientation (see "orientation: 3" in my second loadImage call), I can rotate it... but I am trying to use loadImage.parseMetaData to get the Orientation.
I have used web based EXIF parsers and the orientation info is there in the image.
When I call loadImage.parseMetaData "data.exif" seems to be null. See this fiddle: http://jsfiddle.net/aginsburg/GgrTM/13/
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.filepicker.io/api/file/U0D9Nb9gThy0fFbkrLJP', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
// Note: .response instead of .responseText
console.log ("got image");
var blob = new Blob([this.response], {type: 'image/png'});
console.log("about to parse blob:" + _.pairs(this.response));
loadImage.parseMetaData(blob, function (data) {
console.log("EXIF:" + _.pairs(data))
var ori ="initial";
if (data.exif) {
ori = data.exif.get('Orientation');
}
console.log("ori is:" + ori);
});
var loadingImage = loadImage(
blob,
function (img) {
console.log("in loadingImage");
document.body.appendChild(img);
},
{maxWidth: 600,
orientation: 3,
canvas: true,
crossOrigin:'anonymous'
}
);
if (!loadingImage) {
// Alternative code ...
}
}
};
xhr.send();
Any ideas or alternative approaches to correctly orientating images welcome.
Your call to loadImage needs to be inside the callback from the call to parseMetaData.
The reason: as is, your code contains a race condition. The call the loadImage is very likely to be made BEFORE the call the parseMetaData completes and stuffs the orientation due to the fact they are both asynchronous calls.
Why are you making a new blob whereas you asked for a Blob? The metadata are then lost, this is why you are losing it and exif is null.
Just replace :
var blob = new Blob([this.response], {type: 'image/png'});
By:
var blob = this.response;
Should do the trick.
Had the same problem, I changed the reponse type for 'arrayBuffer' and then create the blob from the response
xhr.responseType = 'arraybuffer';
xhr.onload = function (e) {
if (this.status == 200) {
var arrayBufferView = new Uint8Array(this.response);
var blob = new Blob([arrayBufferView], { type: "image/jpeg" });
...

Show video blob after reading it through AJAX

While I was trying to create a workaround for Chrome unsupporting blobs in IndexedDB I discovered that I could read an image through AJAX as an arraybuffer, store it in IndexedDB, extract it, convert it to a blob and then show it in an element using the following code:
var xhr = new XMLHttpRequest(),newphoto;
xhr.open("GET", "photo1.jpg", true);
xhr.responseType = "arraybuffer";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
newphoto = xhr.response;
/* store "newphoto" in IndexedDB */
...
}
}
document.getElementById("show_image").onclick=function() {
var store = db.transaction("files", "readonly").objectStore("files").get("image1");
store.onsuccess = function() {
var URL = window.URL || window.webkitURL;
var oMyBlob = new Blob([store.result.image], { "type" : "image\/jpg" });
var docURL = URL.createObjectURL(oMyBlob);
var elImage = document.getElementById("photo");
elImage.setAttribute("src", docURL);
URL.revokeObjectURL(docURL);
}
}
This code works fine. But if I try the same process, but this time loading a video (.mp4) I can't show it:
...
var oMyBlob = new Blob([store.result.image], { "type" : "video\/mp4" });
var docURL = URL.createObjectURL(oMyBlob);
var elVideo = document.getElementById("showvideo");
elVideo.setAttribute("src", docURL);
...
<video id="showvideo" controls ></video>
...
Even if I use xhr.responseType = "blob" and not storing the blob in IndexedDB but trying to show it immediately after loading it, it still does not works!
xhr.responseType = "blob";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
newvideo = xhr.response;
var docURL = URL.createObjectURL(newvideo);
var elVideo = document.getElementById("showvideo");
elVideo.setAttribute("src", docURL);
URL.revokeObjectURL(docURL);
}
}
The next step was trying to do the same thing for PDF files, but I'm stuck with video files!
This is a filler answer (resolved via the OP found in his comments) to prevent the question from continuing to show up under "unanswered" questions.
From the author:
OK, I solved the problem adding an event that waits for the
video/image to load before executing the revokeObjectURL method:
var elImage = document.getElementById("photo");
elImage.addEventListener("load", function (evt) { URL.revokeObjectURL(docURL); }
elImage.setAttribute("src", docURL);
I suppose the revokeObjectURL method was executing before the video
was totally loaded.

Categories