Ich erstelle eine App, die unter anderem die Möglichkeit bietet, Dateien auf eine vorhandene API hochzuladen. Diese API übernimmt sowohl Metadaten als auch Inhalte von Dateien in einem JSON-Objekt. Daher muss ich den binären Inhalt der Dateien in base64-codierte Strings konvertieren.Web-Mitarbeiter nicht genügend Arbeitsspeicher bei der Verarbeitung großer Arrays
Da dies eine potenziell schwere Operation ist, habe ich die Funktionalität in einen Web-Worker verschoben. Der Worker nimmt ein Objekt ArrayBuffer
mit dem binären Dateiinhalt (zurückgegeben von FileReader.readAsArrayBuffer()
) auf und gibt eine Base64-codierte Zeichenfolge zurück.
Dies funktioniert gut für kleinere Dateien, aber für die größten Dateien, die ich unterstützen muss (~ 40 MB) führt dies zu Speicherausnahmen für meinen Worker (8007000E in Internet Explorer). In seltenen Fällen passiert es, aber meistens stirbt der Arbeiter. Das gleiche geschah vor dem Verschieben in den Worker, außer dass die gesamte Browserseite abstürzte (sowohl in IE als auch in Chrome). Chrome scheint etwas widerstandsfähiger gegen die Speicherbelastung bei Arbeitern zu sein als IE, aber ich muss es trotzdem im IE (10+) richtig machen.
Mein Arbeiter:
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);
}
};
Bin ich einige große No-nos hier machen? Gibt es Möglichkeiten, den Speicherverbrauch zu reduzieren? Eine Lösung, über die ich nachgedacht habe, wäre, den Inhalt in Chunks anstatt der gesamten Datei zu verarbeiten, dann die resultierenden Strings zu verketten und außen zu codieren. Wäre das machbar oder würde das eigene Probleme verursachen? Gibt es noch andere magische Funktionen, von denen ich nichts weiß? Ich hatte einen Hoffnungsschimmer mit FileReader.readAsBinaryString()
, aber es ist jetzt aus dem Standard entfernt (und in IE10 sowieso nicht unterstützt), so dass ich es nicht verwenden kann.
(Ich weiß, diese Frage zu im Code Review relevant sein könnte, aber da mein Code tatsächlich abstürzt, ich dachte, so war die richtige Stelle)
Nicht sicher, ob es zu einer Lösung für Ihr Problem verwendet ist, aber warum füllen 'chars' von jedem Ende beginnen und in der Mitte beenden? –
Es halbiert die Anzahl der Iterationen (von 40M bis 20M für eine 40MB-Datei), also war es ein Optimierungsversuch. Es hat die Größe erhöht, die es bewältigen konnte, bevor es ein wenig nach unten ging, aber es ist immer noch nicht genug für die größten Dateien. –