You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用纯Javascript将本地.xlsx文件转换为JSON格式

纯前端无依赖Excel转JSON实现方案

报错原因

File is not a blob 报错的核心触发逻辑是浏览器安全策略限制:前端JS无法直接通过本地文件路径读取磁盘文件,必须通过用户主动交互(点击上传按钮选文件、拖拽文件到页面)获取的File对象(属于Blob的子类)才能被正常读取处理。

完整实现代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Excel转JSON</title>
</head>
<body>
    <!-- 文件选择输入框 -->
    <input type="file" id="excelUpload" accept=".xlsx">
    <pre id="jsonOutput"></pre>

    <script>
        // 监听文件选择事件
        document.getElementById('excelUpload').addEventListener('change', async (e) => {
            const file = e.target.files[0];
            if (!file) return;
            try {
                const jsonData = await excelToJson(file);
                // 输出JSON结果
                document.getElementById('jsonOutput').textContent = JSON.stringify(jsonData, null, 2);
            } catch (err) {
                console.error('转换失败:', err);
            }
        });

        // 核心转换函数,无任何外部依赖
        async function excelToJson(file) {
            // 预先验证文件类型,避免blob报错
            if (!(file instanceof Blob)) {
                throw new Error('传入参数不是合法的Blob/File对象');
            }
            // 读取文件为二进制数组
            const arrayBuffer = await file.arrayBuffer();
            const data = new Uint8Array(arrayBuffer);
            // 校验是否为xlsx格式(本质是ZIP压缩包)
            if (data[0] !== 0x50 || data[1] !== 0x4B) {
                throw new Error('仅支持.xlsx格式的Excel文件');
            }

            // 解析xlsx压缩包内容
            const zip = await unzip(data);
            const workbookXML = parseXML(zip['xl/workbook.xml']);
            const sharedStringsXML = parseXML(zip['xl/sharedStrings.xml']);
            const sheets = Array.from(workbookXML.querySelectorAll('sheets sheet'));
            const sharedStrings = Array.from(sharedStringsXML.querySelectorAll('si t')).map(el => el.textContent);

            const result = {};
            // 遍历每个工作表转换
            for (const sheet of sheets) {
                const sheetId = sheet.getAttribute('sheetId');
                const sheetName = sheet.getAttribute('name');
                const sheetXML = parseXML(zip[`xl/worksheets/sheet${sheetId}.xml`]);
                const rows = Array.from(sheetXML.querySelectorAll('sheetData row'));
                
                const sheetData = [];
                const headers = {};
                rows.forEach((row, rowIndex) => {
                    const cells = Array.from(row.querySelectorAll('c'));
                    const rowData = {};
                    cells.forEach(cell => {
                        const col = cell.getAttribute('r').match(/[A-Z]+/)[0];
                        const valueNode = cell.querySelector('v');
                        let value = valueNode ? valueNode.textContent : '';
                        // 处理共享字符串类型的单元格内容
                        if (cell.getAttribute('t') === 's') {
                            value = sharedStrings[parseInt(value)];
                        }
                        // 第一行内容作为表头键名
                        if (rowIndex === 0) {
                            headers[col] = value;
                        } else {
                            rowData[headers[col]] = value;
                        }
                    });
                    if (rowIndex > 0 && Object.keys(rowData).length > 0) {
                        sheetData.push(rowData);
                    }
                });
                result[sheetName] = sheetData;
            }
            return result;
        }

        // 简化版ZIP解压工具函数,仅适配xlsx格式
        async function unzip(data) {
            const zip = {};
            const view = new DataView(data.buffer);
            let offset = 0;
            while (offset < data.length - 4) {
                if (view.getUint32(offset, true) !== 0x04034b50) break;
                const compressedSize = view.getUint32(offset + 18, true);
                const uncompressedSize = view.getUint32(offset + 22, true);
                const fileNameLen = view.getUint16(offset + 26, true);
                const extraFieldLen = view.getUint16(offset + 28, true);
                offset += 30;
                const fileName = new TextDecoder().decode(data.subarray(offset, offset + fileNameLen));
                offset += fileNameLen + extraFieldLen;
                const compressedData = data.subarray(offset, offset + compressedSize);
                offset += compressedSize;
                // 调用浏览器原生解压API
                if (compressedSize > 0 && uncompressedSize > 0) {
                    const ds = new DecompressionStream('deflate-raw');
                    const writer = ds.writable.getWriter();
                    writer.write(compressedData);
                    writer.close();
                    const output = await new Response(ds.readable).arrayBuffer();
                    zip[fileName] = new TextDecoder().decode(new Uint8Array(output));
                }
            }
            return zip;
        }

        // XML解析工具函数
        function parseXML(xmlStr) {
            const parser = new DOMParser();
            return parser.parseFromString(xmlStr, 'application/xml');
        }
    </script>
</body>
</html>

注意事项

  • 本实现完全依赖浏览器原生API,无需引入任何Node库或第三方依赖,可直接在静态站点运行
  • 仅支持.xlsx格式文件,老版本.xls属于二进制专有格式,纯前端解析复杂度极高,建议先转成.xlsx再使用
  • 必须通过用户点击选择文件的交互触发读取,无法直接通过本地文件路径静默读取,符合浏览器安全规范
  • 转换后的JSON会以表格第一行作为键名,每行数据对应一个对象,所有Sheet的内容会按Sheet名分类返回

内容的提问来源于stack exchange,提问作者Shreyash Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 03:39:03