Replies: 6 comments 4 replies
-
How can I reproduce this issue? |
Beta Was this translation helpful? Give feedback.
-
Because you used the Worker thread when decompressing. In Ios16, Worker cannot continue to be created in Worker. Of course, it's just a small problem, and it doesn't matter if it can't be solved. 因为你解压的时候用了Worker线程。在Ios16里,Worker中不能继续创建Worker。当然只是小问题,不能解决也没关系。 reproduce: self.Worker === undefined |
Beta Was this translation helpful? Give feedback.
-
can run in PC edge/Chromium,In mobile IOS16 can't run import './assets/js/lib/MyWorker.js';
let btn = document.querySelector('#upload');
btn.onchange = async e=>{
let files = e.target.files[0];
const worker = new MyWorker({url:'./assets/js/Worker/WorkerAppZip.js'});
let result = await worker.postMethod('unpack',files);
console.log(result);
document.querySelector('#result').innerHTML = Array.from(result.keys).join(',');
}; MyWorker.jsif (self.Worker) {
class MyWorker extends Worker {
constructor(options) {
const {
url,
type,
name
} = options;
super(url, {
type,
name
});
if (options.install === true) {
this.ready = new Promise(back => {
this.addFeedback('complete', back);
});
}
this.addMessage();
}
methods = new Map();
feedback = new Map();
isFeedback(method) {
return this.feedback.has(method);
}
callFeedback(method, ...arg) {
if (this.isFeedback(method)) {
return this.feedback.get(method).apply(this, arg);
}
}
isMethod(method) {
this.methods.has(method);
}
callMethod(method, ...arg) {
if (this.isMethod(method)) {
return this.methods.get(method).apply(this, arg);
}
}
set(name, func) {
return this.methods.set(name, func), this;
}
uuid() {
if (self.crypto && self.crypto.randomUUID) {
return self.crypto.randomUUID();
}
return btoa(performance.now() + Math.random());
}
addFeedback(id, back, error) {
this.feedback.set(id, function (data) {
if (data.error && error) return error(data.error);
back(data.result);
this.feedback.delete(data.id);
});
}
async getFeedback(result, transf) {
return new Promise((back, error) => {
const id = this.uuid();
this.addFeedback(id, back, error);
result.id = id;
this.postMessage(result, transf);
});
}
async postMethod(method, result, transf) {
return this.getFeedback({
method,
result
}, transf);
}
async getMethod() {
const methods = await this.postMethod('getMethod');
methods && methods.forEach(method => {
if (method == 'constructor') return;
this.methods.set(
method,
new Function('return this.postMethod("' + method + '",Array.from(arguments))')
);
});
}
async addMessage() {
this.addEventListener('message', async function (event) {
const data = event.data;
const port = event.source || event.target;
if (data && data.constructor === Object) {
if (this.isMethod(data.method)) {
return this.callMethod(data.method, data, port);
}
if (this.isFeedback(data.id)) {
return this.callFeedback(data.id, data, port);
}
if (data.method == 'zip_password') {
let password = data.result || '';
if (self.prompt) {
password = self.prompt('输入密码', password);
} else if (self.postMessage) {
password = await this.getMethod('zip_password', password);
}
return port.postMessage({
id: data.id,
result: password === null ? false : password
});
}
}
if (data === 'complete' && this.isFeedback(data)) {
return this.callFeedback(data, {
id: data,
result: this
});
}
if (this.callMessage instanceof Function) return this.callMessage(data, port);
});
this.addEventListener('error', function (event) {
if (self.alert) alert(event.message);
});
}
}
Object.defineProperty(self, 'MyWorker', {
get: () => MyWorker
});
} WorkerAppZipimportScripts('https://registry.npmmirror.com/@zip.js/zip.js/2.7.42/files/dist/zip.min.js');
class WorkerApp {
feedback = new Map;
methods = new Map;
shareports = [];
cache_name = 'cache-worker';
idb_name = 'worker-datas';
idb_table = 'files';
constructor(name) {
if(name)this.idb_table = name;
this.onRun();
}
isMethod(method) {
return this.methods && this.methods.has(method);
}
callMethod(method, ...arg) {
if (this.isMethod(method)) {
return this.methods.get(method).apply(this, arg);
}
}
isFeedback(method) {
return this.feedback && this.feedback.has(method);
}
async callFeedback(method, ...arg) {
if (this.isFeedback(method)) {
let result = this.feedback.get(method).apply(this, arg);
if (result instanceof Promise) {
result = await result;
}
return result;
}
}
callFunc(method, ...arg) {
const func = this.functions.get(method);
if (func instanceof Function) {
return func.apply(this, arg);
}
return func;
}
onRun() {
if (self.postMessage) {
self.addEventListener('message', e => this.onMessage(e));
this.callFunc('onInitialized',()=>this.callFunc('complete',self));
} else {
self.addEventListener('connect',e => this.callFunc('set_share_port', e));
}
}
async getMessage(port, result, method) {
return await this.getFeedback(port, { result, method });
}
addFeedback(id, back, error) {
this.feedback.set(id, function (data) {
this.feedback.delete(data.id);
if (data.error && error) return error(data.error);
back(data.result);
});
}
async getFeedback(port, result, transf) {
return new Promise((back, error) => {
const id = this.callFunc('uuid');
this.addFeedback(id, back, error);
result.id = id;
port.postMessage(result, transf);
});
}
async onMessage(e) {
const data = e.data;
const port = e.source || e.target;
if (data && data.constructor === Object) {
const method = data.method;
if (this.isMethod(method)) {
const result = await this.callMethod(method, data, port);
const transf = [];
if (result !== undefined) {
if (result.byteLength) {
transf.push(result.buffer || result);
}
if (data.id) {
port.postMessage({ id: data.id, result }, transf);
}
}
return;
}
if (this.isFeedback(data.id)) {
return this.callFeedback(data.id, data, port);
}
}
if (this.callMessage instanceof Function) return this.callMessage(data, port);
}
functions = new Map(Object.entries({
uuid() {
return self.crypto&&self.crypto.randomUUID&&self.crypto.randomUUID()||btoa(performance.now()+Math.random());
},
async set_share_port(e, fn) {
e.source.onmessage = e => this.onMessage(e);
this.callFunc('onInitialized',()=>this.callFunc('complete',e.source));
},
complete(port){
port.postMessage('complete');
}
}));
}
Object.defineProperty(self,'WorkerApp',{get:()=>WorkerApp});
new class WorkerAppZip extends WorkerApp {
/**
* 创建GB2312编码集
* @returns
*/
GB_byte() {
let ranges = [
[0xA1, 0xA9, 0xA1, 0xFE],
[0xB0, 0xF7, 0xA1, 0xFE],
[0x81, 0xA0, 0x40, 0xFE],
[0xAA, 0xFE, 0x40, 0xA0],
[0xA8, 0xA9, 0x40, 0xA0],
[0xAA, 0xAF, 0xA1, 0xFE],
[0xF8, 0xFE, 0xA1, 0xFE],
[0xA1, 0xA7, 0x40, 0xA0],
];
let codes = new Uint16Array(23940);
let i = 0;
for (let [b1Begin, b1End, b2Begin, b2End] of ranges) {
for (let b2 = b2Begin; b2 <= b2End; b2++) {
if (b2 !== 0x7F) {
for (let b1 = b1Begin; b1 <= b1End; b1++) {
codes[i++] = b2 << 8 | b1;
}
}
}
}
let table = new Uint16Array(65536);
let gbkstr = new TextDecoder('gbk').decode(codes.buffer);
for (let i = 0; i < gbkstr.length; i++) {
table[gbkstr.codePointAt(i)] = codes[i];
}
gbkstr = null;
codes = null;
return table;
}
/**
* 把字符变为GB2312编码二进制
* @param {*} str
* @returns
*/
GB_encode(str) {
if (!this.gb2312) {
this.gb2312 = this.GB_byte();
}
let buf = [];
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);
if (code < 0x80) {
buf.push(code);
continue;
}
const gbk = this.gb2312.at(code);
if (gbk) {
buf.push(gbk, gbk >> 8);
} else if (code === 8364) {
buf.push(0x80);
} else {
buf.push(63);
}
}
return new Uint8Array(buf);
}
async get_data(entry,port){
const notUTF8 = entry.filenameUTF8 == false;
let rawPassword;
let password = this.password;
if (entry.encrypted) {
if (password) {
if (password instanceof ArrayBuffer||password instanceof Uint8Array){
password = new Uint8Array(password);
rawPassword = password;
}else{
rawPassword = notUTF8 ? this.GB_encode(password):new TextEncoder().encode(password)
}
}
}
let options = {
rawPassword,
onprogress: (current, total) => port.postMessage({method:'zip_progress', current, total, file: entry.filename })
};
return entry.getData(new zip.Uint8ArrayWriter(),options).catch(async e=>{
let msg = e.message;
if (password === false) return;
if (msg == zip.ERR_INVALID_PASSWORD || msg == zip.ERR_ENCRYPTED) {
if (password instanceof Uint8Array) password = new TextDecoder('gbk').decode(password);
password = await this.getFeedback(port, { method: 'zip_password', result: password || '' });
if (password) {
if (notUTF8) password = this.GB_encode(password);
this.password = password;
return await this.get_data(entry,port);
} else {
this.password = false;
}
}
});
}
async unpack(data, port) {
let { result, password, id, encode, each,close} = data;
this.password = password;
const ReaderList = await new zip.ZipReader(new zip.BlobReader(result instanceof Blob ? result : new Blob([result]))).getEntries({
decodeText(buf, encoding) {
let text = new TextDecoder('utf-8').decode(buf);
let newbuf = new TextEncoder().encode(text);
return newbuf.byteLength > buf.byteLength ? new TextDecoder('gb18030').decode(buf) : text;
}
}).catch(e => null);
if (ReaderList&&ReaderList.length) {
let newresult = [];
let buffers = [];
for (let entry of ReaderList) {
if (entry.directory) continue;
let data = await this.get_data(entry,port);
if (data) {
if(!each){
newresult.push([entry.filename,data]);
buffers.push(data.buffer);
}else{
port.postMessage({
method:'zip_file',
result: data,
file: entry.filename,
}, [data.buffer]);
}
}
}
if(newresult.length){
port.postMessage({
result: new Map(newresult),
ready: true,
id
}, buffers);
}
}else{
port.postMessage({
result: false,
ready: true,
id
});
}
if(close)port.close();
}
methods = new Map(
Object.entries({
async unpack(data, port) {
return this.unpack(data,port);
},
getMethod() {
return ['upack', 'toblob', 'open', 'add', 'close'];
}
})
);
} |
Beta Was this translation helpful? Give feedback.
-
Thank you very much. Unfortunately, I cannot reproduce the issue. I created a project on Plunker with your code, see https://plnkr.co/edit/o1tb8lkC6GuU1iT8?preview. When I open the demo with Safari in the Simulator, the demo runs as expected (i.e. the logs are OK). Do I need a real iOS device in order to reproduce the issue? I'm running the test in iOS 17, is this issue specific to iOS 16? |
Beta Was this translation helpful? Give feedback.
-
I've tested https://plnkr.co/edit/o1tb8lkC6GuU1iT8?preview with the Simulator on iOS 16.4. Unfortunately, I was not able to reproduce the issue. Everything works fine and the size of the decompressed entry displayed in the console is correct. You run the OS in stealth mode maybe? Maybe I missed something else, what is the popup in your screenshot (I do not speak Chinese)? Should I do the test with a password-protected zip file? |
Beta Was this translation helpful? Give feedback.
-
zip.min.js
run in Worker in IOS will not decode filedataBecause the IOS
Worker
cannot run the secondaryWorker
, the file is not decompressed, and it is still the original RAW (garbled characters) - -!Beta Was this translation helpful? Give feedback.
All reactions