jszip
jszip copied to clipboard
getTypeOf() Can't infer datatype in Firefox Addon context
I'm trying to save a bunch of image blob (from canvas.toBlob) into a zip for an addon I'm working on.
But I keep getting the Can’t read the data of […]. Is it in a supported JavaScript type error.
Here's the code block I'm using
Promise.all(promises)
.then( blobs => {
blobs.forEach((blob, i) => {
console.log("Insert image", blob);
zip.file(`Frame ${i}.jpg`, blob, {base64: true});
});
console.log("Zipped Files", zip.files);
zip.generateAsync({type:"blob"})
.then( blob => {
console.log("Saving zip...")
saveAs(blob, "gMeetSlides.zip")
})
.catch( err => console.log(err));
})
In what seems like a repetition of #151 this is the error I get
Error: Can't read the data of 'Frame 0.jpg'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?
prepareContent moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/lib/jszip.js:3488
promise callback*[32]</exports.prepareContent moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/lib/jszip.js:3483
fileAdd moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/lib/jszip.js:1416
file moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/lib/jszip.js:1572
downloadZip moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/capture.js:121
downloadZip moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/capture.js:119
promise callback*downloadZip moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/capture.js:118
<anonymous> moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/capture.js:163
Which leads me to exports.getTypeOf. I added some console logs to it, and this is the result
exports.getTypeOf = function(input) {
console.log("Input", input);
console.log("Blob", input instanceof Blob);
console.log("Array", input instanceof ArrayBuffer);
2 calls, seems legit, there are 2 images being zipped. (in all trials)
Input ArrayBuffer { byteLength: 12666 } jszip.js:3361:17
Blob false jszip.js:3362:17
Array false jszip.js:3363:17
Input ArrayBuffer { byteLength: 12666 } jszip.js:3361:17
Blob false jszip.js:3362:17
Array false jszip.js:3363:17
Error: Can't read the data...
According to this stackoverflow comment it might be better to use input.constructor.name instead of instanceof. I using that in the function blocks, but while that doesn't throw an error, I don't get the download prompt either (not the console.logs in the callback function)
if (support.uint8array && input.constructor.name === "Uint8Array") {
return "uint8array";
}
if (support.arraybuffer && input.constructor.name === "ArrayBuffer") {
return "arraybuffer";
}
Multiple calls, each with a different type!?
Input ArrayBuffer { byteLength: 12664 } jszip.js:3361:17
Blob false jszip.js:3362:17
Array false jszip.js:3363:17
Input ArrayBuffer { byteLength: 12664 } jszip.js:3361:17
Blob false jszip.js:3362:17
Array false jszip.js:3363:17
Input Uint8Array(12664) [ 255, 216, … ] jszip.js:3361:17
Blob false jszip.js:3362:17
Array false jszip.js:3363:17
Input ArrayBuffer { byteLength: 12664 } jszip.js:3361:17
Blob false jszip.js:3362:17
Array false jszip.js:3363:17
Input ArrayBuffer { byteLength: 12664 } jszip.js:3361:17
Blob false jszip.js:3362:17
Array false jszip.js:3363:17
Input Uint8Array(12664) [ 255, 216, … ] jszip.js:3361:17
Blob false jszip.js:3362:17
Array false jszip.js:3362:17
I also tried some console.logs to checkout the return value, and the output is even more confusion
exports.getTypeOf = function(input) {
console.log("Input", input);
console.log("Blob", input instanceof Blob);
console.log("Array", input instanceof ArrayBuffer);
let _type = undefined;
if (typeof input === "string") {
_type = "string";
}
if (Object.prototype.toString.call(input) === "[object Array]") {
_type = "array";
}
if (support.nodebuffer && nodejsUtils.isBuffer(input)) {
_type = "nodebuffer";
}
if (support.uint8array && input.constructor.name === "Uint8Array") {
_type = "uint8array";
}
if (support.arraybuffer && input.constructor.name === "ArrayBuffer") {
_type = "arraybuffer";
}
console.log("Constr Type", input.constructor.name);
console.log("Return Type", type);
return _type;
};
Only 1 call to the function (a Blob?)
Input Blob { size: 12671, type: "image/jpeg" } jszip.js:3361:17
Blob true jszip.js:3362:17
Array false jszip.js:3363:17
Constr Type undefined jszip.js:3380:17
The addon works as expected on chrome though. Which is ironic, since I'm developing primarily with firefox in mind
This issue is easy to reproduce with a UserScript. Install Greasemonkey or Violentmonkey in Firefox. Then install this script:
// ==UserScript==
// @name test
// @version 1
// @include https://github.com/*
// @grant none
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.js
// ==/UserScript==
(async () => {
const zip = new JSZip();
const blob = await zip.generateAsync({type:'blob'});
try {
zip.file('test.txt', new Blob(['']));
}
catch(error) {
console.error(error);
}
try {
await JSZip.loadAsync(blob);
}
catch(error) {
console.error(error);
}
})();
Adding blob to a zip throws Error: Can't read the data of 'test.txt'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?. Loading zip from blob throws Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?.
Both can be fixed with either #578 or #598. But then loading zip from blob throws Error: Permission denied to access property "constructor". Caused by this line: https://github.com/Stuk/jszip/blob/053d8d5a132e0e89e32892619e7478aa3bf0e0a0/lib/reader/Uint8ArrayReader.js#L18
And I'm not sure how to fix that.
WORKAROUND
Since I don't know when it's going to be fixed and in my case those PRs don't solve my problem I'm using this workaround:
const readAsBinaryString = blob => new Promise(resolve => {
const reader = new FileReader();
reader.onload = function(event) {
resolve(event.target.result);
};
reader.readAsBinaryString(blob);
});
(async () => {
const zip = new JSZip();
const blob = await zip.generateAsync({type:'blob'});
zip.file('test.txt', await readAsBinaryString(new Blob([''])), {binary: true});
await JSZip.loadAsync(await readAsBinaryString(blob));
})();
And now it works.
Related issue https://github.com/greasemonkey/greasemonkey/issues/3120
Faced the same problem in Firefox web extension content script.
const encodedText = new TextEncoder().encode('some text'); // ok
const buffer = new Uint8Array(encodedText.buffer); // ok, but seems like some security flags for new object are missing
buffer.slice(); // Error: Permission denied to access property "constructor"
buffer.subarray(0, 2); // Error: Permission denied to access property "constructor"
Creating new ArrayBufferView from existing ArrayBuffer in web extension content script leads to Error: Permission denied to access property "constructor" on any method call for new object.
Created a bug report here: https://bugzilla.mozilla.org/show_bug.cgi?id=1868675