Как получить большие файлы данных через API GitHub

Во время моей стажировки в Hackernoon у меня была задача получить несколько файлов из GitHub API. Изначально я думал, что это будет простой GET-запрос, но вскоре понял, что работа превратилась в головную боль. Нужный мне файл был довольно большим, и поэтому ответ подсказывал мне, что нужно использовать что-то другое, чем я уже знал. В итоге я потратил на эту проблему два дня.

Теперь пришло время написать статью, чтобы моему будущему «я» и другим не пришлось тратить столько времени на простой вопрос.

В этой статье я покажу вам, как получить файлы размером более 1 МБ из API GitHub. До конца вы узнаете о следующих темах:

  • Получение больших файлов из GitHub API
  • Работа с SHA коммита
  • Манипулирование блобами и данными в кодировке base64

Для целей этой статьи я использую данные конкурса HackerNoon’s Startup of the Year. Данные находятся в открытом доступе на GitHub, если вы хотите проследить за ними.

Получение файлов размером менее 1 МБ

Получить файлы меньшего размера очень просто, нужно лишь передать API три параметра. Первый — это имя владельца, которое может быть вашим именем пользователя. Второй — repo, это имя репозитория, в котором зафиксирован ваш файл. И path — это путь к файлу от корня.

Я создал простой codepen, если вы хотите использовать рабочие фрагменты кода.

Для лучшего понимания я создал четыре переменные для хранения данных в каждом состоянии.

let fileSHA, fileBlob, fileContent, file
Вход в полноэкранный режим Выход из полноэкранного режима

Вы можете найти код ссылки в следующем разделе, просто измените путь к папке на реальный путь к файлу (с расширением файла) и все готово!

Проблема с большими файлами

Однако если вы получите файл размером более 1 МБ, API выдаст ошибку. Это связано с тем, что указанная выше конечная точка не поддерживает большие файлы. Чтобы получить данные, мы должны использовать API базы данных Git.

Git Data API позволяет вам читать и записывать необработанные объекты Git в и из вашего репозитория и списка Git, а также обновлять ссылки Git. Этот API в основном работает с блобами, о которых и пойдет речь в этой статье.

Для повышения производительности Git преобразует большой файл в блоб в кодировке base64, а не хранит его целиком. В результате, когда вы запрашиваете тот же файл, он ожидает, что вы используете конечную точку Database, которая возвращает блоб.

Чтобы получить блоб, вам нужно передать SHA файла. Вы можете спросить, что такое SHA и как его получить?

Каждый раз, когда вы фиксируете новый файл, git создает уникальный ID, называемый hash или SHA, чтобы сохранить запись об изменениях. Иногда это может вызвать проблему, но об этом мы поговорим в конце статьи.

Получение SHA файла

На данный момент нам нужно найти способ получить SHA нужного файла. API GitHub имеет конечную точку для взаимодействия с содержимым файла, которую мы можем использовать.

GET /repos/{owner}/{repo}/contents/{path}
Вход в полноэкранный режим Выйти из полноэкранного режима

В ответ мы получаем массив объектов, содержащих метаданные о каждом файле в каталоге. Метаданные содержат 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)
}
Вход в полноэкранный режим Выход из полноэкранного режима

data[3].sha содержит SHA файла votes_by_region.json, поэтому мы сохраняем его в fileSHA для дальнейшего использования. Консольный вывод будет выглядеть примерно так:

Fetch Blob of the file

У нас есть SHA файла, который необходим для получения блоба. Нам нужно использовать другую конечную точку для работы с блобами. Эта конечная точка аналогична предыдущей, но в качестве третьего параметра мы должны передать SHA файла.

GET /repos/{owner}/{repo}/git/blobs/{file_sha}
Вход в полноэкранный режим Выход из полноэкранного режима

Следующая функция извлекает блоб нашего файла, когда мы передаем его SHA в качестве параметра.

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);
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Этот запрос возвращает блоб в кодировке base64. Base64 — это схема кодирования, которая представляет двоичные данные в формате строк ASCII. Она полезна, когда нам нужно хранить или передавать данные без каких-либо изменений. Вывод на консоль будет выглядеть тарабарщиной, но это именно то, как кодируются данные base64.

Преобразование блоба в полезные данные

На последнем этапе мы должны преобразовать блоб в кодировке base64 во что-то, что мы можем использовать в нашей программе. Этот шаг довольно сложный, потому что стандартные способы в некоторых случаях не работают.

Потратив несколько часов на решение этой проблемы, я нашел три решения. Я не гарантирую, что каждое решение сработает для вас, но я уверен, что одно из них обязательно сработает.

Самый простой способ

Это стандартный способ, изначально поддерживаемый JavaScript. atob() — это интерфейс Web API, который декодирует закодированные base64 данные в обычную строку.

atob следует читать как ASCII to Binary, поскольку он преобразует данные base64 в кодировке ASCII в двоичную форму. На выходе получается декодированная строка. Чтобы преобразовать ее в исходный тип данных, мы используем JSON.parse().

fileContents = atob(blob)
file = JSON.parse(fileContents)
Вход в полноэкранный режим Выход из полноэкранного режима

Второй способ

Если вышеуказанный метод выдает ошибку, Node.js предоставляет метод Buffer.from(), который вы можете использовать. В качестве первого параметра он принимает строку, которую нужно декодировать, а в качестве второго — технику кодирования.

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

  console.log(file)
} catch(error) {
  console.log(error)
}
Вход в полноэкранный режим Выход из полноэкранного режима

Третий способ

Последний способ:
Есть большая вероятность, что предыдущий способ не сработает, если вы работаете на фронтенде. В этом случае создайте функцию, использующую 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);
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Вы можете вызвать эту функцию, передав в качестве параметра fileBlob, и получить декодированную строку!

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

console.log(file)
Вход в полноэкранный режим Выход из полноэкранного режима

Ничего не получилось?

Что ж, есть два последних варианта, которые вы можете попробовать. Либо вы можете использовать API FileReader, либо использовать decodeURIComponent по-другому.

API FileReader: Вы можете найти документацию MDN здесь.
decodeURIComponent: Обратитесь к этому блогу Раджива Сингха, который работает с 16-битной кодировкой строк.
В качестве альтернативы вы можете попробовать пакет NPM под названием js-base64. Я сам его не использовал, поэтому не могу судить о нем.

Ограничения

Как упоминалось ранее, SHA — это уникальный ID, который меняется каждый раз, когда вы изменяете файл. Кроме того, поскольку в функции getFileSHA() мы имеем жестко закодированный индекс объекта, API может ответить SHA другого файла, если вы добавляете или удаляете файлы в каталоге.

Чтобы решить эту проблему, вы можете использовать точное имя файла вместо индекса, чтобы сохранить функцию неизменной после внесения новых изменений.

Резюме и TL;DR

  1. Git хранит большие файлы в формате blob, поэтому мы используем GitHub Database API для получения файлов размером более 1 МБ.
  2. Для получения блоба нам нужно указать имя пользователя, имя репозитория и SHA файла.
  3. Чтобы получить SHA, мы указываем конечной точке путь к папке и сохраняем его в переменной.
  4. Позже мы передадим SHA в качестве параметра другой конечной точке и получим блоб в кодировке base64.
  5. Перед использованием блоб должен быть декодирован в обычную строку, а затем преобразован в исходный формат с помощью JSON.parse().

Дополнительные ссылки

  1. Содержимое репозиториев GitHub
  2. База данных Git
  3. Начало работы с API базы данных Git

Дайте мне знать, какой метод декодирования сработал для вас. Я наиболее активен в Twitter, LinkedIn и Showwcase!

Счастливого поиска!

Оцените статью
Procodings.ru
Добавить комментарий