Excel в JSON с помощью JavaScript

Нужно конвертировать Excel в JSON. Создал форму:

div>
    <input type="file" id="uploadFile" name="uploadFile" accept=".xls, .xlsx" />

<a> href="#" uname="@fileObj.FileUniqueName" class="uploadData">Загрузить</a>
</div>

Импортировал зависимость:

<script src="https://cdnjs.cloudflare.com/ajax/libs/xls/0.7.6/xls.min.js"></script>

В интернете нашел следующею подходящею функцию. Вот в таком виде она работает:

var oFileIn;

        $(function () {
            oFileIn = document.getElementById('uploadFile');
            if (oFileIn.addEventListener) {
                oFileIn.addEventListener('change', ExcelToJSON, false);
            }
        });

        function ExcelToJSON(oEvent) {

            // Get The File From The Input
            var oFile = oEvent.target.files[0];
            var sFilename = oFile.name;
            // Create A File Reader HTML5
            var reader = new FileReader();

            // Ready The Event For When A File Gets Selected
            reader.onload = function (e) {
                var data = e.target.result;
                var cfb = XLS.CFB.read(data, { type: 'binary' });
                var wb = XLS.parse_xlscfb(cfb);
                // Loop Over Each Sheet
                wb.SheetNames.forEach(function (sheetName) {
                    // Obtain The Current Row As CSV
                    var sCSV = XLS.utils.make_csv(wb.Sheets[sheetName]);
                    var oJS = XLS.utils.sheet_to_row_object_array(wb.Sheets[sheetName]);

                    $("#my_file_output").html(sCSV);
                    console.log(oJS)
                });
            };

            // Tell JS To Start Reading The File.. You could delay this if desired
            reader.readAsBinaryString(oFile);
}

Мне нужно, чтобы она срабатывала при нажатии на ссылку. Для этого я её немного переделал и получился следующий вариант:

$(".uploadData").click(function () {
            var data = ExcelToJSON();
            //data.append("uname", $(this).attr('uname'));  //Other data
            $.ajax({
                type: "POST",
                //url: "http://10.1.48.68:80/uploadinmeta/api/FileApi/uploadData",
                url: "http://localhost:6141/api/FileApi/uploadData",
                contentType: "application/json;charset=utf-8",
                processData: false,
                data: JSON.stringify(data),
                success: function (response) {
                    alert(response);
                    console.log(response);
                },
                error: function (result, status, er) {
                    alert("error: " + result + " status: " + status + " er:" + er);
                }
            });
        });

    var ExcelToJSON = function () {

        // Get The File From The Input
        var oFile = document.getElementById('uploadFile').files[0];
        //var oFile = oFileIn.target.files[0];
        var sFilename = oFile.name;
        // Create A File Reader HTML5
        var reader = new FileReader();

    var json_objects = []
    // Ready The Event For When A File Gets Selected
    reader.onload = function (e) {
        var data = e.target.result;
        var cfb = XLS.CFB.read(data, { type: 'binary' });
        var wb = XLS.parse_xlscfb(cfb);
        // Loop Over Each Sheet
        wb.SheetNames.forEach(function (sheetName) {
            // Obtain The Current Row As CSV
            var sCSV = XLS.utils.make_csv(wb.Sheets[sheetName]);
            var json_object = XLS.utils.sheet_to_row_object_array(wb.Sheets[sheetName]);

            json_objects.push(json_object);
            $("#my_file_output").html(sCSV);
            console.log(json_object)
            
            });
        };
        return json_objects;
    // Tell JS To Start Reading The File.. You could delay this if desired
    reader.readAsBinaryString(oFile);
    }

Но моя реализация срабатывает не до конца, вот эта часть функции не отрабатывает:

reader.onload = function (e) {
        var data = e.target.result;
        var cfb = XLS.CFB.read(data, { type: 'binary' });
        var wb = XLS.parse_xlscfb(cfb);
        // Loop Over Each Sheet
        wb.SheetNames.forEach(function (sheetName) {
            // Obtain The Current Row As CSV
            var sCSV = XLS.utils.make_csv(wb.Sheets[sheetName]);
            var json_object = XLS.utils.sheet_to_row_object_array(wb.Sheets[sheetName]);

            json_objects.push(json_object);
            $("#my_file_output").html(sCSV);
            console.log(json_object)
            
            });
        };

Где накосячил, как можно исправить?

