使用JavaScript复制SharePoint宏启用Excel文件后文件损坏问题排查
问题
我的SharePoint文档库中存储着带宏的Excel文件(.xlsm),想用JavaScript复制并重命名生成副本供用户编辑。当前代码能完成复制重命名操作,但打开生成的文件时会弹出错误提示:
We found a problem with some content with filename.xlsm. Do you want us to try to recover as much as we can? If you trust the source of this workbook, click Yes
点击"Yes"后提示关闭,但无法恢复内容,手动修改扩展名也无法解决问题,推测是代码逻辑导致文件损坏,需要排查问题或寻找替代实现方案。
当前使用的代码:
function copyAndRenameTemplate(fileName, templateFilePath, destinationFolderPath) { getFormDigestValue().then(function(formDigestValue) { // Get template file's unique id $.ajax({ url: "https://mysiteurl/sites/subsite/_api/web/getfilebyserverrelativeurl('" + templateFilePath + "')", type: "GET", headers: { "Accept": "application/json;odata=verbose", "X-RequestDigest": formDigestValue }, success: function(data) { var templateFileUniqueId = data.d.UniqueId; // Copy template file to destination folder $.ajax({ url: "https://mysiteurl.ca/sites/subsite/_api/web/getfolderbyserverrelativeurl('" + destinationFolderPath + "')/Files/add(url='" + fileName + ".xlsm', overwrite=true)", type: "POST", headers: { "Accept": "application/json;odata=verbose", "X-RequestDigest": formDigestValue, "X-HTTP-Method": "POST", "If-Match": "*", "Content-Type": "application/json;odata=verbose" }, data: JSON.stringify({ 'type': 'SP.File', 'sourceUniqueId': templateFileUniqueId }), success: function(data) { console.log("File copied and renamed successfully."); }, error: function(error) { console.log("Error copying and renaming file: " + JSON.stringify(error)); } }); }, error: function(error) { console.log("Error retrieving template file: " + JSON.stringify(error)); } }); }).catch(function(error) { console.error('Error retrieving form digest: ', error); alert('Failed to retrieve form digest value: ' + JSON.stringify(error)); }); }
解决方案
问题根源
你当前使用的Files/add方法搭配sourceUniqueId参数的方式,并非SharePoint官方推荐的文件复制逻辑。这种方式本质是尝试创建新文件并关联源文件ID,但并未正确读取源文件的二进制内容,导致生成的文件仅包含元数据,实际内容为空或格式损坏——对于.xlsm这类二进制复合文件(包含宏容器),这种错误会直接导致文件无法正常打开。
方案1:使用SP.File.copyTo方法(推荐)
SharePoint REST API提供了专门的copyTo方法用于文件复制,能完整保留文件的所有内容和属性,包括宏数据,不会损坏文件。
修改后的代码:
function copyAndRenameTemplate(fileName, templateFilePath, destinationFolderPath) { getFormDigestValue().then(function(formDigestValue) { // 拼接目标文件的完整服务器相对路径 const destinationFileUrl = `${destinationFolderPath}/${fileName}.xlsm`; // 调用源文件的copyTo方法完成复制 $.ajax({ url: `https://mysiteurl/sites/subsite/_api/web/getfilebyserverrelativeurl('${templateFilePath}')/copyTo(strNewUrl='${destinationFileUrl}', boverwrite=true)`, type: "POST", headers: { "Accept": "application/json;odata=verbose", "X-RequestDigest": formDigestValue }, success: function() { console.log("文件复制并重命名成功"); }, error: function(error) { console.log("复制文件出错: " + JSON.stringify(error)); } }); }).catch(function(error) { console.error('获取Form Digest失败: ', error); alert('获取Form Digest失败: ' + JSON.stringify(error)); }); }
关键说明
copyTo是SharePoint原生的文件复制API,会完整复制文件的二进制内容、宏、权限等所有属性。- 无需提前获取源文件的
UniqueId,直接通过源文件的服务器相对路径调用方法即可。 - 参数
boverwrite=true保持原逻辑,允许覆盖目标路径的同名文件。
方案2:读取源文件二进制内容后上传
如果因权限或环境限制无法使用copyTo,可以先读取源文件的二进制Blob数据,再上传到目标路径,确保文件内容完整。
代码示例:
function copyAndRenameTemplate(fileName, templateFilePath, destinationFolderPath) { getFormDigestValue().then(function(formDigestValue) { // 第一步:读取源文件的二进制Blob内容 $.ajax({ url: `https://mysiteurl/sites/subsite/_api/web/getfilebyserverrelativeurl('${templateFilePath}')/$value`, type: "GET", headers: { "X-RequestDigest": formDigestValue }, responseType: "blob", success: function(blobData) { // 第二步:通过FormData上传Blob到目标文件夹 const formData = new FormData(); formData.append("file", blobData, `${fileName}.xlsm`); $.ajax({ url: `https://mysiteurl/sites/subsite/_api/web/getfolderbyserverrelativeurl('${destinationFolderPath}')/Files/add(overwrite=true)`, type: "POST", headers: { "X-RequestDigest": formDigestValue }, processData: false, contentType: false, data: formData, success: function() { console.log("文件复制并重命名成功"); }, error: function(error) { console.log("上传文件出错: " + JSON.stringify(error)); } }); }, error: function(error) { console.log("读取源文件出错: " + JSON.stringify(error)); } }); }).catch(function(error) { console.error('获取Form Digest失败: ', error); alert('获取Form Digest失败: ' + JSON.stringify(error)); }); }
关键说明
- 通过
$value端点获取文件的原始二进制Blob数据,确保完整读取.xlsm的所有内容(包括宏容器)。 - 使用
FormData上传Blob,避免JSON序列化导致的二进制数据损坏。 - 这种方式适合需要对文件内容做修改后再上传的场景,同样能保证文件完整性。
内容的提问来源于stack exchange,提问作者Toner
相关产品推荐
相关产品推荐

