web-dev-qa-db-fra.com

javascript FileReader - analyse de fichiers longs en morceaux

J'ai un long fichier à analyser. Parce que c'est très long, j'ai besoin de le faire morceau par morceau. J'ai essayé ceci:

function parseFile(file){
    var chunkSize = 2000;
    var fileSize = (file.size - 1);

    var foo = function(e){
        console.log(e.target.result);
    };

    for(var i =0; i < fileSize; i += chunkSize)
    {
        (function( fil, start ) {
            var reader = new FileReader();
            var blob = fil.slice(start, chunkSize + 1);
            reader.onload = foo;
            reader.readAsText(blob);
        })( file, i );
    }
}

Après l'avoir lancé, je ne vois que le premier morceau dans la console. Si je modifie 'console.log' en jquery, je ne vois que le premier bloc dans cette div. Qu'en est-il des autres morceaux? Comment le faire fonctionner?

22
mnowotka

FileReader API est asynchrone, vous devez donc le gérer avec des appels block. Un for loop ne ferait pas l'affaire, car il n'attendrait pas que chaque lecture soit terminée avant de lire le morceau suivant .

function parseFile(file, callback) {
    var fileSize   = file.size;
    var chunkSize  = 64 * 1024; // bytes
    var offset     = 0;
    var self       = this; // we need a reference to the current object
    var chunkReaderBlock = null;

    var readEventHandler = function(evt) {
        if (evt.target.error == null) {
            offset += evt.target.result.length;
            callback(evt.target.result); // callback for handling read chunk
        } else {
            console.log("Read error: " + evt.target.error);
            return;
        }
        if (offset >= fileSize) {
            console.log("Done reading file");
            return;
        }

        // of to the next chunk
        chunkReaderBlock(offset, chunkSize, file);
    }

    chunkReaderBlock = function(_offset, length, _file) {
        var r = new FileReader();
        var blob = _file.slice(_offset, length + _offset);
        r.onload = readEventHandler;
        r.readAsText(blob);
    }

    // now let's start the read with the first block
    chunkReaderBlock(offset, chunkSize, file);
}
44
alediaferia

Le deuxième argument de slice est en fait l'octet de fin. Votre code devrait ressembler à quelque chose comme:

 function parseFile(file){
    var chunkSize = 2000;
    var fileSize = (file.size - 1);

    var foo = function(e){
        console.log(e.target.result);
    };

    for(var i =0; i < fileSize; i += chunkSize) {
        (function( fil, start ) {
            var reader = new FileReader();
            var blob = fil.slice(start, chunkSize + start);
            reader.onload = foo;
            reader.readAsText(blob);
        })(file, i);
    }
}

Ou vous pouvez utiliser cette BlobReader pour une interface plus simple:

BlobReader(blob)
.readText(function (text) {
  console.log('The text in the blob is', text);
});

Plus d'information:

6
Minko Gechev

Répondez @ alediaferia à une classe ( Version TypeScript here ) et retournez le résultat dans une promesse. De braves codeurs l'auraient même intégré dans un itérateur asynchrone ...

class FileStreamer {
    constructor(file) {
        this.file = file;
        this.offset = 0;
        this.defaultChunkSize = 64 * 1024; // bytes
        this.rewind();
    }
    rewind() {
        this.offset = 0;
    }
    isEndOfFile() {
        return this.offset >= this.getFileSize();
    }
    readBlockAsText(length = this.defaultChunkSize) {
        const fileReader = new FileReader();
        const blob = this.file.slice(this.offset, this.offset + length);
        return new Promise((resolve, reject) => {
            fileReader.onloadend = (event) => {
                const target = (event.target);
                if (target.error == null) {
                    const result = target.result;
                    this.offset += result.length;
                    this.testEndOfFile();
                    resolve(result);
                }
                else {
                    reject(target.error);
                }
            };
            fileReader.readAsText(blob);
        });
    }
    testEndOfFile() {
        if (this.isEndOfFile()) {
            console.log('Done reading file');
        }
    }
    getFileSize() {
        return this.file.size;
    }
}

Exemple d'impression d'un fichier entier dans la console (dans un contexte async context)

const fileStreamer = new FileStreamer(aFile);
while (!fileStreamer.isEndOfFile()) {
  const data = await fileStreamer.readBlockAsText();
  console.log(data);
}
3
Flavien Volken

Vous pouvez profiter de Response (une partie de fetch ) pour convertir la plupart des choses en n'importe quoi d'autre blob, text, json et obtenir également un ReadableStream qui peut vous aider à lire le blob en morceaux ????

var dest = new WritableStream({
  write (str) {
    console.log(str)
  }
})

new Response(new Blob(['bloby']))
  .body
  // Decode the binary-encoded response to string
  .pipeThrough(new TextDecoderStream())
  .pipeTo(dest)
  .then(() => {
    console.log('done')
  })

Old answer (WritableStreams pipeTo et pipeThrough n'étaient pas implémentés auparavant)

Je suis arrivé avec une idéa intéressante qui est probablement très rapide, car elle convertira le blob en ReadableByteStreamReader probablement beaucoup plus facile aussi, car vous n'avez pas besoin de gérer des éléments tels que la taille du bloc et le décalage, puis de tout faire de manière récursive

function streamBlob(blob) {
  const reader = new Response(blob).body.getReader()
  const pump = reader => reader.read()
  .then(({ value, done }) => {
    if (done) return
    // uint8array chunk (use TextDecoder to read as text)
    console.log(value)
    return pump(reader)
  })
  return pump(reader)
}

streamBlob(new Blob(['bloby'])).then(() => {
  console.log('done')
})

2
Endless

Analyser le fichier volumineux en petit morceau en utilisant la méthode simple:

                //Parse large file in to small chunks
                var parseFile = function (file) {

                        var chunkSize = 1024 * 1024 * 16; //16MB Chunk size
                        var fileSize = file.size;
                        var currentChunk = 1;
                        var totalChunks = Math.ceil((fileSize/chunkSize), chunkSize);

                        while (currentChunk <= scope.totalChunks) {

                            var offset = (currentChunk-1) * chunkSize;
                            var currentFilePart = file.slice(offset, (offset+chunkSize));

                            console.log('Current chunk number is ', currentChunk);
                            console.log('Current chunk data', currentFilePart);

                            currentChunk++;
                        }
                };
1
Radadiya Nikunj