NodeJS Google Drive API如何更新文件

时间:2019-03-25 10:26:41

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

嗨,我正在尝试使用Google Drive API使用NodeJS更新Google文档,但出现此错误:

{
 "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 个答案:

答案 0 :(得分:0)

  • 您要使用服务帐户通过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 = new Buffer(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控制台上启用了驱动器API。
    2. 可以使用服务帐户。
    3. Google文档已与服务帐户共享。
    4. 最新版本的googleapis。

参考:

编辑:

  • 您不想使用googleapis。
  • 您要使用文本值覆盖Google文档,而不使用googleapis。

从您的评论中,我可以像上面那样理解。如果我的理解是正确的,那么该示例脚本如何?运行此脚本时,请确认以下几点。

  1. 关于脚本,
    • 请从服务帐户的JSON文件中设置privateKey和clientEmail。
    • 请设置您要覆盖的现有Google文档的文件ID。
  2. 在API控制台上启用了驱动器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();