I'm trying to construct a Blob from an array buffer that original comes from a binary string. It works fine in Firefox & Chrome, but I don't know whats wrong with Safari & Opera
This is a simplified version of my problem:
http://plnkr.co/edit/sfEEHf?p=preview
// 1x1 red PNG pixle
base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2P8z8DwHwAFBQIAHl6u2QAAAABJRU5ErkJggg==";
byteString = atob(base64);
// convert binary to array buff so we can construct a blob later
arrayBuffer = new ArrayBuffer(byteString.length);
intArray = new Uint8Array(arrayBuffer);
for (i = 0; i < byteString.length; i += 1) {
intArray[i] = byteString.charCodeAt(i);
}
// construct blob
blob = new Blob([intArray], {type: "image/png"});
console.log(blob.size); // suppose to be 70 (its 19 in safari)
In Safari you need to use the 'buffer' property on the TypedArray, i.e. this:
blob = new Blob([intArray.buffer], {type: "image/png"});
and it'll work.
Related
I have an existing code which converts base64 string to corresponding image file. Now, I want to add some metadata like last modified date, photographer, copyright, credits etc. Tried below which did not work. Is there a way I could add the metadata?
here base64Source is the dataUri from which I am getting base 64 content.
function (base64Source, name) {
var base64Content = base64Source.substr(base64Source.indexOf(',') + 1);
var byteString = window.atob(base64Content);
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var file = new Blob([ia], {type: 'image/jpeg'});
return new File([the_file], name);
};
I modified this piece of code as
function (base64Source, name) {
var base64Content = base64Source.substr(base64Source.indexOf(',') + 1);
var byteString = window.atob(base64Content);
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var file = new Blob([ia], {type: 'image/jpeg'});
return new File([the_file], name,{
lastModified: new Date().getTime(),
tooltip : 'test tooltip',
caption : 'test caption',
copyright : 'Test',
credits : 'Test'
});
};
These metadata should be part of the file's data itself.
The javascript File object will not change the file's data (apart from the EOL endings option of the Blob constructor that File does inherit from).
So you'd have to actually modify your file's data (i.e the bytes contained in ia's ArrayBuffer).
The way you will do it depends on the type of image you are dealing with. Different image formats have different rules as to what metadata they can accept and how it should be stored in the file.
For instance, for JPEG files, TIFF and webp formats it is common to have it in EXIF data, stored in an Application Segment, identifiable by the 0xFFE1 marker for JPEG. webp marks it with a chunkHeader('EXIF'). They also all support XMP.
PNG also allows some text-info metadata.
Other file formats may support other metadata formats, and writing you an universal encoder would be ... a very hard work.
But some libraries do exist for the most common formats (JPEG's EXIF ones are easily found using your favorite web search tool).
In my javascript I have a base64 encoded pkcs12 object, which I want to provide as download link. The Pkcs12 (pfx) file to be downloaded is binary data.
So I decoded the object and tried to create an objectUrl from it:
var bin = atob(pkcs12);
var blob = new Blob([bin],
{ type : 'application/x-pkcs12' });
$scope.pkcs12Blob = (window.URL || window.webkitURL).createObjectURL( blob );
The problem is, that the downloaded file is bigger than the original binary data and is not recognized as pkcs12. It looks like as if some utf-8/unicode stuff was introduced into the file.
If I provide the original base64 encoded data to the createObjectURL and download the base64 encoded file, I can decode the downloaded file and get a valid p12 file.
So I am wondering: How does createObjectURL work for binary data?
For some reason createObjectURL does not accept a binary string but requires a byte array. This code worked like a charm:
var bytechars = atob($scope.enrolledToken.pkcs12);
var byteNumbers = new Array(bytechars.length);
for (var i = 0; i < bytechars.length; i++) {
byteNumbers[i] = bytechars.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var blob = new Blob([byteArray], {type: 'application/x-pkcs12'});
$scope.pkcs12Blob = (window.URL || window.webkitURL).createObjectURL( blob );
I want to process an image and make it secure by adding a watermark to it using transloadit. But the source of image I have is base64 encoded string rather than asking user to upload it to a form. Can I pass this base64 encoded string to form and process it to get watermark on it?
Any help is appreciated. Thanks!
It seems like you need to convert the base64 data uri to a Blob object, then manually set the blob as a parameter in FormData, as suggested in this SO question
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});
}
Then set it using:
var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
credit to #Stoive for the code
The server saves the audio data as base64 data string. The mobile web client fetches the data and plays the audio.
But found an issue in mobile Chrome in iOS and android that the audio with data uri can't play (issue).
To make it work, I was wondering if there is a way in the client side to convert the data string to an audio file (like .m4a) and link the audio src to the file?
Figured out directly using the web audio api has the best compatibility across the mobile browsers in iOS and Android.
function base64ToArrayBuffer(base64) {
var binaryString = window.atob(base64);
var len = binaryString.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
var base64 = '<data string retrieved from server>';
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
var source = audioContext.createBufferSource();
audioContext.decodeAudioData(base64ToArrayBuffer(base64), function(buffer) {
source.buffer = buffer;
source.connect(audioContext.destination);
source.start(0);
});
It works in iOS safari, Chrome and Android default browser and Chrome.
There is a way to do kind of what you want, it works on desktop, but I cannot guarantee it works on mobile. The idea is to convert the dataURI to ArrayBuffer, construct a Blob from it and then make a ObjectURL with it, to pass to the audio element. Here is the code (I tested it in Chrome/Firefox under Linux and it works):
<script>
var base64audio = "data:audio/ogg;base64,gibberish";
function dataURItoBlob(dataURI)
{
// Split the input to get the mime-type and the data itself
dataURI = dataURI.split( ',' );
// First part contains data:audio/ogg;base64 from which we only need audio/ogg
var type = dataURI[ 0 ].split( ':' )[ 1 ].split( ';' )[ 0 ];
// Second part is the data itself and we decode it
var byteString = atob( dataURI[ 1 ] );
var byteStringLen = byteString.length;
// Create ArrayBuffer with the byte string and set the length to it
var ab = new ArrayBuffer( byteStringLen );
// Create a typed array out of the array buffer representing each character from as a 8-bit unsigned integer
var intArray = new Uint8Array( ab );
for ( var i = 0; i < byteStringLen; i++ )
{
intArray[ i ] = byteString.charCodeAt( i );
}
return new Blob( [ intArray ], {type: type} );
}
document.addEventListener( 'DOMContentLoaded', function()
{
// Construct an URL from the Blob. This URL will remain valid until user closes the tab or you revoke it
// Make sure at some point (when you don't need the audio anymore) to do URL.revokeObjectURL() with the constructed URL
var objectURL = URL.createObjectURL(dataURItoBlob(base64audio));
// Pass the URL to the audio element and load it
var audio = document.getElementById( 'test' );
audio.src = objectURL;
audio.load();
} );
</script>
...
<audio id="test" controls />
I hope that helps ;)
I have a bunch of hex values and I have to convert it into binary data before write them into a file.
I trasformed the hex string in an array of integers, then I convert each integer to a char:
// bytes contains the integers
str = String.fromCharCode.apply(String, bytes);
now I create the blob file and download it:
var blob = new Blob([str], {type: "application/octet-stream"});
saveAs(blob, "file.bin");
but something goes wrong: if I print the length of bytes and the length of str I have the same value (512), but the file contains 684 chars, and of course it isn't how I expect it.
So I have:
512 pairs of hex values ->
512 integers ->
512 chars ->
I save the file ->
684 chars inside the file.
What am I doing wrong? I even tried to add the charset to the blob file, ie:
var blob = new Blob([str], {type: "application/octet-stream;charset=UTF-8,"});
but with no success.
EDIT:
Original HEX:
Saved file:
Thanks to Andrey I found the solution:
I have to write in binary mode, so:
var ab = new ArrayBuffer(bytes.length); //bytes is the array with the integer
var ia = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
ia[i] = bytes[i];
}
var blob = new Blob([ia], {type: "application/octet-stream"});
saveAs(blob, id + "_<?php echo $report['md5']; ?>.bin");