【问题标题】:NodeJS Google Drive API how to update fileNodeJS Google Drive API如何更新文件
【发布时间】:2019-03-25 10:26:41
【问题描述】:

您好,我正在尝试使用 Google Drive API 使用 NodeJS 更新 Google Doc,但出现此错误:

{
 "error": {
  "code": 500,
  "message": null
 }
}

以下是相关代码:

var j = new google.auth.JWT(
    creds.client_email,
    null,
    creds.private_key,
    [
        "https://www.googleapis.com/auth/drive"
    ]
);
async function refreshTokens() {
    startedWaiting = true;
    return j.authorize((r,t) => {
        startedWaiting = false;
        timeTillNeedRefresh = t["expiry_date"] - Date.now();
        setTimeout(function() {
            refreshTokens();
        //    console.log("doing Q", Q);

        }, timeTillNeedRefresh);

        tokens = t;
        console.log("GOT A TOKEN", tokens);
        Q.forEach(x=>x());
        Q = [];

    });
}

async function start() {
    await refreshTokens();
}
start();
function myFetch(opts) {
    if(!opts) opts = {};
    var cb = opts.cb || empty;
    var headers =  {
        'Accept-Encoding': 'gzip',
        'User-Agent': 'google-api-nodejs-client/0.7.2 (gzip)',
        Authorization:tokens.token_type +" "+ tokens.access_token,
        Accept:"application/json"
    };
    if(opts.headers) {
        for(k in opts.headers) {
            headers[k] = opts.headers[k];
        }
    }
    fetch(
        opts.url 
        || "",
    {
        method:opts.method || "GET",
        headers: headers
    })
        .then(res => res.text())
        .then(body => {
            cb(body);

        });
}

updateFile({
    id: "1vebqOamZ9QB4HqfUaQN-U9Zt4y33eij-I21glMvaPVQ",
    content: "hi there"
});

async function allFuncs(tmp) {
    if(tokens === null) {
        console.log("DOING it later")
        Q.push(tmp);
        await refreshTokens();
    } else if(startedWaiting || timeTillNeedRefresh <= 0) {
        console.log("NOT for a while");
        Q.push(tmp);
    } else {
        console.log("THIS is what should happen");
        start = Date.now();
        tmp();
    }
}

async function updateFile(opts) {
    var id = opts.id,
        content = opts.content || "";
    if(id) {
        allFuncs(() => {
            myFetch({
                url:`https://www.googleapis.com/upload/drive/v3/files/${id}?uploadType=media`,
                method:"PATCH",
                headers: {
                    body: opts.content,
                    "Content-Type": "application/vnd.google-apps.document"
                },
                cb(b) {
                    console.log(b, "DID I DO IT?!");
                }
            });
        });
    }
}

我尝试查找此错误的含义,但找不到与 nodejs 相关的任何内容... 有谁知道这是标题问题还是有什么办法可以解决?

我不知道是否可以使用服务密钥,或者您是否必须验证用户才能这样做??

如果服务密钥电子邮件似乎具有文件的编辑权限,它应该能够随意编辑它。

【问题讨论】:

  • 为了纠正你的情况,我可以问你关于你的问题吗? 1. 你想用hi there 的文字覆盖谷歌文档吗? 2. Google 文档是否与服务帐户共享? 3. Node.js的googleapis能用吗? 4. 您是否只需要使用 Drive API?例如,使用 Google Docs API 怎么样?
  • @Tanaike 是的,是的,是的,我目前只是使用驱动器 API 我不知道现在 doc 和驱动器 API 之间存在差异,我可以使用我需要的任何东西
  • 感谢您的回复。我能理解你的问题。我提出了一个示例脚本作为答案。你能确认一下吗?如果这不是您想要的结果,我深表歉意。
  • 我的回答是否向您展示了您想要的结果?你能告诉我吗?这对我学习也很有用。如果这可行,与您有相同问题的其他人也可以将您的问题作为可以解决的问题。如果您对我的回答有疑问,我深表歉意。到时候,可以问一下你现在的情况吗?我想学习解决你的问题。
  • @Tanaike 是的,虽然我确实说过我可以使用 googleAPI,但我没有提到如果没有它我会感兴趣;仅仅使用 GET 请求,你知道怎么做吗?

标签: node.js google-drive-api google-apis-explorer


【解决方案1】:
  • 您希望使用服务帐户使用 Drive API 用 hi there 的文本覆盖现有的 Google 文档。
  • Google 文档与服务帐户共享。
  • 您可以使用 googleapis。

我可以像上面那样理解。如果我的理解是正确的,那么这个示例脚本怎么样?在这个示例脚本中,我使用了 Drive API 的 files.update 方法。

示例脚本:

在运行脚本之前,请设置服务账号创建时下载的json文件路径。请再次确认 Google Document 的文件 ID。

const stream = require('stream');
const {google} = require('googleapis');
const creds = require('###'); // Please set the json file path downloaded when the Service Account is created.
const jwtClient = new google.auth.JWT(
    creds.client_email,
    null,
    creds.private_key,
    ['https://www.googleapis.com/auth/drive'],
    null
);

const id = "1vebqOamZ9QB4HqfUaQN-U9Zt4y33eij-I21glMvaPVQ"; // Please set the file ID of Google Document
const content = "hi there"; // Please set the text.

