简体   繁体   中英

NodeJS Google Drive API how to update file

Hi I'm trying to update a Google Doc with NodeJS using the Google Drive API and I'm getting this error:

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

Here's the relevant code :

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?!");
                }
            });
        });
    }
}

I tried looking up what this error means but I couldn't find anything related to nodejs... Does anyone know if its a header issue or is there any way to fix it?

I don't know if its possible to do wih a service key, or if you HAVE to verify the user in order to do so??

SEEMINGLY if the service key email has edit permissions for the file, it should just be able to edit it at will.

  • You want to overwrite the existing Google Document by a text of hi there with Drive API using Service Account.
  • The Google Document is shared with the Service Account.
  • You can use googleapis.

I could understand like above. If my understanding is correct, how about this sample script? In this sample script, I used the files.update method of Drive API.

Sample script:

Before you run the script, please set the json file path downloaded when the Service Account is created. And please confirm the file ID of Google Document, again.

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);
});

Note:

  • When you run the script, the existing Google Document is overwritten. So please be careful this. I recommend to use a sample Document for testing.
  • If this script didn't work, please confirm the following points.
    1. Drive API is enabled at API console.
    2. The Service Account can be used.
    3. The Google Document is shared with the Service Account.
    4. The version of googleapis is the latest one.

Reference:

Edit:

  • You don't want to use googleapis.
  • You want to overwrite Google Document with a text value without using googleapis.

From your comments, I understood like above. If my understanding is correct, how about this sample script? When you run this script, please confirm the following points.

  1. About the script,
    • Please set privateKey and clientEmail from JSON file of Service Account.
    • Please set the file ID of the existing Google Document you want to overwrite.
  2. Drive API is enabled at API console.
  3. The Service Account can be used.
  4. The Google Document is shared with the Service Account.
  5. The version of googleapis is the latest one.

Sample script:

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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM