首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >无法将文件从读取到

无法将文件从读取到
EN

Stack Overflow用户
提问于 2019-07-22 18:36:06
回答 1查看 3.5K关注 0票数 6

我正在进行一个项目,在这个项目中,我正在阅读从Google创建的用SimpleMind创建的思维地图文件,修改这些文件,然后将它们上传回Google。

SimpleMind创建的SMMX文件是zip文件,其中包含XML文件和媒体文件。

当我在本地运行它时,我的程序工作得很好,我对思维图所做的更改显示在SimpleMind中。

现在我想使用App在Google平台上运行这个程序。

由于安全限制,我不能将从Google下载的文件直接写入云中应用服务器的文件系统。相反,我创建了一个存储桶来将文件存储在那里。

但是,当我这样做时,我的文件在运行我的程序之后就会损坏,而不是zip文件内容,它是一个JSON文件,显然是读流的字符串表示。

运行本地工作的

这是我代码的简化版本,在没有实际修改zip文件的情况下,我忽略了这一点,因为它与问题无关,也与任何错误处理无关--从来没有任何错误。

当我在本地运行代码时,我使用写流和读流在本地文件系统上保存和加载文件:

代码语言:javascript
复制
#!/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下载到本地文件系统(371字节) 从本地文件系统读取的371字节 文件deleteme.smmx成功上传到Google

我可以任意多次运行它,每次都会在Google上创建新版本的文件,每个文件都有371字节大。

运行在Google中的-不工作的

下面是上面脚本的一个版本,我试图做同样的事情,下载并上传一个文件,运行在App上的Google中,并上传到Google Drive中:

代码语言:javascript
复制
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;
};

这里唯一的区别是,我使用的不是createWriteStreamcreateReadStream来自Node.js fs模块,而是使用了Google库中的相应方法file.createWriteStreamfile.createReadStream

当我第一次在云中的App上运行这段代码时,似乎一切正常,输出与在本地运行时相同:

完成将文件deleteme.smmx从Google下载到云桶(371字节) 从存储器读取的371字节 文件deleteme.smmx成功上传到Google

然而,当我查看Google前端文件的最新版本时,它不再是我的smmx文件,而是一个JSON文件,它看起来像读流的字符串表示:

代码语言:javascript
复制
{
  "_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并不像我希望的那样工作。

我做错什么了?要使代码在云中正确运行,需要更改什么?

如果你感兴趣的话,我的项目的完整源代码可以在GitHub上找到。

更新:的解决方案

我找到了解决这个问题的方法:

  • 将从云存储桶读取的数据读入缓冲区
  • 从此缓冲区如本教程所述创建可读流
  • 将此“缓冲区流”传递给drive.files.update方法

这样,Google上的zip文件就不会被破坏,一个新版本的内容会像预期的那样存储在相同的内容中。

不过,我觉得这很难看。对于大型的mind映射文件,例如那些包含许多图像的文件,它会对服务器造成压力,因为文件的全部内容必须存储在内存中。

我更愿意让从云存储桶到的直接管道工作。

EN

回答 1

Stack Overflow用户

发布于 2020-05-20 08:04:37

显然,您可以使用一个通过流。

代码语言:javascript
复制
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:https://github.com/googleapis/google-api-nodejs-client/issues/2015

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57151911

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档