Подтвердить что ты не робот

Javascript FileReader - анализ длинного файла в кусках

У меня есть длинный файл, который мне нужно проанализировать. Потому что очень долго мне нужно сделать это куском куска. Я пробовал это:

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 );
    }
}

После запуска я вижу только первый фрагмент в консоли. Если я изменил "console.log" на jquery append на некоторый div, я вижу только первый фрагмент в этом div. А как насчет других кусков? Как заставить его работать?

4b9b3361

Ответ 1

FileReader API является асинхронным, поэтому вы должны обрабатывать его с помощью вызовов block. A for loop не будет делать трюк, так как он не дождался завершения каждого чтения перед чтением следующего фрагмента. Здесь рабочий подход.

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 start the read with the first block
    chunkReaderBlock(offset, chunkSize, file);
}

Ответ 2

Второй аргумент slice на самом деле является конечным байтом. Ваш код должен выглядеть примерно так:

 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);
    }
}

Или вы можете использовать этот BlobReader для упрощения интерфейса:

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

Дополнительная информация:

Ответ 3

Обновленный ответ @alediaferia в классе (= this.getFileSize(); } public readBlockAsText(length: number = this.defaultChunkSize) { const fileReader: FileReader = new FileReader(); const blob: Blob = this.file.slice(this.offset, this.offset + length); return new Promise((resolve, reject) => { fileReader.onloadend = (event: ProgressEvent) => { const target: FileReader = (event.target) as FileReader; if (target.error == null) { const result: string = target.result; this.offset += result.length; this.testEndOfFile(); resolve(result); } else { reject(target.error); } }; fileReader.readAsText(blob); }); } private testEndOfFile(): void { if (this.isEndOfFile()) { console.log('Done reading file'); } } private getFileSize(): number { return this.file.size; } } rel="nofollow noreferrer">версия для машинописи здесь) и возвращение результата в обещании. Отважные кодеры даже обернули бы его в асинхронный итератор...

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;
    }
}

Пример печати всего файла в консоли (в асинхронном контексте)

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

Ответ 4

Вы можете воспользоваться функцией Response (частью извлечения), чтобы преобразовать большинство вещей во что-либо еще blob, text, json, а также получить ReadableStream, который может помочь вам прочитать большой двоичный объект 👍

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')
  })

Ответ 5

Разбор большого файла в маленький кусок с помощью простого метода:

                //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++;
                        }
                };