我在 Hackernoon 上有一个任务,该任务需要使用 GitHub API 获取一些文件。起初,我认为这将会是一个简单的 GET 请求,但很快我意识到是我把问题想简单了。

我在 Hackernoon 上有一个任务,该任务需要使用 GitHub API 获取一些文件。起初,我认为这将会是一个简单的 GET 请求,但很快我意识到是我把问题想简单了。从刚开始 GET 请求的结果来看,我需要使用一些我目前还没有掌握的方法,因为需要获取的文件非常大,最终在这个问题上我花费了两天的时间。

现在是时候写一篇文章了,这样以后我和其他人就都不必在一个简单的问题上花费太多时间了。

在本文中,我将向您展示如何使用 GitHub API 获取大于 1MB 的文件。学习完本文章后,您将会了解以下内容:

  • 如何使用 GitHub API 获取大文件

  • 如何使用commit SHA

  • 使用 blob  base64 编码数据

在本文中,我将使用了来自 HackerNoon 的年度创业大赛(https://startups.hackernoon.com/faq?ref=hackernoon.com)的数据。 该部分数据在 GitHub上是公开的。

获取小于1MB的文件

获取较小的文件很简单,您只需将三个参数传递给 API。 首先是所有者的姓名,可以是您的用户名。第二个是 repo,它是提交文件的存储库。path 是文件的绝对路径。

下面是我使用codepen创建的一小段代码,供大家参考。

let fileSHA, fileBlob, fileContent, file

const getFileSHA = async () => {
  try {
    const response = await fetch(
      "https://api.github.com/repos/hackernoon/where-startups-trend/contents/2021/"
    );
    const data = await response.json();
    // console.log(data);
    
    fileSHA = data[1].sha
    console.log(fileSHA);

  } catch (error) {
    console.log(error);
  }
  
  getFileBlob()
}

const getFileBlob = async (fileSHA)=> {
  try {
  const response = await fetch(
    `https://api.github.com/repos/hackernoon/where-startups-trend/git/blobs/a51a49dfc2bd7be262bd59bb85e85271ea0c18cd`
  );
  const data = await response.json();
    
  fileBlob = data.content
  convertBlob(fileBlob)
  } catch (error) {
    console.log(error);
  }
}

const convertBlob = async blob => {
  // console.log(blob)
  try {
    
    // const fileContents = Buffer.from(blob, "base64").toString()
    // file = JSON.parse(fileContents)
    // file = JSON.parse(fileContents)
  
    fileContents = base64EncodeUnicode(blob)
    file = JSON.parse(fileContents)
    console.log(file)
    
  } catch(error) {
    console.log(error)
  }
}

function base64EncodeUnicode(str) {
  utf8Bytes = decodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
    return String.fromCharCode('0x' + p1);
  });

  return atob(utf8Bytes);
}


getFileSHA()1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.

为了更好地理解上述代码,我创建了四个变量,用来存储不同状态下的数据。

let fileSHA, fileBlob, fileContent, file1.
  • fileSHA用来存储我们想要获取的文件的 SHA

  • fileBlob存储从API 获取的 blob。

  • fileContent用于存储解码后的字符串。

  • file包含我们真正需要的数据。

您可以在下一节中找到对应的参考代码,只需将文件夹路径更改为实际文件路径(带文件扩展名)。

获取大文件

但是,如果您需要获取大于 1MB 的文件,API 会抛出错误。这是因为上述方式不支持大文件。若需要获取大文件,我们必须改为使用 Github API。

这类API允许您从 Git 存储库和列表中读取和写入原始 Git 对象,也可以更新 Git 引用(Git references)。 该类API主要适用于blob,这正是本文的重点。

Git将大文件转换为base64 编码的 blob,而不是存储整个文件,以便获得更好的性能。因此,当您请求同一个文件时,希望您使用返回 blob的接口。

要获取blob,您需要传递文件对应的SHA。您可能想问,什么是 SHA?又该如何获得它?

每次提交新文件时,git 都会创建一个名为 hash 或 SHA 的唯一ID 来记录此次更改。但有时可能会导致问题,我将在本文末尾讨论这个问题。

获取文件对应的SHA

现在,我们需要找到一种方法来获取所需文件的“SHA”。GitHub API有一个接口,可以用来同我们需要使用的文件内容进行交互。

GET /repos/{owner}/{repo}/contents/{path}1.

作为响应,我们得到一个有关对象的数组,其中包含目录中每个文件的元数据。 元数据中包含我们可以存储和后面会用到的SHA。