Проблема в том, что если смотреть через отладчик, то он даже не заходит вот в эту часть функции:

reader.onload = function (e) {
            var data = e.target.result;
            var cfb = XLS.CFB.read(data, { type: 'binary' });
            var wb = XLS.parse_xlscfb(cfb);
            // Loop Over Each Sheet
            wb.SheetNames.forEach(function (sheetName) {
                // Obtain The Current Row As CSV
                var sCSV = XLS.utils.make_csv(wb.Sheets[sheetName]);
                var json_object = XLS.utils.sheet_to_row_object_array(wb.Sheets[sheetName]);

                json_objects.push(json_object);
                $("#my_file_output").html(sCSV);
                console.log(json_object)
                
                });
            };

дойдя да этой части он встает вот на эту строку:

reader.onload = function (e) {

а потом сразу перепрыгивает на строку за этой функцией:

 return json_objects;

Содержание объектов reder и oFile перед исполнением:

    reader: FileReader
    error: null
    onabort: null
    onerror: null
    onload: null
    onloadend: null
    onloadstart: null
    onprogress: null
    readyState: 0
    result: null

 oFile: File
    lastModified: 1621684181841
    lastModifiedDate: Sat May 22 2021 16:49:41 GMT+0500 (Екатеринбург, стандартное время) {}
    name: "Test.xls"
    size: 25088
    type: "application/vnd.ms-excel"
    webkitRelativePath: ""
    __proto__: File

Смущает, что у объекта FileReader все либо null либо 0

Объект event функции reader.onload, почему-то как будто слушает мышь введите сюда описание изображения

При начальном (рабочем) варианте функции переменная события e имеет другое содержание, содержит в т.ч. srcElement: FileReader {readyState: 2,, которого нет в моем варианте: введите сюда описание изображения

Думаю все дело в данной переменной события е (event), которая имеет отличное от первоначального (рабочего) варианта функции.


Ответы (1 шт):

Автор решения: Vlad-i-mir

Возникающая ошибка и не выполнение кода обработчика события reader.onload() связано с тем, что его исполнение откладывается на потом, когда файл будет прочитан браузером, но сначала выполнится весь оставшийся код функции ExcelToJSON(). ExcelToJSON() сразу возвращает пустой массив, потому что reader.onload() ещё не сработал.

Для решения данной проблемы необходимо обработчик события вызывающий ExcelToJSON() сделать асинхронным (async), а вызов ExcelToJSON() произвести с оператором await, который укажет, что необходимо дождаться полного исполнения данной функции. Сам обработчик reader.onload() и возврат результата его работы обернуть в Promise, чтобы обозначить, что этот блок должен точно выполниться как единое целое.

В итоге получается следующее решение:

 $(".uploadData").click(async function () {
                var data = await ExcelToJSON('AccrualsPaymentsSocialRent');
                if (data != undefined & data.length > 0){
                    data = data[0];
                }
                $.ajax({
                    type: "POST",
                    url: "http://localhost:6141/api/FileApi/uploadData",
                    contentType: "application/json;charset=utf-8",
                    processData: false,
                    data: JSON.stringify(data),
                    success: function (response) {
                        alert(response);
                        console.log(response);
                    },
                    error: function (result, status, er) {
                        alert("error: " + result + " status: " + status + " er:" + er);
                    }
                });
            });

var ExcelToJSON = function (sheetNameRead) {

            // Get The File From The Input
            var oFile = document.getElementById('uploadFile').files[0];
            //var oFile = oFileIn.target.files[0];
            var sFilename = oFile.name;
            // Create A File Reader HTML5
            var reader = new FileReader();

            var json_objects = [];
            return new Promise(resolve => {
                // Ready The Event For When A File Gets Selected
                reader.onload = function (event) {
                    var data = event.target.result;
                    var cfb = XLSX.read(data, { type: 'binary' });
                    // Loop Over Each Sheet
                    cfb.SheetNames.forEach(function (sheetName) {

                        if (sheetName == sheetNameRead) {
                            // Obtain The Current Row As CSV
                            var sCSV = XLS.utils.make_csv(cfb.Sheets[sheetName]);
                            var json_object = XLS.utils.sheet_to_json(cfb.Sheets[sheetName]);

                            json_objects.push(json_object);
                        }
                    });
                    resolve(json_objects);
                };                
            
            // Tell JS To Start Reading The File.. You could delay this if desired
                reader.readAsBinaryString(oFile);
            });
        }
→ Ссылка