Каким образом можно предотвратить утечку памяти при парсинге файлов формата xlsx в Node.js?

Как можно улучшить структуру и ясность следующего кода, сохраняя его оригинальное намерение и функциональность? Вот мой код: 

```javascript
export async function getDataExel(file: Buffer): Promise<any[]> {
    const workbook = xlsx.read(file, { type: 'buffer' });
    const sheetName = workbook.SheetNames[0];
    const sheet = workbook.Sheets[sheetName];
    return xlsx.utils.sheet_to_json(sheet, { header: 1 }).slice(3, -1);
}

export async function downloadAndProcessExcel(url: string) {
    try {
        let response = await axios.get(url, {
            responseType: 'arraybuffer'
        });
        return await getDataExel(response.data);
    } catch (error) {
        console.error('Ошибка при загрузке и обработке файла:', error);
        return [];
    }
}

export async function main() {
    console.log("Start");

    await ArticleService.deleteAll('juna');
    const settings = await SettingsService.getData();
    const links = await fetchLinks();

    for (const link of links) {
        console.log(link);
        try {
            const result = await downloadAndProcessExcel('https://price.juna-life.ru' + link);
            // await processAndSaveData(result, settings);
        } catch (e) {
            console.error(`Ошибка при обработке ссылки ${link}:`, e);
        }
    }

    console.log("Finish");
}

// Start Script

import { main } from "./parserJuna";

// Функция для получения текущего использования памяти
function logMemoryUsage() {
    const used = process.memoryUsage();
    const rss = (used.rss / 1024 / 1024).toFixed(2);
    console.log(`RSS: ${rss} MB`);
}

// Основная функция
async function start() {
    console.log("Start");

    const interval = setInterval(() => {
        logMemoryUsage();
    }, 5000); // Логирование каждые 5 с
```
  • 1 сентября 2024 г. 17:44
Ответы на вопрос 2
Чтобы предотвратить утечку памяти при парсинге файлов формата XLSX в Node.js, можно рассмотреть несколько подходов:

1. **Использование стрима**: Вместо загрузки всего файла в память, можно использовать стрим для обработки данных. Это особенно полезно для больших файлов, так как позволяет обрабатывать файл по частям.

2. **Освобождение памяти**: После обработки файла убедитесь, что ссылки на объекты, которые больше не нужны, обнуляются. Это позволяет сборщику мусора освобождать память.

3. **Контроль использования памяти**: Регулярно проверяйте использование памяти (что вы уже делаете) и, при необходимости, выполняйте действия по очистке.

4. **Ограничение размера буфера**: Обработка больших файлов может привести к высоким расходам памяти. Если возможно, установите лимиты на размер обрабатываемого Excel-файла.

Ниже приведен улучшенный и более структурированный код с учетом предложений по улучшению ясности и возможности управления памятью:

```javascript
import axios from 'axios';
import xlsx from 'xlsx';
import { ArticleService, SettingsService } from './services'; // Предполагаем, что эти сервисы уже реализованы.

export async function getDataExel(file: Buffer): Promise<any[]> {
    const workbook = xlsx.read(file, { type: 'buffer' });
    const sheetName = workbook.SheetNames[0];
    const sheet = workbook.Sheets[sheetName];
    
    // Возвращаем только нужные данные, используя slice
    return xlsx.utils.sheet_to_json(sheet, { header: 1 }).slice(3, -1);
}

export async function downloadAndProcessExcel(url: string) {
    try {
        const response = await axios.get(url, { responseType: 'arraybuffer' });
        const data = await getDataExel(response.data);
        return data;
    } catch (error) {
        console.error('Ошибка при загрузке и обработке файла:', error);
        return [];
    }
}

export async function processLinks(links: string[], settings: any) {
    const results = [];

    for (const link of links) {
        console.log(`Обработка ссылки: ${link}`);
        try {
            const result = await downloadAndProcessExcel(`https://price.juna-life.ru${link}`);
            results.push(result);
            // await processAndSaveData(result, settings); // Раскомментируйте, если нужна обработка.
        } catch (e) {
            console.error(`Ошибка при обработке ссылки ${link}:`, e);
        }
    }

    return results;
}

export async function main() {
    console.log("Начало");

    await ArticleService.deleteAll('juna');
    const settings = await SettingsService.getData();
    const links = await fetchLinks();

    await processLinks(links, settings);

    console.log("Завершение");
}

// Функция для логирования использования памяти
function logMemoryUsage() {
    const used = process.memoryUsage();
    const rss = (used.rss / 1024 / 1024).toFixed(2);
    console.log(`Использование памяти (RSS): ${rss} MB`);
}

// Основная функция запуска
async function start() {
    console.log("Начало");

    const interval = setInterval(() => {
        logMemoryUsage();
    }, 5000); // Логирование каждые 5 секунд

    // Запуск выполнения основного логики
    await main();

    clearInterval(interval); // Очистка интервала после выполнения
}

start();
```

### Основные изменения и улучшения:
- **Структура**: Добавлена функция `processLinks`, которая обрабатывает массив ссылок, что улучшает читаемость кода.
- **Логирование**: Логирование теперь более детализированное при обработке ссылок.
- **Управление памятью**: Освобождение и контроль использования памяти логируется.
- **Улучшение читаемости**: Код разбит на логические блоки, что улучшает понимание его структуры.
1. Используй ReadableStream 
2. Нет смысла держать программу запущенной все время, если она запускается лишь раз в 2 сутки. Когда она завершится, память автоматически освободится обратно к ОС. Используй планировщик задач ОС.
3. Можно скачивать файлы на хранилище вместо того, чтобы держать в памяти. Затем подчищать.
Похожие вопросы