encode/decode image with base64 breaks image - javascript

I am trying to encode and decode an image. I am using the FileReader's readAsDataURL method to convert the image to base64. Then to convert it back I have tried using readAsBinaryString() and atob() with no luck. Is there another way to persist images without base64 encoding them?
readAsBinaryString()
Starts reading the contents of the specified Blob, which may be a
File. When the read operation is finished, the readyState will become
DONE, and the onloadend callback, if any, will be called. At that
time, the result attribute contains the raw binary data from the file.
Any idea what I'm doing wrong here?
Sample Code
http://jsfiddle.net/qL86Z/3/
$("#base64Button").on("click", function () {
var file = $("#base64File")[0].files[0]
var reader = new FileReader();
// callback for readAsDataURL
reader.onload = function (encodedFile) {
console.log("reader.onload");
var base64Image = encodedFile.srcElement.result.split("data:image/jpeg;base64,")[1];
var blob = new Blob([base64Image],{type:"image/jpeg"});
var reader2 = new FileReader();
// callback for readAsBinaryString
reader2.onloadend = function(decoded) {
console.log("reader2.onloadend");
console.log(decoded); // this should contain binary format of the image
// console.log(URL.createObjectURL(decoded.binary)); // Doesn't work
};
reader2.readAsBinaryString(blob);
// console.log(URL.createObjectURL(atob(base64Image))); // Doesn't work
};
reader.readAsDataURL(file);
console.log(URL.createObjectURL(file)); // Works
});
Thanks!

After some more research I found the answer from here
I basically needed to wrap the raw binary in an arraybuffer and convert the binary chars to Unicode.
This is the code that was missing,
var binaryImg = atob(base64Image);
var length = binaryImg.length;
var ab = new ArrayBuffer(length);
var ua = new Uint8Array(ab);
for (var i = 0; i < length; i++) {
ua[i] = binaryImg.charCodeAt(i);
}
The full sample code is here

URL.createObjectURL expects a Blob (which can be a File) as its argument. Not a string. That's why URL.createObjectURL(file) works.
Instead, you are creating a FileReader reader that reads file as a data url, then you use that data url to create another Blob (with the same contents). And then you even create a reader2 to get a binary string from the just constructed blob. However, neither the base64Image url string part (even if btoa-decoded to a larger string) nor the decoded.binary string are vaild arguments to URL.createObjectURL!

Related

Uploading a blob URL to Imgur?

Im trying to upload blob url to imgur api:
https://apidocs.imgur.com/#c85c9dfc-7487-4de2-9ecd-66f727cf3139
it says clearly in docs that it can be: binary file or base64 or url
My URL (example): blob:http://localhost:8080/7e44709-093d-4a4-b167-a3fdfc63a8e
formData.append('image', 'blob:http://localhost:8080/7e44709-093d-4a4-b167-a3fdfc63a8e');
formData.append('type', 'URL');
However Im getting 400 error from imgur api that:
{"data":{"error":
"Invalid URL (blob:http:\/\/localhost:8080\/7e44729-093d-4aa4-167-a3fdef3a8e)",
"request":"\/3\/image","method":"POST"},"success":false,"status":400}
Looking forward for help why does it fail and how to upload it properly. Thank u
The image needs to be converted to base64 and then from base64 in to binary. This is done using .toDataURL() and dataURItoBlob()
Img => Base64 => Binary
function imgToURI() {
// Convert image to Base64
var img = snap.toDataURL();
// Convert Base64 image to binary
var file = dataURItoBlob(img);
}
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});
}
You could stop at base64 if that's all you need, in my case I needed to convert again to binary so that I could pass the data over to twitter (using OAuth) without use of a db. It turns out you can tweet binary which is pretty cool, twitter will convert it back in to an image.
I created a blog post about this a few years ago
Since the API accepts binary, you can send the Blob you used to generate this blobURI (the one you must have passed to URL.createObjectURL since it is the only method able to genereate such an URI).
formData.append('image', the_original_blob);
formData.append('type', 'file');
If you are not responsible of the code that did produce this blobURI you've got a problem.
you can check this answer which provides a monkey-patch to URL methods so that we can retrieve the Blob they point to,
or you could fetch that blobURI as a Blob again (more memory usage, but still a bit less than going through base64).

converting video src to a single-session blob url

