Отслеживание хода архивирования с помощью архиватора в Node.js

Я использую архиватор, чтобы заархивировать каталог и отправить его пользователю моего веб-приложения. Все работает как положено, и есть код функции:

export async function packDirectory(directoryPath, archiveFilePath, options) {
    options = options || {};

    const {
        archiverFormat = 'zip',
        archiverOptions = {},
        inputStreamOptions = { highWaterMark: 1024 * 1024 },
        outputStreamOptions = { highWaterMark: 1024 * 1024 }
    } = options;

    const archive = archiver(archiverFormat, archiverOptions);
    const outputStream = fs.createWriteStream(archiveFilePath, outputStreamOptions);
    archive.pipe(outputStream);

    const fileItems = await listDirectoryFiles(directoryPath);
    for (let i = 0; i < fileItems.length; i++) {
        const fileItem = fileItems[i];
        const relativeFilePath = fileItem.path;
        const filePath = path.resolve(directoryPath, relativeFilePath);
        const inputStream = fs.createReadStream(filePath, inputStreamOptions);
        archive.append(inputStream, {
            name: relativeFilePath
        });
    }

    return new Promise((resolve, reject) => {
        archive.on('error', e => reject(e));
        archive.on('warning', e => {
            if (e.code !== 'ENOENT') {
                reject(e);
            }
        });
        outputStream.on('close', resolve);
        archive.finalize();
    });
}

Теперь я хочу, чтобы иметь возможность отслеживать ход молнии. archiver предоставляет события входа и прогресса, но я не смог их использовать. Они запускаются только тогда, когда файл обрабатывается (добавляется в результирующий zip-файл), и это не позволяет плавно отслеживать процесс архивирования. Размеры архивируемых файлов значительно различаются, например, это содержимое каталога для упаковки (размер в байтах и ​​имена файлов):

99          RP3_04203_03_GEOTON_20161213_033229_033233.L2.dbf
249         RP3_04203_03_GEOTON_20161213_033229_033233.L2.DC.xml
23562       RP3_04203_03_GEOTON_20161213_033229_033233.L2.MD.xml
257         RP3_04203_03_GEOTON_20161213_033229_033233.L2.prj
18721       RP3_04203_03_GEOTON_20161213_033229_033233.L2.QL.jpg
2668        RP3_04203_03_GEOTON_20161213_033229_033233.L2.shp
108         RP3_04203_03_GEOTON_20161213_033229_033233.L2.shx
81          RP3_04203_03_GEOTON_20161213_033229_033233.L2.tfw
135886758   RP3_04203_03_GEOTON_20161213_033229_033233.L2.tif

Таким образом, подсчет прогресса чтения входных файлов был бы приемлем для меня, и я попробовал следующее:

export async function packDirectory(directoryPath, archiveFilePath, options) {
    // .......
    const { onProgress } = options;
    const fileItems = await listDirectoryFiles(directoryPath);
    const overallBytes = fileItems.reduce((bytes, fileItem) => bytes + fileItem.stats.size, 0);
    let bytes = 0;
    for (let i = 0; i < fileItems.length; i++) {
        // .......
        const inputStream = fs.createReadStream(filePath, inputStreamOptions);
        inputStream.on('data', chunk => {
            bytes += chunk.length;
            if (typeof onProgress === 'function') {
                onProgress((bytes / overallBytes) * 100);
            }
        });
        archive.append(inputStream, {
            name: relativeFilePath
        });
    }

    return new Promise(/*.......*/);
}

Расчет процентного значения передан onProgress обратный вызов работает нормально, но как только я регистрирую слушателя data событие на inputStream, файлы в результирующем zip архиве становятся испорченными:

0           RP3_04203_03_GEOTON_20161213_033229_033233.L2.dbf
249         RP3_04203_03_GEOTON_20161213_033229_033233.L2.DC.xml
0           RP3_04203_03_GEOTON_20161213_033229_033233.L2.MD.xml
0           RP3_04203_03_GEOTON_20161213_033229_033233.L2.prj
0           RP3_04203_03_GEOTON_20161213_033229_033233.L2.QL.jpg
0           RP3_04203_03_GEOTON_20161213_033229_033233.L2.shp
0           RP3_04203_03_GEOTON_20161213_033229_033233.L2.shx
0           RP3_04203_03_GEOTON_20161213_033229_033233.L2.tfw
125400998   RP3_04203_03_GEOTON_20161213_033229_033233.L2.tif

Только первый обработанный файл (.DC.xml) не сломан, все остальные имеют нулевой размер и даже последний большой .tif размер файла меньше исходного. Что не так с моим кодом? Есть ли другое правильное решение для подсчета прочитанных байтов заархивированных файлов? Я никогда не ожидал, что простая регистрация слушателя (даже пустого) может изменить поведение.

Глядя на исходный код, я увидел, что Archiver наследуется от Transform, но я не совсем знаком с ним. Кажется, что данные, прочитанные в моем слушателе, зарегистрированы для data Событие по какой-то причине не успевает преобразоваться в выходной zip.

Я также пытался обрабатывать файлы последовательно:

const inputStream = fs.createReadStream(filePath, inputStreamOptions);
await (async() => {
    return new Promise((resolve, reject) => {
        inputStream.on('data', chunk => {
            bytes += chunk.length;
            if (typeof onProgress === 'function') {
                onProgress((bytes / overallBytes) * 100)
            }
        });
        inputStream.on('close', resolve);
        inputStream.on('error', reject);
        archive.append(inputStream, {
            name: relativeFilePath
        });
    });
})();

Это дает лучшие результаты, но иногда некоторые файлы (.shx) еще пусто

99          RP3_04203_03_GEOTON_20161213_033229_033233.L2.dbf
249         RP3_04203_03_GEOTON_20161213_033229_033233.L2.DC.xml
23562       RP3_04203_03_GEOTON_20161213_033229_033233.L2.MD.xml
257         RP3_04203_03_GEOTON_20161213_033229_033233.L2.prj
18721       RP3_04203_03_GEOTON_20161213_033229_033233.L2.QL.jpg
2668        RP3_04203_03_GEOTON_20161213_033229_033233.L2.shp
0           RP3_04203_03_GEOTON_20161213_033229_033233.L2.shx
81          RP3_04203_03_GEOTON_20161213_033229_033233.L2.tfw
135886758   RP3_04203_03_GEOTON_20161213_033229_033233.L2.tif

0 ответов

Другие вопросы по тегам