嗨,我正在尝试使用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相关的任何信息... 有人知道它是否为标头问题,还是有任何解决方法?
我不知道是否可以使用服务密钥,或者您是否必须验证用户身份才能这样做?
如果服务密钥电子邮件具有文件的编辑权限,则应该可以随意对其进行编辑。
答案 0 :(得分:0)
hi there
的文本覆盖现有的Google文档。我可以像上面那样理解。如果我的理解是正确的,那么该示例脚本如何?在此示例脚本中,我使用了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);
});
从您的评论中,我可以像上面那样理解。如果我的理解是正确的,那么该示例脚本如何?运行此脚本时,请确认以下几点。
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();