使用 Cloud Function 从 URL 下载 JSON 文件,然后上传到 Cloud Storage 存储桶,状态 200 但上传的 JSON 文件只有 20 个字节且为空

Use Cloud Function download a JSON file from URL then upload to a Cloud Storage bucket, status 200 but JSON file uploaded is only 20 bytes and empty

我正在尝试使用云功能从此处下载 JSON 文件:http://jsonplaceholder.typicode.com/posts? 然后将其上传到 Cloud Storage 存储桶。

函数执行的日志看起来不错,状态returns 200。但是,上传到存储桶的JSON文件只有20字节,而且是空的(原始文件约为 27 KB)


所以如果我遗漏了什么,请帮助我,这里有代码和日志:

index.js

const {Storage} = require('@google-cloud/storage');
    
exports.writeToBucket = (req, res) => {
    const http = require('http');
    const fs = require('fs');
    
    const file = fs.createWriteStream("/tmp/post.json");
    const request = http.get("http://jsonplaceholder.typicode.com/posts?", function(response) {
      response.pipe(file);
    });
    
    
    console.log('file downloaded');
    
    // Imports the Google Cloud client library
    const {Storage} = require('@google-cloud/storage');
    
    // Creates a client
    const storage = new Storage();
    const bucketName = 'tft-test-48c87.appspot.com';
    const filename = '/tmp/post.json';

    // Uploads a local file to the bucket
    storage.bucket(bucketName).upload(filename, {
      gzip: true,
      metadata: {
        cacheControl: 'no-cache',
      },
    });

    res.status(200).send(`${filename} uploaded to ${bucketName}.`);
    
};

package.json

{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
        "@google-cloud/storage": "^3.0.3"
    }
}

结果:

日志:

我写的 NodeJS 不多,但我认为你的问题出在异步代码上。

您创建流然后发出 http.get 但在开始 GCS 上传之前您没有阻止回调(管道文件)完成。

您可能需要将 .on("finish", () => {...}) 附加到 pipe 并在该回调中将文件上传到 GCS。

NOTE IIRC GCS has a method that will let you write a stream directly from memory rather than going through a file.

NOTE if you pull the storage object up into the global namespace, it will only be created whenever the instance is created and not every time the function is invoked.

您不需要写入流来获取 URL 数据,获取 URL,等待响应解析,调用适当的 response.toJson() 方法。

就个人而言,与 http 相比,我更喜欢使用 Fetch 和 Axios,因为它们更易于使用。但是使用节点 http 您可以执行以下操作:


https.get(url,(res) => {
    let body = "";

    res.on("data", (chunk) => {
        body += chunk;
    });

    res.on("end", () => {
        try {
            let json = JSON.parse(body);
            // do something with JSON
        } catch (error) {
            console.error(error.message);
        };
    });

}).on("error", (error) => {
    console.error(error.message);
});

一旦你有了它,你就可以将它作为数据 blob 或字节数组直接传递给存储方法。

byte[] byteArray = resultJson.toString().getBytes("UTF-8");

正如 @DazWilkin 所指出的,异步代码存在问题。您必须等待 onfinish() 触发,然后才能继续。 upload() 方法 returns 也是一个承诺。尝试使用 async-await 语法重构您的函数,如下所示:

exports.writeToBucket = async (req, res) => {
  const http = require('http');
  const fs = require('fs');

  // Imports the Google Cloud client library
  const {Storage} = require('@google-cloud/storage');
    
  // Creates a client
  const storage = new Storage();
  const bucketName = 'tft-test-48c87.appspot.com';
  const filename = '/tmp/post.json';  

  await downloadJson()

  // Uploads a local file to the bucket
  await storage.bucket(bucketName).upload(filename, {
    gzip: true,
    metadata: {
      cacheControl: 'no-cache',
    },
  });

  res.status(200).send(`${filename} uploaded to ${bucketName}.`);
}

const downloadJson = async () => {
 const Axios = require('axios')
 const fs = require("fs")
 const writer = fs.createWriteStream("/tmp/post.json")
 const response = await Axios({
   url: "http://jsonplaceholder.typicode.com/posts",
   method: 'GET',
   responseType: 'stream'
 })
 response.data.pipe(writer)
 return new Promise((resolve, reject) => {
   writer.on('finish', resolve)
   writer.on('error', reject)
 })
}

此示例使用 Axios,但您可以使用 http 执行相同的操作。

请注意,您可以直接将获取的 JSON 作为文件上传:

exports.writeToBucket = async (req, res) => {
  const Axios = require("axios");
  const { Storage } = require("@google-cloud/storage");

  const storage = new Storage();
  const bucketName = "tft-test-48c87.appspot.com";
  const filename = "/tmp/post.json";

  const { data } = await Axios.get("http://jsonplaceholder.typicode.com/posts");

  const file = storage.bucket(bucketName).file("file.json");
  const contents = JSON.stringify(data);
  await file.save(contents);

  res.status(200).send(`${filename} uploaded to ${bucketName}.`);
};

您可以在 documentation 中阅读有关 save() 方法的更多信息。