Excel 文件密码保护

SpreadJS 支持在导入和导出 Excel 文件时使用密码进行加密和解密,确保敏感数据的安全性。用户可以在导入加密文件时输入密码解密,或在导出文件时设置密码加密,为表格数据提供安全保护。

概述 本 Demo 展示了如何在 SpreadJS 中实现 Excel 文件的密码保护功能,包括导入加密的 Excel 文件时验证密码,以及导出 Excel 文件时设置密码加密。Demo 提供了完整的用户交互界面,支持文件选择、密码输入和错误提示。 实现思路 初始化 SpreadJS 工作簿实例 为文件选择按钮和导入/导出按钮绑定事件处理函数 导入加密文件时,在 import 方法的选项参数中传入用户输入的密码 错误回调函数中判断错误类型(无密码或密码错误),并提供友好的用户提示 导出文件时,在 export 方法的选项参数中传入密码,生成加密的 Excel 文件 代码解析 导入加密的 Excel 文件 这段代码通过 spread.import() 方法导入文件。password 参数用于解密文件,如果密码错误或缺失,错误回调函数会接收到 errorCode 为 noPassword 或 invalidPassword 的错误对象。通过 GC.Spread.Sheets.IO.ErrorCode 枚举可以判断具体的错误类型。 密码错误处理 当密码错误时,此函数会为输入框添加警告样式,并播放抖动动画(0.5秒),同时在警告框中显示错误消息,清空密码输入框。 导出加密的 Excel 文件 这段代码通过 spread.export() 方法导出文件。如果提供了 password 参数,导出的 Excel 文件将被加密;如果没有提供密码,则导出未加密的文件。文件名根据是否加密添加相应的前缀。 运行效果 点击"选择"按钮选择一个加密的 Excel 文件(.xlsx) 在"密码"输入框中输入正确的密码,点击"导入"按钮,文件将被成功解密并加载到表格中 如果密码错误或缺失,输入框会抖动并显示错误提示信息 在导出区域的"密码"输入框中输入密码,点击"导出"按钮,将生成加密的 Excel 文件并下载 如果不输入密码直接导出,将生成未加密的普通 Excel 文件 API 参考 spread.import() 方法 file:要导入的文件对象 successCallback:成功回调函数 errorCallback:错误回调函数,接收错误对象 error options.fileType:文件类型,如 GC.Spread.Sheets.FileType.excel options.password:用于解密文件的密码字符串 spread.export() 方法 successCallback:成功回调函数,接收 blob 对象 errorCallback:错误回调函数 options.fileType:文件类型,如 GC.Spread.Sheets.FileType.excel options.password:用于加密文件的密码字符串(可选) GC.Spread.Sheets.IO.ErrorCode 枚举 fileIOError (0):文件读写异常 fileFormatError (1):文件格式错误 noPassword (2):需要密码 invalidPassword (3):密码错误
const $ = selector => document.querySelector(selector); const listen = (host, type, handler) => host.addEventListener(type, handler); window.onload = function () { let spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss")); const importPassword = $('#importPassword'); const selectBtn = $('#selectBtn'); const fileSelect = $('#selectedFile'); const importBtn = $('#importBtn'); const warningBox = $('#warningBox'); const exportPassword = $('#exportPassword'); const exportBtn = $('#exportBtn'); listen(selectBtn, "click", () => fileSelect.click()); const fileSelectedHandler = () => { importPassword.disabled = false; importBtn.disabled = false; } listen(fileSelect, 'change', fileSelectedHandler); const wrongPasswordHandler = message => { importPassword.setAttribute('warning', true); importPassword.style.animation = "shake 0.5s"; setTimeout(() => importPassword.style.animation = "", 500); warningBox.innerText = message; importPassword.value = ''; }; listen(importPassword, 'focus', () => { warningBox.innerText = ''; importPassword.removeAttribute('warning'); }); const importFileHandler = () => { let file = fileSelect.files[0]; if (!file) return ; spread.import(file, console.log, error => { if (error.errorCode === GC.Spread.Sheets.IO.ErrorCode.noPassword || error.errorCode === GC.Spread.Sheets.IO.ErrorCode.invalidPassword ) { wrongPasswordHandler(error.errorMessage); } }, { fileType: GC.Spread.Sheets.FileType.excel, password: importPassword.value }); }; listen(importBtn, 'click', importFileHandler); const exportFileHandler = () => { let password = exportPassword.value; spread.export(blob => saveAs(blob, (password ? 'encrypted-' : '') + 'export.xlsx'), console.log, { fileType: GC.Spread.Sheets.FileType.excel, password: password }); }; listen(exportBtn, 'click', exportFileHandler); };
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta name="spreadjs culture" content="zh-cn" /> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-io/dist/gc.spread.sheets.io.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-shapes/dist/gc.spread.sheets.shapes.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-charts/dist/gc.spread.sheets.charts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-slicers/dist/gc.spread.sheets.slicers.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-pivot-addon/dist/gc.spread.pivot.pivottables.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-reportsheet-addon/dist/gc.spread.report.reportsheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-tablesheet/dist/gc.spread.sheets.tablesheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-ganttsheet/dist/gc.spread.sheets.ganttsheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-resources-zh/dist/gc.spread.sheets.resources.zh.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/FileSaver.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <div class="sample-tutorial"> <div class="sample-container"> <div id="ss" class="sample-spreadsheets"></div> <div id="statusBar"></div> </div> <div class="options-container"> <div class="option-row"> <div class="inputContainer"> <div class="options-title">导入:</div> <input class="passwordIpt" id="importPassword" type="password" placeholder="密码" disabled> <br> <div id="warningBox"></div> <input id="selectedFile" type="file" accept=".xlsx" /> <button class="settingButton" id="selectBtn">选择</button> <button class="settingButton" id="importBtn" disabled>导入</button> </div> <div class="inputContainer"> <div class="options-title">导出:</div> <input class="passwordIpt" id="exportPassword" type="password" placeholder="密码"> <br> <button class="settingButton" id="exportBtn">导出</button> </div> </div> </div> </div> </body> </html>
body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-container { width: calc(100% - 280px); height: 100%; float: left; } .sample-spreadsheets { width: 100%; height: calc(100% - 25px); overflow: hidden; } .options-container { float: right; width: 280px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .sample-options { z-index: 1000; } .inputContainer { width: 100%; height: auto; border: 1px solid #eee; padding: 6px 12px; margin-bottom: 10px; box-sizing: border-box; } .settingButton { color: #fff; background: #82bc00; outline: 0; line-height: 1.5715; position: relative; display: inline-block; font-weight: 400; white-space: nowrap; text-align: center; height: 32px; padding: 4px 15px; font-size: 14px; border-radius: 2px; user-select: none; cursor: pointer; border: 1px solid #82bc00; box-sizing: border-box; margin-bottom: 10px; margin-top: 10px; } .settingButton:hover { color: #fff; border-color: #88b031; background: #88b031; } .settingButton:disabled { background: #e2dfdf; border-color: #ffffff; } .options-title { font-weight: bold; margin: 4px 2px; } #selectedFile { display: none; } select, input[type="text"], input[type="number"] { display: inline-block; margin-left: auto; width: 120px; font-weight: 400; outline: 0; line-height: 1.5715; border-radius: 2px; border: 1px solid #F4F8EB; box-sizing: border-box; } .passwordIpt { margin-top: 10px; height: 25px; } .passwordIpt[warning="true"] { border-color: red; } .passwordIpt[warning="true"]::placeholder { color: red; opacity: 0.8; } @keyframes shake { 0% { transform: translate(1px, 1px) rotate(0deg); } 10% { transform: translate(-1px, -2px) rotate(-1deg); } 20% { transform: translate(-3px, 0px) rotate(1deg); } 30% { transform: translate(3px, 2px) rotate(0deg); } 40% { transform: translate(1px, -1px) rotate(1deg); } 50% { transform: translate(-1px, 2px) rotate(-1deg); } 60% { transform: translate(-3px, 1px) rotate(0deg); } 70% { transform: translate(3px, 1px) rotate(-1deg); } 80% { transform: translate(-1px, -1px) rotate(1deg); } 90% { transform: translate(1px, 2px) rotate(0deg); } 100% { transform: translate(1px, 1px) rotate(0deg); } } #warningBox { color: red; }