如何通过Google Apps Script Web应用与jQuery实现Chrome扩展向Google Drive上传图片
解决Chrome扩展通过Google Apps Script上传图片到Google Drive的问题
我来帮你搞定这个上传卡壳的问题,主要涉及前端代码优化和Google Apps Script(GAS)的请求解析两部分,同时也会解决文件名获取的问题。
一、问题分析
你的前端代码里有个冗余的FileReader操作(reader.readAsDataURL),这其实完全没必要——你已经拿到了图片的Blob对象,直接用它构造File并上传就行。另外,GAS的doPost函数没有正确解析multipart/form-data类型的请求,导致无法获取文件内容和文件名。
二、优化前端代码
移除不必要的FileReader,并在FormData中明确传递文件名,方便GAS获取:
$(partial).on('paste', e => { e = e.originalEvent; let item = Array.from(e.clipboardData.items).find(x => /^image\//.test(x.type)); if (!item) return; // 非图片类型直接返回 let blob = item.getAsFile(); let base = URL.createObjectURL(blob); let img = new Image(); img.src = base; img.onload = function () { // 更新页面显示的图片 const $imgEvi = $(partial).find('.img-evi'); $imgEvi.attr({ width: this.width, height: this.height, src: base }); // 准备上传数据 const fileName = `capture_${new Date().getTime()}.png`; // 加时间戳避免重名 const file = new File([blob], fileName, { type: 'image/png' }); const fd = new FormData(); fd.append("image", file); fd.append("fileName", fileName); // 单独传递文件名 // 发送AJAX请求 $.ajax({ type: "POST", url: "https://script.google.com/macros/s/#########/exec", data: fd, processData: false, contentType: false, cache: false, dataType: 'json', success: (data) => { alert(`上传成功!文件ID: ${data.data.id}`); }, error: function (xhr, status, error) { alert(`上传失败:${xhr.responseText}`); } }); // 清理URL对象,释放内存 URL.revokeObjectURL(base); }; img.onerror = function() { alert("图片加载失败"); URL.revokeObjectURL(base); }; })
三、修复Google Apps Script的doPost函数
GAS默认不会自动解析multipart/form-data请求,所以需要手动解析请求内容来获取文件和文件名。这里提供两种方案:
方案1:解析multipart/form-data请求(推荐,保持FormData上传方式)
添加一个解析multipart数据的逻辑,提取文件和文件名:
function doPost(e) { try { // 获取请求的boundary(用于分割multipart内容) const boundaryMatch = e.postData.type.match(/boundary=(.*)/); if (!boundaryMatch) throw new Error("无法解析请求格式"); const boundary = boundaryMatch[1]; const parts = e.postData.contents.split(`--${boundary}`); let fileBlob = null; let fileName = ""; // 遍历每个部分,提取文件和文件名 parts.forEach(part => { // 提取文件名 const nameMatch = part.match(/name="fileName"\r\n\r\n(.*)\r\n/); if (nameMatch) { fileName = nameMatch[1]; } // 提取图片文件 const imageMatch = part.match(/name="image"; filename=".*"\r\nContent-Type: image\/png\r\n\r\n(.*)\r\n--/s); if (imageMatch) { // 将原始内容转成Blob fileBlob = Utilities.newBlob(Utilities.base64Decode(imageMatch[1]), 'image/png', fileName); } }); if (!fileBlob) { return ContentService.createTextOutput(JSON.stringify({ ok: false, message: "未找到上传的图片文件" })).setMimeType(ContentService.MimeType.JSON); } // 上传到指定Drive文件夹 const folderId = '### Folder ID ######'; const folder = DriveApp.getFolderById(folderId); const file = folder.createFile(fileBlob); return ContentService.createTextOutput(JSON.stringify({ ok: true, data: { id: file.getId(), name: file.getName() }, message: `图片上传成功,文件ID: ${file.getId()}` })).setMimeType(ContentService.MimeType.JSON); } catch (error) { return ContentService.createTextOutput(JSON.stringify({ ok: false, message: `上传失败:${error.message}` })).setMimeType(ContentService.MimeType.JSON); } }
方案2:改用Base64字符串上传(更简单,避免multipart解析)
如果觉得multipart解析太麻烦,可以把前端的Blob转成Base64字符串,然后作为JSON发送:
前端修改:
// 替换之前的AJAX部分为: const reader = new FileReader(); reader.onload = function(e) { const base64Data = e.target.result.split(',')[1]; // 去掉data:image/png;base64,前缀 const fileName = `capture_${new Date().getTime()}.png`; $.ajax({ type: "POST", url: "https://script.google.com/macros/s/#########/exec", data: JSON.stringify({ image: base64Data, fileName: fileName }), contentType: 'application/json', dataType: 'json', success: (data) => { alert(`上传成功!文件ID: ${data.data.id}`); }, error: function(xhr, status, error) { alert(`上传失败:${xhr.responseText}`); } }); }; reader.readAsDataURL(blob);
GAS的doPost修改:
function doPost(e) { try { const payload = JSON.parse(e.postData.contents); const base64Data = payload.image; const fileName = payload.fileName || "capture.png"; // 将Base64转成Blob const fileBlob = Utilities.newBlob(Utilities.base64Decode(base64Data), 'image/png', fileName); // 上传到Drive文件夹 const folderId = '### Folder ID ######'; const folder = DriveApp.getFolderById(folderId); const file = folder.createFile(fileBlob); return ContentService.createTextOutput(JSON.stringify({ ok: true, data: { id: file.getId(), name: file.getName() }, message: `图片上传成功,文件ID: ${file.getId()}` })).setMimeType(ContentService.MimeType.JSON); } catch (error) { return ContentService.createTextOutput(JSON.stringify({ ok: false, message: `上传失败:${error.message}` })).setMimeType(ContentService.MimeType.JSON); } }
四、关键注意事项
GAS Web应用部署设置:
- 部署时选择Anyone, even anonymous(如果你的扩展不需要权限验证)
- 执行权限设置为Me(你的邮箱)
- 每次修改GAS代码后,需要重新部署并更新版本号
Chrome扩展权限:
- 在
manifest.json中添加相关权限:"permissions": [ "https://script.google.com/*", "https://script.googleusercontent.com/*" ]
- 在
内容的提问来源于stack exchange,提问作者Jailer Betancourt
相关产品推荐
相关产品推荐

