如何通过Google脚本更新Google Drive文件版本并保留共享链接?
需求与问题
我已经能通过Google Apps Script的setContent()方法提取邮件数据并更新Google Drive文件内容,但实际需求是将邮件中的完整附件替换为Drive中现有文件的新版本——这样既能保持原文件的共享链接不变,又能让文件内容更新到最新版本。
现有代码
目前实现setContent更新的代码如下:
function UploadToDrive() { var labelName = "Port"; // Gmail中设置的标签 var targetFolderName = "Schedules"; // Google Drive中的目标文件夹名 var targetFileName = "Schedule.xlsx"; // 目标文件夹中的目标文件名 // 获取带指定标签的所有邮件线程 var label = GmailApp.getUserLabelByName(labelName); var threads = label.getThreads(); if (threads.length > 0) { // 获取最新的邮件线程 var mostRecentThread = threads[0]; // 获取该线程中最新的邮件 var messages = mostRecentThread.getMessages(); var latestMessage = messages[messages.length - 1]; // 检查最新邮件中的附件 var attachments = latestMessage.getAttachments(); for (var k = 0; k < attachments.length; k++) { var attachment = attachments[k]; var attachmentName = attachment.getName().toLowerCase(); // 检查附件名是否包含指定关键词 if (attachmentName.includes("Work Schedule".toLowerCase())) { var targetFolder = DriveApp.getFoldersByName(targetFolderName).next(); var targetFile = targetFolder.getFilesByName(targetFileName); if (targetFile.hasNext()){ var file = targetFile.next(); file.setContent(attachment.setDataFromString()) } } } } }
尝试的Drive API方法及报错
我尝试调用Drive API来实现版本更新,但代码无法正常运行,报错信息:Exception: Unexpected error while getting the method or property update on object Apiary.drive.files.
尝试的方法代码如下:
// 更新指定文件夹中目标文件版本的函数 function updateTargetFileVersion(attachment, targetFolder, targetFileName) { var targetFiles = targetFolder.getFilesByName(targetFileName); if (targetFiles.hasNext()) { // 如果同名文件存在,更新其版本 var targetFile = targetFiles.next(); // 将附件内容转为Blob var newContentBlob = Utilities.newBlob(attachment.getContentType(), attachment.getDataAsString()); // 上传新内容创建新版本 var updatedFile = Drive.Files.update({}, targetFile.getId(), newContentBlob); // 确保文件名正确 updatedFile.setName(targetFileName); } }
解决方案
问题分析
- 原
setContent()方法仅适用于纯文本文件,对于Excel这类二进制文件会损坏内容,无法正常打开。 - 调用Drive API报错的核心原因:
- 未在脚本中启用Drive API服务;
Utilities.newBlob的参数顺序错误(正确顺序为内容, 内容类型, 文件名);Drive.Files.update的调用格式不符合API要求。
修正后的最优代码(无需Drive API)
DriveApp本身提供了setBlob()方法,可直接将附件的二进制内容更新到现有文件,自动创建新版本且保留原共享链接,无需额外启用API:
function UploadToDrive() { var labelName = "Port"; // Gmail中设置的标签 var targetFolderName = "Schedules"; // Google Drive中的目标文件夹名 var targetFileName = "Schedule.xlsx"; // 目标文件夹中的目标文件名 var label = GmailApp.getUserLabelByName(labelName); var threads = label.getThreads(); if (threads.length === 0) return; // 获取最新线程的最新邮件 var mostRecentThread = threads[0]; var latestMessage = mostRecentThread.getMessages().pop(); var attachments = latestMessage.getAttachments(); // 遍历附件寻找目标文件 for (var attachment of attachments) { var attachmentName = attachment.getName().toLowerCase(); if (attachmentName.includes("work schedule")) { var targetFolder = DriveApp.getFoldersByName(targetFolderName).next(); var targetFiles = targetFolder.getFilesByName(targetFileName); if (targetFiles.hasNext()) { var targetFile = targetFiles.next(); // 直接用附件Blob更新文件,自动创建新版本,共享链接保持有效 targetFile.setBlob(attachment); // 可选:标记邮件为已读并移除标签,避免重复处理 mostRecentThread.markRead(); label.removeFromThread(mostRecentThread); break; // 找到目标附件后停止遍历 } } } }
若需使用Drive API的修正方案
如果需要更精细的版本控制(如设置版本描述),可启用Drive API后使用以下代码:
- 先在脚本编辑器中启用Drive API:点击服务 > 添加服务 > 选择Drive API并保存。
- 修正后的方法代码:
function updateTargetFileVersion(attachment, targetFolder, targetFileName) { var targetFiles = targetFolder.getFilesByName(targetFileName); if (!targetFiles.hasNext()) return; var targetFile = targetFiles.next(); // 正确创建Blob:参数顺序为 内容, 内容类型, 文件名 var newContentBlob = Utilities.newBlob(attachment.getDataAsString(), attachment.getContentType(), attachment.getName()); // 调用Drive API更新文件,指定元数据保持文件名不变 var updatedFile = Drive.Files.update( { title: targetFileName }, targetFile.getId(), newContentBlob, { convert: false } // 禁止自动转换格式,保留原文件类型 ); }
关键注意事项
- 对于二进制文件(Excel、PDF、图片等),绝对不能使用
setContent(),必须用setBlob()或Drive API的二进制上传方式,否则文件会损坏。 - 处理完邮件后建议标记已读或移除标签,避免脚本重复执行时重复更新文件。
内容的提问来源于stack exchange,提问作者apex
相关产品推荐
相关产品推荐

