如何使用纯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
相关产品推荐
相关产品推荐

