You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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);
    }
}

四、关键注意事项

  1. GAS Web应用部署设置:

    • 部署时选择Anyone, even anonymous(如果你的扩展不需要权限验证)
    • 执行权限设置为Me(你的邮箱)
    • 每次修改GAS代码后,需要重新部署并更新版本号
  2. Chrome扩展权限:

    • 在manifest.json中添加相关权限:
      "permissions": [
          "https://script.google.com/*",
          "https://script.googleusercontent.com/*"
      ]
      

内容的提问来源于stack exchange,提问作者Jailer Betancourt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 16:47:41