tipos llamar funciones español ejemplos desde codigo archivo javascript html html5 client-side filereader

llamar - Leyendo el archivo línea por línea en JavaScript en el lado del cliente



llamar javascript desde html (3)

¿Podría por favor ayudarme con el siguiente problema.

Gol

Lea el archivo en el lado del cliente (en el navegador a través de las clases JS y HTML5) línea por línea, sin cargar el archivo completo en la memoria.

Guión

Estoy trabajando en una página web que debería analizar los archivos en el lado del cliente. Actualmente, estoy leyendo el archivo como se describe en este article .

HTML:

<input type="file" id="files" name="files[]" />

JavaScript:

$("#files").on(''change'', function(evt){ // creating FileReader var reader = new FileReader(); // assigning handler reader.onloadend = function(evt) { lines = evt.target.result.split(//r?/n/); lines.forEach(function (line) { parseLine(...); }); }; // getting File instance var file = evt.target.files[0]; // start reading reader.readAsText(file); }

El problema es que FileReader lee el archivo completo a la vez, lo que provoca que la pestaña se bloquee para archivos grandes (tamaño> = 300 MB). El uso de reader.onprogress no resuelve un problema, ya que solo incrementa el resultado hasta que llega al límite.

Inventar una rueda

He investigado un poco en Internet y no he encontrado una forma sencilla de hacer esto (hay un montón de artículos que describen esta funcionalidad exacta pero en el lado del servidor para node.js).

Como única forma de resolverlo solo veo lo siguiente:

  1. Dividir el archivo por fragmentos (mediante el File.split(startByte, endByte) )
  2. Encuentre el último carácter de nueva línea en ese fragmento (''/ n'')
  3. Lea ese fragmento, excepto la parte después del último carácter de nueva línea y conviértalo a la cadena y divídalo por líneas
  4. Lea el siguiente fragmento a partir del último carácter de nueva línea que se encuentra en el paso 2

Pero mejor usaré algo que ya existe para evitar el crecimiento de la entropía.


Finalmente, he creado un nuevo lector línea por línea, que es totalmente diferente del anterior.

Las características son:

  • Acceso basado en índices al archivo (secuencial y aleatorio)
  • Optimizado para la lectura aleatoria repetida (hitos con compensación de bytes guardados para líneas que ya se navegaron en el pasado), por lo que después de haber leído todo el archivo una vez, acceder a la línea 43422145 será casi tan rápido como acceder a la línea 12.
  • Buscando en el archivo: encuentra el siguiente y encuentra todo .
  • Índice exacto, desplazamiento y longitud de las coincidencias, para que pueda resaltarlas fácilmente

Compruebe este jsFiddle para ejemplos.

Uso:

// Initialization var file; // HTML5 File object var navigator = new FileNavigator(file); // Read some amount of lines (best performance for sequential file reading) navigator.readSomeLines(startingFromIndex, function (err, index, lines, eof, progress) { ... }); // Read exact amount of lines navigator.readLines(startingFromIndex, count, function (err, index, lines, eof, progress) { ... }); // Find first from index navigator.find(pattern, startingFromIndex, function (err, index, match) { ... }); // Find all matching lines navigator.findAll(new RegExp(pattern), indexToStartWith, limitOfMatches, function (err, index, limitHit, results) { ... });

El rendimiento es igual a la solución anterior. Puedes medirlo invocando ''Leer'' en jsFiddle.

GitHub: https://github.com/anpur/client-line-navigator/wiki


He escrito un módulo llamado line-reader-browser para el mismo propósito. Utiliza Promises .

Sintaxis (Typescript): -

import { LineReader } from "line-reader-browser" // file is javascript File Object returned from input element // chunkSize(optional) is number of bytes to be read at one time from file. defaults to 8 * 1024 const file: File const chunSize: number const lr = new LineReader(file, chunkSize) // context is optional. It can be used to inside processLineFn const context = {} lr.forEachLine(processLineFn, context) .then((context) => console.log("Done!", context)) // context is same Object as passed while calling forEachLine function processLineFn(line: string, index: number, context: any) { console.log(index, line) }

Uso:-

import { LineReader } from "line-reader-browser" document.querySelector("input").onchange = () => { const input = document.querySelector("input") if (!input.files.length) return const lr = new LineReader(input.files[0], 4 * 1024) lr.forEachLine((line: string, i) => console.log(i, line)).then(() => console.log("Done!")) }

Intente seguir el fragmento de código para ver cómo funciona el módulo.

<html> <head> <title>Testing line-reader-browser</title> </head> <body> <input type="file"> <script src="https://cdn.rawgit.com/Vikasg7/line-reader-browser/master/dist/tests/bundle.js"></script> </body> </html>


Espero que le ahorre el tiempo a alguien!

Actualización: marque https://github.com/anpur/client-line-navigator/wiki en mi segunda respuesta, ese lector es mucho mejor.

He hecho mi propio lector, que satisface mis necesidades.

Actuación

Como el problema está relacionado solo con los archivos enormes, el rendimiento fue la parte más importante.

Como puede ver, el rendimiento es casi el mismo que la lectura directa (como se describe en la pregunta anterior). Actualmente estoy intentando mejorarlo, ya que el consumidor de mayor tiempo es una llamada asíncrona para evitar el golpe de límite de la pila de llamadas, lo que no es innecesario para el problema de ejecución. Problema de rendimiento resuelto.

Calidad

Los siguientes casos fueron probados:

  • Archivo vacío
  • Archivo de una sola línea
  • Archivo con nueva línea char en el final y sin
  • Compruebe las líneas analizadas
  • Múltiples carreras en la misma página.
  • No hay líneas perdidas ni problemas de orden.

Código y uso

Html:

<input type="file" id="file-test" name="files[]" /> <div id="output-test"></div>

Uso:

$("#file-test").on(''change'', function(evt) { var startProcessing = new Date(); var index = 0; var file = evt.target.files[0]; var reader = new FileLineStreamer(); $("#output-test").html(""); reader.open(file, function (lines, err) { if (err != null) { $("#output-test").append(''<span style="color:red;">'' + err + "</span><br />"); return; } if (lines == null) { var milisecondsSpend = new Date() - startProcessing; $("#output-test").append("<strong>" + index + " lines are processed</strong> Miliseconds spend: " + milisecondsSpend + "<br />"); return; } // output every line lines.forEach(function (line) { index++; //$("#output-test").append(index + ": " + line + "<br />"); }); reader.getNextLine(); }); reader.getNextLine(); });

Código:

function FileLineStreamer() { var loopholeReader = new FileReader(); var chunkReader = new FileReader(); var delimiter = "/n".charCodeAt(0); var expectedChunkSize = 15000000; // Slice size to read var loopholeSize = 200; // Slice size to search for line end var file = null; var fileSize; var loopholeStart; var loopholeEnd; var chunkStart; var chunkEnd; var lines; var thisForClosure = this; var handler; // Reading of loophole ended loopholeReader.onloadend = function(evt) { // Read error if (evt.target.readyState != FileReader.DONE) { handler(null, new Error("Not able to read loophole (start: )")); return; } var view = new DataView(evt.target.result); var realLoopholeSize = loopholeEnd - loopholeStart; for(var i = realLoopholeSize - 1; i >= 0; i--) { if (view.getInt8(i) == delimiter) { chunkEnd = loopholeStart + i + 1; var blob = file.slice(chunkStart, chunkEnd); chunkReader.readAsText(blob); return; } } // No delimiter found, looking in the next loophole loopholeStart = loopholeEnd; loopholeEnd = Math.min(loopholeStart + loopholeSize, fileSize); thisForClosure.getNextBatch(); }; // Reading of chunk ended chunkReader.onloadend = function(evt) { // Read error if (evt.target.readyState != FileReader.DONE) { handler(null, new Error("Not able to read loophole")); return; } lines = evt.target.result.split(//r?/n/); // Remove last new line in the end of chunk if (lines.length > 0 && lines[lines.length - 1] == "") { lines.pop(); } chunkStart = chunkEnd; chunkEnd = Math.min(chunkStart + expectedChunkSize, fileSize); loopholeStart = Math.min(chunkEnd, fileSize); loopholeEnd = Math.min(loopholeStart + loopholeSize, fileSize); thisForClosure.getNextBatch(); }; this.getProgress = function () { if (file == null) return 0; if (chunkStart == fileSize) return 100; return Math.round(100 * (chunkStart / fileSize)); } // Public: open file for reading this.open = function (fileToOpen, linesProcessed) { file = fileToOpen; fileSize = file.size; loopholeStart = Math.min(expectedChunkSize, fileSize); loopholeEnd = Math.min(loopholeStart + loopholeSize, fileSize); chunkStart = 0; chunkEnd = 0; lines = null; handler = linesProcessed; }; // Public: start getting new line async this.getNextBatch = function() { // File wasn''t open if (file == null) { handler(null, new Error("You must open a file first")); return; } // Some lines available if (lines != null) { var linesForClosure = lines; setTimeout(function() { handler(linesForClosure, null) }, 0); lines = null; return; } // End of File if (chunkStart == fileSize) { handler(null, null); return; } // File part bigger than expectedChunkSize is left if (loopholeStart < fileSize) { var blob = file.slice(loopholeStart, loopholeEnd); loopholeReader.readAsArrayBuffer(blob); } // All file can be read at once else { chunkEnd = fileSize; var blob = file.slice(chunkStart, fileSize); chunkReader.readAsText(blob); } }; };