Let's say I have a video element on my website:
<video src="/video.mp4" controls="" id="video"></video>
How do I go about protecting the original source file (/video.mp4) by converting it to a single-session Blob URL?
I have seen a few posts stating that it needs to be done with JavaScript, though none of them actually expand on the necessary details explaining how to do it (or where you can find out how).
So, what is the best approach for something like this?
Here is a quick and dirty example. Hope it helps.
Make sure to go over the docs of all of the methods being used and check their browser support. This will not protect your video from being downloadable though.
// Request the video using a new XMLHttpRequest() with the
// responseType set to blob.
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(){
var reader = new FileReader();
reader.onloadend = function(){
// Pass this string to atob to decode the base-64 encoded string to a string
// representing each byte of binary data.
var byteCharacters = atob(reader.result.slice(reader.result.indexOf(',') + 1));
// Now you can create an array of byte values using charCodeAt and looping
// over the byte string.
var byteNumbers = new Array(byteCharacters.length);
for(var i = 0; i < byteCharacters.length; i++){
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
// Pass the resulting array to Uint8Array to create a typed array of
// 8-bit, unsigned integers.
var byteArray = new Uint8Array(byteNumbers);
// This can then can be passed to the Blob constructor.
var blob = new Blob([byteArray], {type: 'video/ogg'});
// Now that you have a blob, you can pass it to the createObjectURL method.
var url = URL.createObjectURL(blob);
// The resulting URL can be attached to the src attribute of your video.
document.getElementById('video').src = url;
}
// Pass the response to the FileReader using readAsDataURL.
// This will give you a base-64 encoded string representation of the file.
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', 'https://upload.wikimedia.org/wikipedia/commons/c/c0/Roaring_Burps.ogg');
xhr.send();
<video controls="" id="video"></video>
To make the Blob URL, I found this answer. This will load large files much faster than DavidDomain's answer (which took unacceptably long for my case of a >100MB video file). Although, I believe that this will download the whole video into the browser's memory, and embed the data into the DOM, so larger files might still cause other performance issues.
Why do you want to "[protect] the original source files location" of the video? If something finds the video's location and requests the video file, then that file should be served: that's a server's job.
AFAIK it's practically impossible to load a video file without exposing the URL required to obtain that video file. (It should be technically possible to embed it into the DOM server-side, but that would force the entire video to be loaded before the page shows anything, and would be unusable)

Converting File object to Image object in JavaScript

So I have a website (using AngularJS) that lets users upload files via tag
<input type="file" ng-model="image" id="trigger" onchange="angular.element(this).scope().setFile(this)" accept="image/*">
When I handle the upload in the controller the image gets stored as a File object. This is my method that saves the file and also sets variables to preview the image
$scope.setFile = function (element) {
$scope.image = element.files[0]; // This is my image as a File
var reader = new FileReader();
//This is for previewing the image
reader.onload = function (event) {
$scope.image_source = event.target.result;
$scope.$apply();
}
reader.readAsDataURL(element.files[0]);
}
Now I am trying to compress the image using J-I-C library found here: https://github.com/brunobar79/J-I-C/blob/master/src/JIC.js
But this library accepts an image object as its parameter and returns it as a compressed image object. I can't seem to find a way to convert my $scope.image File object into an Image object. How would I go about doing this?
I would also need to convert the compressed image object back into a File so I can upload it to my Azure storage.
You just need to create an Image instance, and set it's src to your data url. Then pass it to JIC:
var img = new Image();
img.src = $scope.image_source;
jic.compress(img,...)
It then just uses a canvas element to manipulate the image, generate a new data url, and creates a new Image instance, setting its src to the data url. So when you get the compressed image back just take the src and use atob to decode the base64 encoded data back into binary and create a Blob. You can use Blob in most places that you would use File, for instance like uploading through ajax.
var newImg = jic.compress(oldImg,...);
//replace 'image/png' with the proper image mime type
var base64data = newImg.src.replace("data:image/png;base64,","");
var bs = atob(base64data);
var buffer = new ArrayBuffer(bs.length);
var ba = new Uint8Array(buffer);
for (var i = 0; i < bs.length; i++) {
ba[i] = bs.charCodeAt(i);
}
var blob = new Blob([ba],{type:"image/png"});
//now use blob like you would any other File object

Decode base64 image and upload

I have a webapp that is saving images locally until they are ready to be sent to the server. When I save the images locally, I base64 encode them. Now I want to do a multipart file upload with these images.
So I need to convert the image back into binary form. I've tried using the FileReader to convert it back like this,
var fr = new FileReader();
fr.onloadend = function(binaryImage){
debugger;
binaryImage;
};
var base64Str = item.base64Image.substr(item.base64Image.indexOf("base64") + 7);
//var base64Str = item.base64Image;
fr.readAsBinaryString(base64Str);
but the onloadend event is never fired and there are no errors. Once I get the image I wont have trouble uploading it. Any ideas?
Thanks!
Not to familiar with FileReader, but I believe readAsBinaryString is expecting a Blob or File object. Passing it a string causes errors on my end. Try this:
var fr = new FileReader();
fr.onloadend = function(binaryImage){
debugger;
binaryImage;
};
var blob = new Blob([item.base64Image.
substr(item.base64Image.indexOf("base64") + 7)]);
fr.readAsBinaryString(blob);
I don't think this will give you want though. Check out this article for ways to encode/decode Base64: How can you encode to Base64 using Javascript?
Looks like you can use btoa() and atob() for web kit browsers.

Download and encode image in base64 using JavaScript

lets say i have a URL given. I would like to:
1) download it and convert to base64
2) upload it to some key/value storage (as text)
3) download it from key/value storage (with text/plain mimetype), reencode it from base64, display it.
Best Regards
If someone is still searching for downloading images and encoding them in base64 string, I recently find this kind of outdated method but really reliable. The advantage is that it's pure Javascript so there is no need to install any external library. I previously tried using fetch and axios but for some reason the encoded string was not in a correct format.
NB: If you are encoding this image to send it to an API, some of them require to delete the leading data type including the , at the start of the encoded string.
function toDataURL (url, callback) {
const xhRequest = new XMLHttpRequest()
xhRequest.onload = function () {
const reader = new FileReader()
reader.onloadend = function () {
callback(reader.result)
}
reader.readAsDataURL(xhRequest.response)
}
xhRequest.open('GET', url)
xhRequest.responseType = 'blob'
xhRequest.send()
}
const URL = "https://upload.wikimedia.org/wikipedia/commons/f/f7/Stack_Overflow_logo.png"
const logCallback = (base64image) => {
// Base64 encoded string with leading data type like
// data:image/png;base64,RAW_ENCODED_DATA______
console.log(base64image)
}
toDataURL(URL, logCallback)

Categories