Node.js 无法将文件读取流从Google云存储传输到Google Drive API
我正在做一个项目,在这个项目中,我从Google Drive读取用创建的思维导图文件,修改这些文件,然后将它们上传回Google Drive SimpleMind创建的SMMX文件是包含XML文件和媒体文件的zip文件 我的程序在本地运行时运行良好,我对思维导图所做的更改显示在SimpleMind中 我现在想在谷歌云平台上使用应用程序引擎运行这个程序 由于安全限制,我不能将从Google Drive下载的文件写入云应用服务器的文件系统。相反,我创建了一个存储桶来存储文件 但是,当我执行此操作时,我的文件在运行程序后会损坏,它不是zip文件内容,而是JSON文件,显然是读取流的字符串表示 本地运行–工作 这是我的代码的简化版本,没有对zip文件进行实际修改,我省略了它,因为它与问题无关,也与任何错误处理无关——从来没有任何错误 在本地运行代码时,我使用写流和读流在本地文件系统上保存和加载文件:Node.js 无法将文件读取流从Google云存储传输到Google Drive API,node.js,google-app-engine,google-cloud-platform,google-drive-api,Node.js,Google App Engine,Google Cloud Platform,Google Drive Api,我正在做一个项目,在这个项目中,我从Google Drive读取用创建的思维导图文件,修改这些文件,然后将它们上传回Google Drive SimpleMind创建的SMMX文件是包含XML文件和媒体文件的zip文件 我的程序在本地运行时运行良好,我对思维导图所做的更改显示在SimpleMind中 我现在想在谷歌云平台上使用应用程序引擎运行这个程序 由于安全限制,我不能将从Google Drive下载的文件写入云应用服务器的文件系统。相反,我创建了一个存储桶来存储文件 但是,当我执行此操作时,
#!/usr/bin/env node
const { readFileSync, createReadStream, createWriteStream } = require('fs');
const { google } = require('googleapis');
const tokenPath = 'google-drive-token.json';
const clientId = 'xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com';
const redirectUri = 'urn:ietf:wg:oauth:2.0:oob';
const clientSecret = 'xxxxxxxxxxxxxxxxxxxxxxxx';
const fileId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const fileName = 'deleteme.smmx';
(async () => {
const auth = new google.auth.OAuth2(clientId, clientSecret, redirectUri);
const token = JSON.parse(readFileSync(tokenPath));
auth.setCredentials(token);
const writeStream = createWriteStream(fileName);
const drive = google.drive({ version: 'v3', auth });
let progress = 0;
const res = await drive.files.get({ fileId, alt: 'media' }, { responseType: 'stream' });
await new Promise(resolve => {
res.data.on('data', d => (progress += d.length)).pipe(writeStream);
writeStream.on('finish', () => {
console.log(`Done downloading file ${fileName} from Google Drive to local file system (${progress} bytes)`);
resolve();
});
});
const readStream = createReadStream(fileName);
progress = 0;
const media = {
mimeType: 'application/x-zip',
body: readStream
.on('data', d => {
progress += d.length;
})
.on('end', () => console.log(`${progress} bytes read from local file system`))
};
await drive.files.update({
fileId,
media
});
console.log(`File ${fileName} successfully uploaded to Google Drive`);
})();
当我在本地运行此脚本时,它工作正常,程序输出始终为:
已完成将文件deleteme.smmx从Google Drive下载到本地文件系统(371字节)
从本地文件系统读取371字节
文件deleteme.smmx已成功上载到Google Drive
我可以运行任意次数,每次都会在谷歌硬盘上创建新版本的文件,每个文件都有371字节大
在谷歌云中运行–不工作
下面是上面脚本的一个版本,我正在使用它来尝试做同样的事情,在谷歌云中,在应用程序引擎上运行,从谷歌硬盘下载并上传一个文件:
const { readFileSync } = require('fs');
const { google } = require('googleapis');
const { Storage } = require('@google-cloud/storage');
const tokenPath = 'google-drive-token.json';
const clientId = 'xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com';
const redirectUri = 'urn:ietf:wg:oauth:2.0:oob';
const clientSecret = 'xxxxxxxxxxxxxxxxxxxxxxxx';
const fileId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const fileName = 'deleteme.smmx';
const storageBucketId = 'xxxxxxxxxxx';
module.exports = async () => {
const auth = new google.auth.OAuth2(clientId, clientSecret, redirectUri);
const token = JSON.parse(readFileSync(tokenPath));
auth.setCredentials(token);
const storage = new Storage();
const bucket = storage.bucket(storageBucketId);
const file = bucket.file(fileName);
const writeStream = file.createWriteStream({ resumable: false });
const drive = google.drive({ version: 'v3', auth });
let progress = 0;
const res = await drive.files.get({ fileId, alt: 'media' }, { responseType: 'stream' });
await new Promise(resolve => {
res.data.on('data', d => (progress += d.length)).pipe(writeStream);
writeStream.on('finish', () => {
console.log(`Done downloading file ${fileName} from Google Drive to Cloud bucket (${progress} bytes)`);
resolve();
});
});
const readStream = file.createReadStream();
progress = 0;
const media = {
mimeType: 'application/x-zip',
body: readStream
.on('data', d => {
progress += d.length;
})
.on('end', () => console.log(`${progress} bytes read from storage`))
};
await drive.files.update({
fileId,
media
});
console.log(`File ${fileName} successfully uploaded to Google Drive`);
return 0;
};
这里唯一的区别是,我没有使用Node.jsfs
模块,而是使用了相应的方法和Google云存储库
当我第一次在云中的App Engine上运行此代码时,一切似乎都正常,输出与我在本地运行时相同:
已完成将文件deleteme.smmx从谷歌硬盘下载到云存储桶(371字节)
从存储器中读取371字节
文件deleteme.smmx已成功上载到Google Drive
但是,当我在Google Drive web前端查看该文件的最新版本时,它不再是我的smmx文件,而是一个JSON文件,它看起来像读取流的字符串表示:
{
"_readableState": {
"objectMode": false,
"highWaterMark": 16384,
"buffer": { "head": null, "tail": null, "length": 0 },
"length": 0,
"pipes": null,
"pipesCount": 0,
"flowing": true,
"ended": false,
"endEmitted": false,
"reading": false,
"sync": false,
"needReadable": true,
"emittedReadable": false,
"readableListening": false,
"resumeScheduled": true,
"paused": false,
"emitClose": true,
"destroyed": false,
"defaultEncoding": "utf8",
"awaitDrain": 0,
"readingMore": false,
"decoder": null,
"encoding": null
},
"readable": true,
"_events": {},
"_eventsCount": 4,
"_writableState": {
"objectMode": false,
"highWaterMark": 16384,
"finalCalled": false,
"needDrain": false,
"ending": false,
"ended": false,
"finished": false,
"destroyed": false,
"decodeStrings": true,
"defaultEncoding": "utf8",
"length": 0,
"writing": false,
"corked": 0,
"sync": true,
"bufferProcessing": false,
"writecb": null,
"writelen": 0,
"bufferedRequest": null,
"lastBufferedRequest": null,
"pendingcb": 0,
"prefinished": false,
"errorEmitted": false,
"emitClose": true,
"bufferedRequestCount": 0,
"corkedRequestsFree": { "next": null, "entry": null }
},
"writable": true,
"allowHalfOpen": true,
"_transformState": {
"needTransform": false,
"transforming": false,
"writecb": null,
"writechunk": null,
"writeencoding": null
},
"_destroyed": false
}
看起来,将读取流从云存储桶传输到写入流以上载到Google Drive的方式并不像我希望的那样工作
我做错了什么?我需要更改什么才能使代码在云中正确运行?
如果你感兴趣的话,请点击
更新:解决方案
我找到了解决这个问题的方法:
- 将读取流中的数据从云存储桶读取到缓冲区中
- 从这个缓冲区创建一个可读的流
- 将此“缓冲流”传递给
drive.files.update方法
我更愿意让从云存储桶到Google Drive API的直接管道工作。显然,您可以使用直通流
const file = storage.bucket(bucketName).file(object.name)
const fileStream = file.createReadStream();
const dataStream = new stream.PassThrough();
fileStream.pipe(dataStream);
await uploadFileToGDrive(dataStream, {
name: object.name,
mimeType: object.contentType,
parents: ['shared_dir_in_g_drive'],
})
src:试试
sys.pump
方法,看看是否有帮助。还可以尝试只使用body:readStream
,而不使用stream@TarunLalwani谢谢你的评论sys.pump
或后来被称为util.pump
自Node.js 4.0.0版以来已被弃用,并从后续版本中删除。我正在使用Node.js版本10(LTS)。所以我认为你链接的答案已经过时了。不幸的是,删除事件处理程序上的也没有帮助。那么json文件也会出现在第一个应用程序引擎运行中吗?或者从第二次开始?在应用程序引擎第一次运行脚本后,Google Drive上的文件将替换为JSONcode@PatrickHund如果在创建写流时指定?