Другое

Экспорт массива JavaScript в CSV на стороне клиента

Узнайте, как экспортировать массивы JavaScript в CSV прямо в браузере без серверной обработки. Поддержка Dojo 1.8, RFC 4180 и рекомендации по библиотекам.

Как экспортировать массив JavaScript в CSV‑формат на стороне клиента?

Мне нужно экспортировать данные массива JavaScript в CSV, используя только клиентский JavaScript. Я работаю с Dojo 1.8, а мои данные структурированы как вложенный массив, например:

javascript
[["name1", "city_name1", ...], ["name2", "city_name2", ...]]

Какой лучший способ преобразовать эту структуру массива в скачиваемый CSV‑файл прямо в браузере без серверной обработки?

Для экспорта массива JavaScript в CSV‑формат на стороне клиента можно использовать несколько подходов, которые работают напрямую в браузере без серверной обработки. Для вашей вложенной структуры массива с Dojo 1.8 ниже приведены самые эффективные методы:

Содержание

Базовое преобразование массива в CSV

Самый простой способ использует встроенные методы JavaScript для преобразования вложенного массива в CSV‑строку и запуска скачивания:

javascript
function exportArrayToCSV(data, filename = 'data.csv') {
    // Преобразуем 2D массив в CSV‑строку
    const csvContent = "data:text/csv;charset=utf-8," + 
        data.map(row => row.map(cell => 
            typeof cell === 'string' ? `"${cell.replace(/"/g, '""')}"` : cell
        ).join(',')).join('\n');
    
    // Создаём ссылку для скачивания
    const encodedUri = encodeURI(csvContent);
    const link = document.createElement("a");
    link.setAttribute("href", encodedUri);
    link.setAttribute("download", filename);
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}

// Использование с вашей вложенной структурой массива
const data = [["name1", "city_name1", ...], ["name2", "city_name2", ...]];
exportArrayToCSV(data, "export.csv");

Этот подход работает во всех современных браузерах и эффективно обрабатывает вашу вложенную структуру массива.

Решение, совместимое с Dojo 1.8

Для совместимости с Dojo 1.8 приведён более надёжный вариант, учитывающий требования фреймворка:

javascript
define([
    "dojo/_base/array",
    "dojo/dom-construct"
], function(array, domConstruct) {
    
    function exportToCSV(data, filename) {
        // Обрабатываем пустые данные
        if (!data || !data.length) {
            console.warn("No data to export");
            return;
        }
        
        // Создаём CSV‑контент с правильной экранизацией
        var csvContent = "data:text/csv;charset=utf-8,";
        
        // Обрабатываем каждую строку
        array.forEach(data, function(row) {
            if (array.isArray(row)) {
                var escapedRow = array.map(row, function(cell) {
                    // Обрабатываем null/undefined
                    if (cell === null || cell === undefined) {
                        return '';
                    }
                    
                    // Преобразуем в строку и экранируем кавычки
                    var cellStr = cell.toString();
                    if (cellStr.indexOf(',') >= 0 || cellStr.indexOf('"') >= 0 || 
                        cellStr.indexOf('\n') >= 0) {
                        return '"' + cellStr.replace(/"/g, '""') + '"';
                    }
                    return cellStr;
                });
                csvContent += escapedRow.join(',') + '\r\n';
            }
        });
        
        // Создаём механизм скачивания
        var encodedUri = encodeURI(csvContent);
        var link = domConstruct.create("a", {
            href: encodedUri,
            download: filename || "export.csv"
        }, document.body);
        
        // Запускаем скачивание
        link.click();
        
        // Очищаем
        domConstruct.destroy(link);
    }
    
    return {
        exportToCSV: exportToCSV
    };
});

Это решение использует утилиты массива и методы создания DOM‑элементов из Dojo.

Реализация, соответствующая RFC 4180

Для корректного форматирования CSV, соответствующего стандарту RFC 4180, используйте более полный вариант:

javascript
function exportRFC4180CSV(data, filename = 'data.csv') {
    var finalVal = '';
    
    for (var i = 0; i < data.length; i++) {
        var value = data[i];
        
        if (Array.isArray(value)) {
            var innerValue = '';
            for (var j = 0; j < value.length; j++) {
                var cellValue = value[j];
                
                if (cellValue === null || cellValue === undefined) {
                    cellValue = '';
                } else {
                    cellValue = cellValue.toString();
                }
                
                // Экранируем кавычки и оборачиваем в кавычки при необходимости
                var result = cellValue.replace(/"/g, '""');
                if (result.indexOf(',') >= 0 || result.indexOf('\n') >= 0 || 
                    result.indexOf('"') >= 0) {
                    result = '"' + result + '"';
                }
                
                if (j > 0) {
                    innerValue += ',';
                }
                innerValue += result;
            }
            finalVal += innerValue + '\r\n';
        }
    }
    
    // Создаём скачивание
    var blob = new Blob([finalVal], { type: 'text/csv;charset=utf-8;' });
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
    } else {
        var link = document.createElement("a");
        if (link.download !== undefined) { // проверка поддержки
            var url = URL.createObjectURL(blob);
            link.setAttribute("href", url);
            link.setAttribute("download", filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}

// Использование
const myData = [["name1", "city_name1", "some info"], ["name2", "city_name2", "more,info"]];
exportRFC4180CSV(myData, "export.csv");

Использование внешних библиотек

Для более сложных сценариев рассмотрите специализированные библиотеки:

PapaParse (рекомендовано для работы с CSV)

html
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.2/papaparse.min.js"></script>

<script>
function exportWithPapaParse(data, filename) {
    Papa.unparse(data, {
        header: false,
        quotes: true,
        escapeFormulae: true,
        encoding: "utf-8"
    }, function(csv) {
        var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
        var link = document.createElement("a");
        var url = URL.createObjectURL(blob);
        
        link.setAttribute("href", url);
        link.setAttribute("download", filename);
        link.style.visibility = 'hidden';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    });
}

// Использование
exportWithPapaParse([["name1", "city_name1"], ["name2", "city_name2"]], "data.csv");
</script>

json2csv

html
<script src="https://cdnjs.cloudflare.com/ajax/libs/json2csv/5.0.6/json2csv.umd.min.js"></script>

<script>
function exportJson2csv(data, filename) {
    const { Parser } = window.json2csv;
    const parser = new Parser({});
    const csv = parser.parse(data);
    
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
    const link = document.createElement("a");
    const url = URL.createObjectURL(blob);
    
    link.setAttribute("href", url);
    link.setAttribute("download", filename);
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}
</script>

Обработка сложных сценариев данных

Для вложенной структуры массива есть несколько продвинутых аспектов:

Заголовки и разделение данных

javascript
function exportWithHeaders(data, headers, filename) {
    // Добавляем строку заголовков
    var csvContent = "data:text/csv;charset=utf-8," + 
        headers.map(h => `"${h.replace(/"/g, '""')}"`).join(',') + '\n';
    
    // Добавляем строки данных
    csvContent += data.map(row => 
        row.map(cell => 
            typeof cell === 'string' ? `"${cell.replace(/"/g, '""')}"` : cell
        ).join(',')
    ).join('\n');
    
    // Запускаем скачивание
    var encodedUri = encodeURI(csvContent);
    var link = document.createElement("a");
    link.setAttribute("href", encodedUri);
    link.setAttribute("download", filename);
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}

// Использование
const headers = ["Name", "City", "Additional Info"];
const data = [["John", "New York", "Some details"], ["Jane", "San Francisco", "More, details with commas"]];
exportWithHeaders(data, headers, "with_headers.csv");

Обработка больших наборов данных

javascript
function exportLargeDataset(data, filename, chunkSize = 1000) {
    var csvContent = "data:text/csv;charset=utf-8,";
    var processed = 0;
    
    function processChunk() {
        var end = Math.min(processed + chunkSize, data.length);
        var chunk = data.slice(processed, end);
        
        var chunkCsv = chunk.map(row => 
            row.map(cell => 
                typeof cell === 'string' ? `"${cell.replace(/"/g, '""')}"` : cell
            ).join(',')
        ).join('\n');
        
        csvContent += chunkCsv;
        processed = end;
        
        if (processed < data.length) {
            // Обрабатываем следующий блок
            setTimeout(processChunk, 0);
        } else {
            // Финальное скачивание
            triggerDownload(csvContent, filename);
        }
    }
    
    function triggerDownload(content, downloadFilename) {
        var encodedUri = encodeURI(content);
        var link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        link.setAttribute("download", downloadFilename);
        link.style.visibility = 'hidden';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
    
    processChunk();
}

Совместимость с браузерами

Для максимальной совместимости, особенно с более старыми браузерами, поддерживаемыми Dojo 1.8:

javascript
function crossBrowserExport(data, filename) {
    var csvContent = "";
    
    // Формируем CSV‑контент
    for (var i = 0; i < data.length; i++) {
        var row = data[i];
        if (Array.isArray(row)) {
            var escapedRow = [];
            for (var j = 0; j < row.length; j++) {
                var cell = row[j];
                if (cell === null || cell === undefined) {
                    escapedRow.push('');
                } else {
                    var cellStr = cell.toString();
                    if (cellStr.indexOf(',') >= 0 || cellStr.indexOf('"') >= 0 || 
                        cellStr.indexOf('\n') >= 0) {
                        escapedRow.push('"' + cellStr.replace(/"/g, '""') + '"');
                    } else {
                        escapedRow.push(cellStr);
                    }
                }
            }
            csvContent += escapedRow.join(',') + '\r\n';
        }
    }
    
    // Специфические методы скачивания для браузеров
    if (window.navigator.msSaveOrOpenBlob) {
        // Поддержка IE 10+
        var blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
        navigator.msSaveOrOpenBlob(blob, filename);
    } else {
        // Стандартные браузеры
        var encodedUri = encodeURI("data:text/csv;charset=utf-8," + csvContent);
        var link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        link.setAttribute("download", filename);
        
        if (link.style) {
            link.style.visibility = 'hidden';
        }
        
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
}

Заключение

Преобразование массивов JavaScript в CSV на стороне клиента простое и доступно несколькими способами:

  1. Базовый метод: простые join() и data‑URI для быстрой экспорта
  2. Совместимость с Dojo 1.8: использование утилит Dojo и методов DOM‑конструкции
  3. Соответствие RFC 4180: надёжное экранирование и форматирование
  4. Библиотечные решения: PapaParse для надёжной работы с CSV без лишнего кода

Для вашей вложенной структуры [['name1', 'city_name1', ...], ['name2', 'city_name2', ...]] решение, совместимое с Dojo 1.8, обеспечивает лучший баланс совместимости и функциональности. Всегда тестируйте с вашими конкретными данными, чтобы убедиться в корректной обработке специальных символов, запятых и переводов строк.

Не забывайте:

  • Правильно обрабатывать специальные символы и кавычки
  • Учитывать требования совместимости браузеров
  • Тестировать с пустыми значениями и большими наборами данных
  • Предоставлять пользователю обратную связь во время операции экспорта

Источники

  1. Stack Overflow – Как экспортировать массив JavaScript в CSV (на стороне клиента)
  2. Stack Overflow – JavaScript array to CSV
  3. LambdaTest Community – Как экспортировать массив JavaScript в CSV напрямую на стороне клиента?
  4. The Web Dev – Как позволить пользователям скачивать данные массива JavaScript как CSV на стороне клиента
  5. SQLPey – JavaScript: Создание и скачивание CSV из массива на стороне клиента
  6. npm export‑to‑csv Package
  7. Delft Stack – Как экспортировать массив в CSV в JavaScript
Авторы
Проверено модерацией
Модерация