const drive = google.drive({version: 'v3', auth: jwtClient});
const buf = Buffer.from(content, 'binary');
const buffer = Uint8Array.from(buf);
var bufferStream = new stream.PassThrough();
bufferStream.end(buffer);
const media = {
    mimeType: 'application/vnd.google-apps.document',
    body: bufferStream,
};
drive.files.update({
    fileId: id,
    media: media,
}, (err, res) => {
    if (err) {
        console.log(err);
        return;
    }
    console.log(res.data);
});

注意:

  • 当您运行脚本时,现有的 Google 文档会被覆盖。所以请注意这一点。我建议使用示例文档进行测试。
  • 如果此脚本不起作用,请确认以下几点。
    1. 在 API 控制台中启用了 Drive API。
    2. 可以使用服务帐号。
    3. Google 文档与服务帐户共享。
    4. googleapis 版本为最新版本。

参考:

编辑:

  • 您不想使用 googleapis。
  • 您想在不使用 googleapis 的情况下用文本值覆盖 Google 文档。

从您的 cmets 中,我理解如上。如果我的理解是正确的,那么这个示例脚本怎么样?运行此脚本时,请确认以下几点。

  1. 关于脚本,
    • 请从Service Account的JSON文件中设置privateKey和clientEmail。
    • 请设置您要覆盖的现有 Google 文档的文件 ID。
  2. 在 API 控制台中启用了 Drive API。
  3. 可以使用服务帐号。
  4. Google 文档与服务帐户共享。
  5. googleapis 版本为最新版本。

示例脚本:

const cryptor = require('crypto');
const request = require('request');

// Get access token using Service Account
function getAccessToken(serviceAccount) {
    const scopes = ["https://www.googleapis.com/auth/drive"];
    const url = "https://www.googleapis.com/oauth2/v4/token";
    const header = {
        alg: "RS256",
        typ: "JWT",
    };
    const now = Math.floor(Date.now() / 1000);
    const claim = {
        iss: serviceAccount.clientEmail,
        scope: scopes.join(" "),
        aud: url,
        exp: (now + 3600).toString(),
        iat: now.toString(),
    };
    const signature = Buffer.from(JSON.stringify(header)).toString('base64') + "." + Buffer.from(JSON.stringify(claim)).toString('base64');
    var sign = cryptor.createSign('RSA-SHA256');
    sign.update(signature);
    const jwt = signature + "." + sign.sign(serviceAccount.privateKey, 'base64');
    return new Promise(function(resolve, reject) {
        request({
            method: "post",
            url: url,
            body: JSON.stringify({
                assertion: jwt,
                grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
            }),
        }, (err, res, body) => {
            if (err) {
                console.log(err);
                return;
            }
            const obj = JSON.parse(body);
            resolve(obj.access_token);
        });
    });
}

// Overwrite file in Google Drive
function overWriting(object) {
    const metadata = {
        mimeType: "application/vnd.google-apps.document",
    };
    const url = "https://www.googleapis.com/upload/drive/v3/files/" + object.googleDocumentFileId + "?uploadType=multipart";
    const boundary = "xxxxxxxxxxx";
    var data = "--" + boundary + "\r\n";
    data += "Content-Disposition: form-data; name=\"metadata\"\r\n";
    data += "Content-Type: application/json; charset=UTF-8\r\n\r\n";
    data += JSON.stringify(metadata) + "\r\n";
    data += "--" + boundary + "\r\n";
    data += "Content-Disposition: form-data; name=\"file\"; filename=\"sample.txt\"\r\n";
    data += "Content-Type: text/plain" + "\r\n\r\n";
    const payload = Buffer.concat([
        Buffer.from(data, "utf8"),
        new Buffer(object.textData, 'binary'),
        Buffer.from("\r\n--" + boundary + "--", "utf8"),
    ]);
    const options = {
        method: 'patch',
        url: url,
        headers: {
            "Content-Type": "multipart/related; boundary=" + boundary,
            'Authorization': 'Bearer ' + object.accessToken,
        },
        body: payload,
    };
    request(options, (error, response, body) => {
        console.log(body);
    });

}

async function main() {
    const serviceAccount = {
        privateKey: "###", // private_key of JSON file retrieved by creating Service Account
        clientEmail: "###", // client_email of JSON file retrieved by creating Service Account
    };
    var object = {
        googleDocumentFileId: "###", // Set the file ID of the existing Google Document
        textData: "hi there",
    };
    const accessToken = await getAccessToken(serviceAccount);
    if (accessToken) {
        object.accessToken = accessToken;
        overWriting(object);
    }
}

main();

【讨论】:

  • @bluejayke 我知道您想在不使用 googleapis 的情况下用文本值覆盖 Google 文档。对于这种情况,我添加了一个示例脚本。你能确认一下吗?如果这不是您想要的,我很抱歉。
  • @bluejayke 我的附加示例脚本是否向您显示了您想要的结果?你能告诉我吗?这对我学习也很有用。如果您对我的回答有疑问,我深表歉意。到时候,可以问一下你现在的情况吗?我想学习解决你的问题。
  • @bluejayke 有什么可以解决您的问题的吗?如果我的回答对您的情况没有用。我必须道歉并修改它。如果您能合作解决您的问题,我很高兴。我想考虑一下解决方案。
  • (node:3136) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
  • @BartusZak 感谢您的评论。我更新了我的答案。你能确认一下吗?
猜你喜欢
  • 1970-01-01
  • 2019-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-23
  • 1970-01-01
相关资源
最近更新 更多