2016-03-23 23 views
2

Diğerlerinin yanı sıra varolan bir API'ye dosya yükleme olanağına sahip bir uygulama yapıyorum. Bu API, bir JSON nesnesindeki dosya meta verilerini ve içeriklerini alır, bu nedenle dosyaların ikili içeriğini base64 kodlu dizelere dönüştürmem gerekir.Web çalışanı büyük bir dizi işlerken bellekten çıktı

Bu potansiyel olarak ağır bir işlem olduğundan, işlevi bir web çalışanına taşıdım. Işçi, ikili dosya içeriği (FileReader.readAsArrayBuffer() döndürdü) ile bir ArrayBuffer nesnesini alır ve bir base64 kodlanmış dizesi döndürür.

Bu, daha küçük dosyalar için iyi çalışır, ancak en büyük dosyalar için (~ 40 MB) desteklemem gerekiyor, bu da çalışanım için bellek dışı istisnalara neden oluyor (Internet Explorer'da 8007000E). Nadir durumlarda, geçer, ama çoğu zaman işçi sadece ölür. Aynı şey işçiye taşınmadan önce gerçekleşti, ancak tüm tarayıcı sayfası çöktü (IE ve Chrome'da). Chrome, işçilerdeki bellek yükü için IE'ye göre biraz daha esnek görünüyor, ancak hala IE'de (10+) düzgün bir şekilde çalışmam gerekiyor.

Benim işçi:

onmessage = e => { 
    const bytes = new Uint8Array(e.data); 
    const l = bytes.length; 
    const chars = new Array(l); 
    for (let i = 0, j = l - 1; i <= j; ++i, --j) { 
    chars[i] = String.fromCharCode(bytes[i]); 
    chars[j] = String.fromCharCode(bytes[j]); 
    } 
    const byteString = chars.join(''); 
    const base64bytes = btoa(byteString); 

    try { 
    postMessage(base64bytes, [base64bytes]); 
    } catch (e) { 
    postMessage(base64bytes); 
    } 
}; 

Burada bazı büyük hayır-nos yapma AM? Bellek tüketimini azaltmanın herhangi bir yolu var mı? Düşündüğüm bir çözüm, içeriği tüm dosyadan ziyade parçalar halinde işlemek, sonra ortaya çıkan dizeleri birleştirmek ve onu dışarıda kodlamak olurdu. Bu uygun mu, yoksa kendi sorunlarına neden olacak mı? Bilmediğim başka sihirli işlevler var mı? Ben FileReader.readAsBinaryString() ile bir umut ışığı vardı, ama şimdi standart (ve IE10 zaten desteklenmez) kaldırıldı, bu yüzden kullanamazsınız.

(Bu soru çok Kod İnceleme de alakalı olabilir, ama benim kod aslında çökmesini olduğundan, ben doğru yeri SO düşündüm idi) Ben yaklaşık olacağını düşündüm

+0

emin değil eğer 'chars' her bir uçtan başlayıp ortada bitirmek? –

+0

Yineleme sayısını azalttığı için (40MB'den 40M'ye 40M'den 20M'ye kadar), optimizasyon denemesi yapıldı. Biraz gitmeden önce yapabileceği boyutu artırdı, ancak hala en büyük dosyalar için yeterli değil. –

cevap

0

bir çözüm İçeriği tüm dosyadan ziyade parçalar halinde işlemek, sonuçta elde edilen dizeleri birleştirmek ve onu dışarıda kodlamak. Bu uygun mu, yoksa kendi sorunlarına neden olacak mı?

https://github.com/beatgammit/base64-js'un yaptığı gibi, bir seferde ~ 16k yapıyor. Bunu kullanarak, benim bilgisayarımda aktarımlar kullanmadan (IE 10 onları desteklemiyor), Chrome bir 190mb ArrayBuffer'ı kodlamayı başarır (bundan daha büyük olanı, geçersiz dize uzunluğu hakkında şikayet eder) ve IE 11 40mb (bundan daha büyüktür. bellek istisnası dışı).

Sen etmek, işçi kodu

var exports = {}; 
importScripts('b64.js') 

onmessage = function(e) { 
    var base64Bytes = fromByteArray(new Uint8Array(e.data)); 
    postMessage(base64Bytes); 
}; 

ve

var worker = new Worker('worker.js'); 
var length = 1024 * 1024 * 40; 
worker.postMessage(new ArrayBuffer(length)); 

worker.onmessage = function(e) { 
    console.log('Received Base64 in UI thread', e.data.length, 'bytes'); 
} 

40mb sınırı ötesine için ana iş parçacığı vardır https://plnkr.co/edit/SShi1PE4DuMATcyqTRPx?p=preview en umut verici görünüyor tek yönlü bunu görebilirsiniz Sadece bir kerede işçiye daha küçük bir dilim geçirir (1mb), kodlayın, sonucu döndürün ve sadece bir sonraki dilimi işçiye aktarın ve sonuçta tüm sonuçları bir araya getirin. Bunu daha büyük tamponları kodlamak için kullanmayı başardım (IE 11'de 250MB'ye kadar). Şüphelerim, asenkronizasyonun çöp toplayıcısının çağırmalar arasında çalışmasına izin vermesidir.

yukarıdaki işçinin aynı kodla https://plnkr.co/edit/un7TXeHwYu8eBltfYAII?p=preview en Örneğin

, ancak UI iş parçacığı içinde: sizin sorununa çözüm ile ilgili ama neden dolduramayacak

var worker = new Worker('worker.js'); 
var length = 1024 * 1024 * 60; 
var buffer = new ArrayBuffer(length); 

var maxMessageLength = 1024 * 1024; 
var i = 0; 
function next() { 
    var end = Math.min(i + maxMessageLength, length); 
    var copy = buffer.slice(i, end); 
    worker.postMessage(copy); 
    i = end; 
} 

var results = []; 
worker.onmessage = function(e) { 
    results.push(e.data); 
    if (i < length) { 
    next(); 
    } else { 
    results = results.join(''); 
    alert('done ' + results.length); 
    } 
}; 

next(); 
İlgili konular