const getFileSHA = async () => {
  try {
    const response = await fetch(
      "<https://api.github.com/repos/hackernoon/where-startups-trend/contents/2021/>"
    );
    const data = await response.json();
    console.log(data);

    fileSHA = data[3].sha
    console.log(fileSHA);

  } catch (error) {
    console.log(error);
  }

  getFileBlob(fileSHA)
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.

data[3].sha 是具有 votes_by_region.jso n 的 SHA,因此我们将其存储在 fileSHA 中以备后面使用。 下图是控制台的输出情况:

获取对应的Blob

我们已经获取了blob所需SHA。 我们需要使用不同的接口来处理 blob。 这个接口与上一个接口相似,但该接口需要将文件对应的SHA 作为第三个参数传递。

GET /repos/{owner}/{repo}/git/blobs/{file_sha}1.

当我们向下面的函数提供文件的 SHA 作为参数时,就可以获取对应blob。

const getFileBlob = async (fileSHA)=> {
  try {
  const response = await fetch(
    `https://api.github.com/repos/hackernoon/where-startups-trend/git/blobs/${fileSHA}`
  );
  const data = await response.json();

  fileBlob = data.content
  convertBlob(fileBlob)
  } catch (error) {
    console.log(error);
  }
}1.2.3.4.5.6.7.8.9.10.11.12.13.

此请求返回一个 base64 编码的 blob。 Base64 是一种以 ASCII 字符串格式表示二进制数据的编码方案。 当我们需要在不进行任何修改的情况下存储或传输数据时,它很有用。控制台输出看起来很乱,但这就是 base64 编码数据的方式。

将 Blob 转换为可使用的数据

在最后一步,我们需要将以 base64 编码的 blob 转换为我们可以在程序中使用的数据。 但这一步非常令人沮丧,因为标准方法在某些情况下似乎不起作用。在这个问题上花了几个小时后,我找到了三个解决方案。

1.最简单的方法

这是 JavaScript 原生支持的标准方式。 atob() 是一个 Web API 接口,可将 base64 编码数据解码为纯字符串。

atob 把读取的 ASCII 转化为二进制,因为它将以 ASCII 编码的 base64 数据转换为二进制形式。 输出将是一个解码后的字符串。 要将其转换为其原始数据类型,我们使用 JSON.parse()。

fileContents = atob(blob)
file = JSON.parse(fileContents)1.2.

2.第二种方法

如果上述方法抛出错误,你可以使用Node.js 提供了一个方法 Buffer.from()。 它将要解码的字符串作为第一个参数,将编码技术作为第二个参数。

try {
  const fileContents = Buffer.from(fileBlob, "base64").toString()
  file = JSON.parse(blobToString)

  console.log(file)
} catch(error) {
  console.log(error)
}1.2.3.4.5.6.7.8.

3.第三种方法

如果您在前端工作,则前面提到的方法很有可能不起作用。 在这种情况下,创建一个使用 decodeURIComponent 的函数。

// define the function
const decodeBase64 = str => {
  utf8Bytes = decodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
    return String.fromCharCode('0x' + p1);
  });

  return atob(utf8Bytes);
}1.2.3.4.5.6.7.8.

您可以通过传递 fileBlob 作为参数来调用此函数,并获取您需要的解码字符串。

fileContents = decodeBase64(fileBlob)
file = JSON.parse(fileContents)

console.log(file)1.2.3.4.

依然不能正常工作

此时,您可以尝试两种最终选择。您可以使用 FileReader API 或以不同的方式使用 decodeURIComponent。

1.FileReader API:您可以在此处找到 MDN 文档(https://developer.mozilla.org/en-US/docs/Web/API/FileReader?ref=hackernoon.com)

2.decodeURIComponent:请参阅 Rajeev Singh关于处理 16 位编码字符串的博客。(https://www.base64decoder.io/javascript/?ref=hackernoon.com)

或者,您也可以尝试一个名为 js-base64(https://www.npmjs.com/package/js-base64?ref=hackernoon.com)的 NPM 包。我自己没有使用过,所以无法提供建设性的意见。

注意

如前所述,SHA 是在每次修改文件时都会更改的具有唯一性的ID号。此外,由于我们在 getFileSHA() 函数中有一个硬编码的对象索引,如果您在目录中添加或删除文件,API 可能会以不同文件的 SHA 响应。

为了解决这个问题,您可以明确指明的文件名而不是索引,这样您就可以在提交新更改后保持函数不变。

总结和 TL;DR

1.Git 以 blob 格式存储较大的文件,因此我们使用 GitHub 数据库 API 来获取大于 1MB 的文件。

2.我们需要提供用户名、repo 名称和文件的 SHA 来获取 blob。

3.为了获取 SHA,我们需要向接口提供文件夹路径并将其存储在变量中。

4.之后我们需要将 SHA 作为参数传递给另一个接口并获得一个 base64 编码的 blob。

5.必须将 blob 解码为纯字符串,然后使用 JSON.parse() 将其转换为原始格式,然后才能使用它。

更多参考资料

GitHub 存储库的内容(https://docs.github.com/en/rest/reference/repos?ref=hackernoon.com#contents)

Git 数据库(https://docs.github.com/en/rest/reference/git?ref=hackernoon.com)

Git 数据库 API 入门(https://docs.github.com/en/rest/guides/getting-started-with-the-git-database-api?ref=hackernoon.com)

译者介绍

赵青窕,51CTO社区编辑,从事多年驱动开发。研究兴趣包含安全OS和网络安全领域,发表过网络相关专利。

原文标题:How to Fetch Large Data Files Through GitHub API,作者:Kaushal Joshi