【问题标题】:How to add media upload for BigQuery Rest API using UrlFetchApp?如何使用 UrlFetchApp 为 BigQuery Rest API 添加媒体上传?
【发布时间】:2020-01-23 08:56:51
【问题描述】:

我需要从我的 Google Apps 脚本插件将数据流式传输到 BigQuery。

但我只需要使用我的服务帐号(我需要将数据插入我的 BigQuery 表,而不是用户的 BigQuery 表)

我按照这个例子:https://developers.google.com/apps-script/advanced/bigquery#load_csv_data

因为 Apps Script Advanced Service 本身不支持服务帐号,所以我需要稍微修改一下这个例子:

我需要从我的服务帐户获取 OAuth 令牌,而不是使用 Advanced Service BigQuery,然后使用 BigQuery Rest API 来处理相同的工作:

这就是我所做的:

function getBigQueryService() {
  return (
    OAuth2.createService('BigQuery')
      // Set the endpoint URL.
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')

      // Set the private key and issuer.
      .setPrivateKey(PRIVATE_KEY)
      .setIssuer(CLIENT_EMAIL)

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getScriptProperties())

      // Caching
      .setCache(CacheService.getUserCache())

      // Locking
      .setLock(LockService.getUserLock())

      // Set the scopes.
      .setScope('https://www.googleapis.com/auth/bigquery')
  )
}

export const insertLog = (userId, type) => {
  const bigQueryService = getBigQueryService()
  if (!bigQueryService.hasAccess()) {
    console.error(bigQueryService.getLastError())
    return
  }

  const projectId = bigqueryCredentials.project_id
  const datasetId = 'usage'
  const tableId = 'logs'
  const row = {
    timestamp: new Date().toISOString(),
    userId,
    type,
  }

  const data = Utilities.newBlob(convertToNDJson(row), 'application/octet-stream')

  // Create the data upload job.
  const job = {
    configuration: {
      load: {
        destinationTable: {
          projectId,
          datasetId,
          tableId,
        },
        sourceFormat: 'NEWLINE_DELIMITED_JSON',
      },
    },
  }

  const url = `https://bigquery.googleapis.com/upload/bigquery/v2/projects/${projectId}/jobs`
  const headers = {
    Authorization: `Bearer ${bigQueryService.getAccessToken()}`,
    'Content-Type': 'application/json',
  }

  const options = {
    method: 'post',
    headers,
    payload: JSON.stringify(job),
  }

  try {
    const response = UrlFetchApp.fetch(url, options)
    const result = JSON.parse(response.getContentText())

    console.log(JSON.stringify(result, null, 2))
  } catch (err) {
    console.error(err)
  }
}

正如您在我的代码中看到的,我使用以下行获取 Blob 数据(这是我需要放入 BigQuery 表中的实际 json 数据):

const data = Utilities.newBlob(convertToNDJson(row), 'application/octet-stream')

但我不知道在哪里使用这个 data 和 BigQuery Rest API

文档没有提及:https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/insert

如何做到这一点?谢谢。

【问题讨论】:

  • 您需要将文件和作业配置作为'multipart/related' 数据发送。请参阅相关的example。用于为您处理它的高级 Google 服务。现在,您需要自己完成此操作
  • 作为@TheMaster 评论的附加信息,使用this GAS library 怎么样?该库使用 Google Apps 脚本创建并请求 multipart/form-data 的类型。
  • @Tanaike:我看到 TheMaster 提到的是您的代码。有没有关于那些“\r\n”和“--”的文档。仅通过阅读代码很难理解该约定。另外,感谢您的图书馆,如果手动方式不起作用,我想我稍后会使用它。
  • @Tanaike:谢谢。我的意思是the manual way 就是您使用\r\n-- 构建字符串的方式。但是我认为您提供的链接是我需要了解的developer.mozilla.org/en-US/docs/Web/HTTP/Headers/…
  • 还可以查看rfc2387rfc7578。尽管字体不好,但实际上更容易理解。

标签: google-apps-script google-bigquery google-apps-script-addon


【解决方案1】:

我可以使用 Tanaike 的 FetchApp 库解决这个问题:

https://github.com/tanaikech/FetchApp#fetch

以后有人会遇到这个问题:请检查我在代码中的注释以了解已完成的操作。

原来,job 变量在表单数据对象中被视为metadatadata 变量被视为file

// First you need to convert the JSON to Newline Delimited JSON,
// then turn the whole thing to Blob using Utilities.newBlob

const data = Utilities.newBlob(convertToNDJson(row), 'application/octet-stream')

  // Create the data upload job.
  const job = {
    configuration: {
      load: {
        destinationTable: {
          projectId,
          datasetId,
          tableId,
        },
        sourceFormat: 'NEWLINE_DELIMITED_JSON',
      },
    },
  }

  const url = `https://bigquery.googleapis.com/upload/bigquery/v2/projects/${projectId}/jobs?uploadType=multipart`
  const headers = {
    Authorization: `Bearer ${bigQueryService.getAccessToken()}`,
  }

  const form = FetchApp.createFormData() // Create form data
  form.append('metadata', Utilities.newBlob(JSON.stringify(job), 'application/json'))
  form.append('file', data)

  const options = {
    method: 'post',
    headers,
    muteHttpExceptions: true,
    body: form,
  }

  try {
    FetchApp.fetch(url, options)
  } catch (err) {
    console.error(err)
  }

注意:创建服务帐号时,选择角色BigQuery Admin,或任何具有bigquery.jobs.create权限的角色

https://cloud.google.com/bigquery/docs/access-control#bigquery-roles

因为如果你不这样做,你就会有错误

用户没有 bigquery.jobs.create 权限...

【讨论】:

    猜你喜欢
    • 2016-09-15
    • 2023-03-17
    • 1970-01-01
    • 2015-11-18
    • 1970-01-01
    • 2020-12-02
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    相关资源
    最近更新 更多