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.
Related
So I have some code that will take a blob and attempt to print it. This works fine the first time you click print in Safari. If you cancel the printing prompt, subsequent requests to print will show a white screen with infinite pages.
I was wondering if this is a core issue with safari, as this happens printing the document directly through JavaScript. Or if there's something I'm missing.
This only happens if you cancel a print. If you continue and finish printing, everything will work fine again until you click cancel on a print job initiated by JavaScript.
let printPdf = function (b64) {
const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize),
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 });
return blob;
}
const contentType = "application/pdf",
blob = b64toBlob(b64, contentType),
blobUrl = URL.createObjectURL(blob);
console.dir('new print');
var iframe = null;
iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.style.display = 'none';
iframe.onload = function() {
setTimeout(function() {
iframe.focus();
iframe.contentWindow.print();
}, 500);
};
iframe.src = blobUrl;
}
Here's a screenshot of the infinite printing window.
This question already has an answer here:
Problems downloading big file(max 15 mb) on google chrome
(1 answer)
Closed 3 years ago.
function openNewWindow(strPreviewId) {
let newWindowViewer = window.open("");
var index = mapPreviewIdWithFile[strPreviewId].indexOf('base64,');
var attachBody = mapPreviewIdWithFile[strPreviewId].substring(index + 7);
var attachmentMimeType = mapPreviewIdWithFile[strPreviewId].substring(0, index + 7);
newWindowViewer.document.write("<iframe width='100%' height='100%' src='" + attachmentMimeType + " " + encodeURI(attachBody) + "'></iframe>");
}
finally the iframe gets created like below:
<iframe width="100%" height="100%" src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAA9AAAALaCAYAAADZQrs6AAAAAXNSR0IArs4..."></iframe>
For files like more than 10MB the attachBody changes to 26 MB, thus it fails to render in the new window. Is there any other library or workaorund which handles large files.
UPDATE
I was trying with canvas, but with canvas the full inage is bnot being shown, only a part is being shown.
if (attachmentMimeType.indexOf('bmp') > -1 || attachmentMimeType.indexOf('gif') > -1 || attachmentMimeType.indexOf('jpeg') > -1 ||
attachmentMimeType.indexOf('jpg') > -1 || attachmentMimeType.indexOf('pdf') > -1 || attachmentMimeType.indexOf('png') > -1) {
newWindowViewer.document.write('<canvas id="c"></canvas>');
var canvas = newWindowViewer.document.getElementById("c");
var ctx = canvas.getContext("2d");
var image = new Image();
image.onload = function() {
ctx.drawImage(image, 0, 0, image.height, image.width);
};
image.src = attachmentMimeType + " " + encodeURI(attachBody);
} else {
newWindowViewer.document.write("<iframe width='100%' height='100%' src='" + attachmentMimeType + " " + encodeURI(attachBody) + "'></iframe>");
}
The issue which you are facing is most probably because of the character length limit which is set for your brower. URLs over 2,000 characters will not work in the most popular web browsers. This SO answer explains it very well.
Alternatively you can try using IMG instead of IFRAME.
let attachBody = ""; // Add base64 string
let newWindowViewer = window.open("");
newWindowViewer.document.write("<img src=\""+ attachBody +"\" >");
Working demo fiddle here. Which is having a base64 encoded file(5.7M) opening properly in a new window.
Update 1
For pdf file you try by injecting blob data in a script tag. A sample script below.
printPreview = (data, type = 'application/pdf') => {
let blob = null;
blob = this.b64toBlob(data, type);
const blobURL = URL.createObjectURL(blob);
const newWindowViewer = window.open(blobURL);
const theDoc = newWindowViewer.document;
const theScript = theDoc.createElement('script');
function injectThis() {
window.print();
}
theScript.innerHTML = `window.onload = ${injectThis.toString()};`;
theDoc.body.appendChild(theScript);
};
b64toBlob = (content, contentType) => {
contentType = contentType || '';
const sliceSize = 512;
// method which converts base64 to binary
const byteCharacters = window.atob(content);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
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
}); // statement which creates the blob
return blob;
};
However, I've not tested it.
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'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 });
}