I'm using flash to capture audio, encode it to mp3, then send it to javascript as ByteArray.
Now I want the javascript to save it as MP3 on my computer (not flash saves it to my computer). I am using Blob and then getDataURL, but the file isn't playing when saved. I used to do the same exact method to save WAV files and it worked perfectly fine.
Here's the JS code:
var getDataFromSWF = function (bytes) {
var myBlob = new Blob([bytes], { type: "audio/mpeg3" });
var url = (window.URL || window.webkitURL).createObjectURL(myBlob);
var link = window.document.createElement('a');
link.href = url;
// $("label").text(url);
link.download = 'output.mp3';
var click = document.createEvent("Event");
click.initEvent("click", true, true);
link.dispatchEvent(click);
// console.log(bytes);
}
I'm pretty much sure that the byteArray is fine because if I let the SWF save the file it works OK too. But I want to know what's wrong with the JS code. (note: i'm new to BLOB)
Try this to get the Blob
function base64toBlob(base64Data, contentType) {
var sliceSize = 1024;
var byteCharacters = atob(base64Data);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0 ; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, { type: contentType });
}
Related
I'm making an offline PWA installer using Blob, but when I share and add to home screen on IOS, the PWA appears completely black. Here is my code:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
function install(){
var autodate, enapp, data;
// Getting current PWA
$("#autodate").load("https://mysite.github.io/pwa/app1.html");
autodate = document.getElementById('autodate');
setTimeout(() => {
// Encode
encapp = btoa(autodate.innerHTML);
// Blob
blobt('text/html', 'data:text/html;base64,'+encapp)
}, 300);
};
function blobt(type, data){
const base64data = data;
const contentType = type;
const byteCharacters = atob(base64data.substr(`data:${contentType};base64,`.length));
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += 1024) {
const slice = byteCharacters.slice(offset, offset + 1024);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, {type: contentType});
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, '_blank');
}
</script>
I don't see what would make it not work. It does everything perfectly in-browser, but once you install it to home screen, it just blacks out. Am I doing something wrong? Is there a solution to this?
Thanks in advance.
I'm using a webservice to get a base64 string and I need to show that document to the user as a PDF.
var charactersArray = atob(base64String);
var byteNumbers = new ArrayBuffer(charactersArray.length);
for (var i = 0; i < charactersArray.length; i++) {
byteNumbers[i] = charactersArray.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var file = new File([byteArray], "file.pdf", {
type: "application/pdf",
});
I'm then using this "file" to create a url with
var url = URL.createObjectURL(file);
I'm opening this url in a button with the ng-click directive, but I'm getting loading the PDF.
You need to write the character codes to the byteArray rather than the ArrayBuffer
var charactersArray = atob(base64String);
var len = charactersArray.length;
var byteNumbers = new ArrayBuffer(len);
var byteArray = new Uint8Array(byteNumbers);
for (var i = 0; i < len; i++) {
byteArray[i] = charactersArray.charCodeAt(i);
}
var file = new File([byteArray], "file.pdf", {
type: "application/pdf",
});
I recently work on a project like this and had the same issue. I used the base64-arraybuffer NPM library to convert a base64 string to a byte array.
It's a JS library so it needs to be imported like this after it's installed:
import * as buffer from 'base64-arraybuffer';
The object URL is created like this:
var byteArray = buffer.decode(base64String);
var file = new Blob([byteArray], {type: 'application/pdf'});
var pdfUrl = URL.createObjectURL(file);
I hope this helps!
For IE11 in this code base64 file is converted to Blob, and then a download link is created. But with a large base64 file (~ >5Mb), the browser hangs at the moment when Blob is created:
new Blob(byteArrays, {type: contentType});
How is it possible to solve this problem?
var fullFileName = 'example.test';
var b64file = '';
var contentType = 'application/octet-stream';
b64toBlob(b64file, contentType, 512, function(blob){
if (typeof MouseEvent != "function") { //for IE
$('#ie_download').off('click').on('click', function(){
window.navigator.msSaveBlob(blob, fullFileName);
})
.show();
success();
return;
}
//other browsers
var blobUrl = URL.createObjectURL(blob);
var jqLink = $('<a style="display: none" target="_blank">Save</a>');
$('#download')
.attr('download', fullFileName)
.attr('href', blobUrl)
.show();
success();
});
function success () {
$('#waiting').hide();
}
function b64toBlob(b64Data, contentType, sliceSize, resultCb) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
var offset = 0;
setTimeout(function generateByteArrays () {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
offset += sliceSize;
if (offset < byteCharacters.length) {
setTimeout(generateByteArrays, 0);
}
else {
resultCb(new Blob(byteArrays, {type: contentType}));
}
}, 0);
}
#download, #ie_download {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='waiting'>waiting...</div>
<a id='download'>Save</a>
<a id='ie_download'>IE Save</a>
Update
I just noticed that the segment size is 512 bytes. This is extremely small and will with a 5 mb file create 10,240 array slices which IE seem to do a very slow operation with (ie. create buffer, copy content, check next slice, create new buffer of old size and next slice's size, copy old buffer + new content etc.).
You should be able to use at least 1000x larger slice size (0.5 mb) and with that not block IE11.
Demo using original code with a larger slice size:
setTimeout(test, 10);
function test() {
// Generate a big base-64 string, chop off data-uri
// NOTE: Initial creation will take a couple of seconds...
var c = document.createElement("canvas"); c.width = c.height = 6000;
var ctx = c.getContext("2d"); // create some lines to degrade compression ratio...
for(var i = 0, r = Math.random.bind(Math), w = c.width, h = c.height; i < 500; i++) {
ctx.moveTo(r()*w, r()*h);ctx.lineTo(r()*w, r()*h);
}
ctx.stroke();
var base64 = c.toDataURL();
base64 = base64.substr(base64.indexOf(",")+1);
// OK, now we have a raw base64 string we can use to test
document.querySelector("out").innerHTML = "Converting...";
// Increase sliceSize by x1024
b64toBlob(base64, "application/octet-stream", 512<<10, function(blob) {
document.querySelector("out").innerHTML = "Blob size: " + blob.size;
});
function b64toBlob(b64Data, contentType, sliceSize, resultCb) {
contentType = contentType || '';
sliceSize = sliceSize || (512<<10);
var byteCharacters = atob(b64Data);
var byteArrays = [];
var offset = 0;
setTimeout(function generateByteArrays () {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
offset += sliceSize;
if (offset < byteCharacters.length) {
setTimeout(generateByteArrays, 5);
}
else {
resultCb(new Blob(byteArrays, {type: contentType}));
}
}, 5);
}
}
<out>Creating test data...</out>
Due to a bug in IE11 one cannot use XMLHttpRequest() with a data-uri and response type "blob", otherwise you could have used that to do all these operations for you.
var c = document.createElement("canvas"); c.width = c.height = 4000;
var ctx = c.getContext("2d"); // create some lines to degrade compression ratio...
for(var i = 0, r = Math.random.bind(Math), w = c.width, h = c.height; i < 200; i++) {
ctx.moveTo(r()*w, r()*h);ctx.lineTo(r()*w, r()*h);
}
ctx.stroke();
var base64 = c.toDataURL();
base64 = base64.substr(base64.indexOf(",")+1);
b64toBlob(base64, "application/octet-stream", function(blob) {
console.log(blob)
})
// Using XMLHttpRequest to do the work (won't work in IE11...)
function b64toBlob(base64, mimeType, callback) {
var xhr = new XMLHttpRequest();
xhr.responseType = "blob";
xhr.onload = function() {
callback(this.response)
};
xhr.open("GET", "data:" + mimeType + ";base64," + base64);
xhr.send();
}
Old answer (still applicable/recommended)
Increase the timeout to something like 7-10ms and see if that unblock the loop (or use even higher value if still blocking).
A timeout of 0 is in effect beating the purpose of this asynchronous segmentation approach.
There must be a way to do this more efficiently. What I'm doing is conceptually very simple:
1) Call a web service and receive b64-encoded-string of a PDF blob.
2) Decode it, create blob, render PDF in new window. Account for pop-up blocker.
My code works. Nothing fancy. Its all client-side. Everything works but IE runs SUPER slow compared to the other browsers (IE 11 vs. current Chrome/Firefox/Safari).
In light of this I am certain I could do this more efficiently. Any tips on how to speed this up for IE 11?
Note: I'm using Jeremy's b64toBlob function (thanks Jeremy).
Part I: modal stuff
var box = new SimpleDialog(Dialogs.getNextId(), false);
box.title = "Fetching PDF";
box.isMovable = false;
box.extraClass = "";
box.width = 400;
box.isModal = true;
box.createDialog();
window.parent.box = box;
box.setContentInnerHTML('<p>Please wait....</p>');
box.show();
Part II: call external service, receive b64 encoded string
setTimeout(function(){
var response = ... ; //do callout... get data
var statusCode = ...; //parse from response
var b64Data = ... ; //parse from response
if(statusCode == 200) {
//Account for IE
if (navigator.appVersion.toString().indexOf('.NET') > 0) {
var blob = b64toBlob(b64Data, "application/pdf");
var fileURL = URL.createObjectURL(blob);
window.navigator.msSaveOrOpenBlob(blob, "theFile.pdf");
window.parent.box.cancel();
} else {
var blob = b64toBlob(b64Data, "application/pdf");
var fileURL = URL.createObjectURL(blob);
var pdfWin = window.open(fileURL,"_blank","width=1000,height=800");
if(!pdfWin) {
box.setTitle("Success: PDF has been retrieved");
box.setContentInnerHTML("<p align='left'></p><p align='left'>A popup blocker was detected. The PDF will not open automatically.<br /><br /></p><p align='left'><a onclick='window.parent.box.cancel();' target='_blank' href='"+fileURL +"' >Click here to view .pdf</a><br /><br /></p><p align='center'><button class='btn' onclick='window.parent.box.cancel(); return false;'>Cancel</button></p>");
} else {
window.parent.box.cancel();
}
}
} else {
box.setTitle("Error fetching PDF");
box.setContentInnerHTML("<p align='left'><img src='/img/msg_icons/warning32.png' style='margin:0 5px;'/></p><p align='left'>Unable to retrieve PDF.</p><p align='center'><button class='btn' onclick='window.parent.box.cancel(); return false;'>OK</button></p>");
}
},200);
I don't really see any slowness, and this plunkr run in IE, (using an update on the original "Jeremy" solution) works just fine:
Sample pdf
There was an update in the original post that improves the answer further:
function base64toBlob(base64Data, contentType, sliceSize) {
var byteCharacters,
byteArray,
byteNumbers,
blobData,
blob;
contentType = contentType || '';
byteCharacters = atob(base64Data);
// Get blob data sliced or not
blobData = sliceSize ? getBlobDataSliced() : getBlobDataAtOnce();
blob = new Blob(blobData, { type: contentType });
return blob;
/*
* Get blob data in one slice.
* => Fast in IE on new Blob(...)
*/
function getBlobDataAtOnce() {
byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
byteArray = new Uint8Array(byteNumbers);
return [byteArray];
}
/*
* Get blob data in multiple slices.
* => Slow in IE on new Blob(...)
*/
function getBlobDataSliced() {
var slice,
byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
slice = byteCharacters.slice(offset, offset + sliceSize);
byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
byteArray = new Uint8Array(byteNumbers);
// Add slice
byteArrays.push(byteArray);
}
return byteArrays;
}
}
From the answer here:
martinoss answer
Is the plunkr slow for you? Can you put in some logging to understand which call is actually slow? Put in a timer and log each line. on the IE route. Which one is reporting "slowness"?
Update On the plunkr, I've put a very simple timer, it shows that there is just 46ms approx taken to get the PDF to you in IE11. Obviously it's not multithreaded, but it is an indication.
I have got base64data from ng-src, but I can't make a jpeg file with this base64data. I have ever been trying as follow:
First I have removed the data:image/jpeg:base64, chunk from data:image/jpeg:base64,/9/DEefesdfae453/sdDFEYEUdfdief8fe ... and write this data with test.jpg file by use of $cordovaFile. Here is my code:
var base64Image = angular.element(document.querySelectorAll('#my-photo')).attr('src');
var writeData = base64Image.replace(/data:image\/jpeg;base64,/g, '');
alert(writeData);
// Writing filtered image -----------------------------------------------------------------
$cordovaFile.writeFile(ROOT_DIR + PM_DATA_DIR_NAME + '/', $scope.photo.name, writeData, true)
.then(function (success) {
// success
$ionicLoading.hide();
$ionicPopup.alert({
template : "Updated Photo!!"
});
}, function (error) {
// error
$ionicLoading.hide();
$ionicPopup.alert({
template : "Failed to update Photo!!"
});
});
But I can't show the file as image file. It is showing only black screen. (original file size : 19K ---> result file size : 66K)
What's wrong?
Please help me.
Thank you everybody to help.
FileWriter’s write method does not take a base64 string. According to the docs (http://docs.phonegap.com/en/edge/cordova_file_file.md.html#FileWriter) text will be encoded as UTF-8 before being written. So your base64 string is being encoded before writing to the file so it’s not valid image data. You have to pass your image data as a Blob or an ArrayBuffer. Note this only works on iOS and Android. Have a look at Jeremy Banks’ b64toBlob function in this answer: https://stackoverflow.com/a/16245768
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
You can pass the resulting blob into the write method.