我在 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