Office JS备份Excel工作表删除后恢复失效问题咨询
使用JavaScript开发Excel Office Add-in功能时,需要实现将工作表内容存储到localStorage等本地存储介质的备份恢复能力,当前编写的备份恢复代码如下:
async backupSheets() { await Excel.run(async (context) => { var worksheets = context.workbook.worksheets; worksheets.load('items'); await context.sync(); let wsBackup = worksheets; for (var i = 0; i < worksheets.items.length; i++) { console.log(worksheets.items[i].toJSON()); worksheets.items[i].delete(); } // 此处计划通过赋值恢复工作表 worksheets = wsBackup; // 恢复逻辑未生效 await context.sync(); }); }
预期行为:执行worksheets = wsBackup赋值操作时,可恢复删除循环执行前备份的工作表,但当前该逻辑并未生效,需要可行替代方案。
预期操作流程
- 将所有工作表复制存入名为
wsBackup的变量中 - 删除除最后一个工作表外的所有工作表
- 通过赋值备份
wsBackup即可恢复操作使用的proxy object - 工作表可在工作簿中被正常还原
实际运行效果
除工作表恢复步骤外其余步骤均可正常执行,恢复操作未生效。
Office JS API 中所有操作Excel实体的对象都是与当前请求上下文绑定的代理对象,仅持有指向Excel运行时真实实体的引用,本身不存储实体的结构、数据内容。
代码中let wsBackup = worksheets仅完成了本地JS变量的引用拷贝,既没有持久化任何工作表实际数据,也不会在变量重新赋值时向Excel发送反向操作指令。执行delete()方法后,Excel侧对应的工作表实体已经被移除,仅修改本地JS变量的指向不会触发任何恢复动作,逻辑自然不生效。
另外这类代理对象和Excel.run创建的上下文强绑定,上下文释放后代理对象会直接失效,完全无法用于跨上下文或者持久化存储场景。
方案1:可持久化到localStorage的全量备份恢复
如果需要把备份存到localStorage等外部存储,必须在备份阶段把工作表的所有需要保留的内容转换成纯可序列化的普通JS对象,恢复阶段基于备份数据重建工作表,不能直接操作代理对象。
参考实现代码:
async backupAndRestoreSheets() { await Excel.run(async (context) => { const worksheets = context.workbook.worksheets; // 预加载工作表基础属性 worksheets.load('items/name, items/position'); await context.sync(); // 1. 备份阶段:提取纯数据,不存代理对象 const wsBackup = []; for (const sheet of worksheets.items) { // 加载工作表已用范围的所有需要备份的属性 const usedRange = sheet.getUsedRange(true); usedRange.load('values, formulas, numberFormat, rowHeight, columnWidth, font, fill'); await context.sync(); wsBackup.push({ name: sheet.name, position: sheet.position, rangeData: { values: usedRange.values, formulas: usedRange.formulas, numberFormat: usedRange.numberFormat, rowHeight: usedRange.rowHeight, columnWidth: usedRange.columnWidth, font: usedRange.font.toJSON(), fill: usedRange.fill.toJSON() // 如需备份条件格式、图表、数据透视表等内容,需在此处对应加载属性后加入备份结构 } }); } // 备份数据可直接序列化存入localStorage localStorage.setItem('excel_ws_backup', JSON.stringify(wsBackup)); // 2. 执行工作表删除逻辑:删除除最后一个工作表外的所有表 const sheetTotal = worksheets.items.length; // 倒序删除避免索引错乱 for (let i = sheetTotal - 2; i >= 0; i--) { worksheets.items[i].delete(); } await context.sync(); // 3. 恢复阶段:基于备份数据重建工作表,禁止直接给代理对象变量赋值 const savedBackup = JSON.parse(localStorage.getItem('excel_ws_backup')); for (const sheetData of savedBackup) { const newSheet = worksheets.add(sheetData.name); newSheet.position = sheetData.position; const rowCount = sheetData.rangeData.values.length; const colCount = sheetData.rangeData.values[0].length; const targetRange = newSheet.getRangeByIndexes(0, 0, rowCount, colCount); // 逐属性还原内容和格式 targetRange.values = sheetData.rangeData.values; targetRange.formulas = sheetData.rangeData.formulas; targetRange.numberFormat = sheetData.rangeData.numberFormat; targetRange.format.rowHeight = sheetData.rangeData.rowHeight; targetRange.format.columnWidth = sheetData.rangeData.columnWidth; targetRange.format.font = sheetData.rangeData.font; targetRange.format.fill = sheetData.rangeData.fill; // 对应还原其他备份的复杂元素 } await context.sync(); }); }
注意:如果业务需要备份图表、数据透视表、条件格式、工作表保护规则等复杂内容,需要对应加载相关属性后加入备份结构,恢复时按照Office JS API的创建规则逐个重建,没有一键复制全量工作表内容的内置快捷方法。
方案2:单次操作内的临时备份(无需持久化)
如果不需要把备份存到localStorage,仅在同一次Excel.run上下文内临时删除后恢复,不需要全量序列化数据,可以在删除前调用worksheet.copy()方法把要删除的工作表复制到工作簿内的临时位置,恢复时直接操作复制生成的工作表即可,性能远高于全量序列化重建。
内容的提问来源于stack exchange,提问作者cgodev

