Статьи

Доступ к Dropbox с помощью PHP

В этой статье мы рассмотрим PHP API Dropbox, создав простой клиент для доступа к файлам в учетной записи Dropbox. Клиент выполнит некоторые основные операции, такие как аутентификация, просмотр файлов, а также загрузка и выгрузка файлов.

Чтобы статья была короткой и удобочитаемой, я буду сводить включенный код к минимуму и вместо этого отсылать вас к полному коду, доступному на GitHub. Для запуска кода вам понадобится PHP с поддержкой cURL и, очевидно, аккаунт Dropbox .

Отправной точкой для всего, что связано с разработкой с Dropbox, должен стать Центр разработчиков Dropbox, где вы можете найти справочник по API вместе с его основными понятиями и лучшими практиками. Вы также можете скачать официальные SDK, но PHP не указан среди поддерживаемых языков. На Github есть ссылка на сторонний PHP SDK .

Наш клиент будет структурирован больше как официальный клиент Python, но я взял некоторые идеи и код из PHP SDK выше, особенно для части OAuth. У нас будет объект DropboxClient объект DropboxClient . Первый позаботится о сложной части: получении учетных данных и управлении ими из Dropbox. Затем клиентский объект будет использовать объект сеанса для выполнения вызовов API и получения данных. Под объектом сеанса находится объект DropboxRESTClient для выполнения HTTP-вызовов с использованием cURL.

Расскажите Dropbox о вашем приложении

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

Войдите в Центр разработчиков и перейдите по ссылке «MyApps», затем выберите «Создать приложение». Dropbox запросит у вас имя, описание и тип доступа для вашего приложения.

Параметр типа доступа указывает, где ваше приложение сможет читать и записывать файлы. Рекомендуемое значение — «Папка приложения», каталог песочницы, который будет создан внутри дома пользователя. Выбрав «Полный Dropbox», приложение увидит весь Dropbox пользователя.

Как только ваше приложение будет создано, появится страница параметров, где вы сможете отредактировать его данные и найти учетные данные для доступа.

Вновь созданные приложения находятся в «статусе разработки». Это позволяет нам немедленно начать разработку и позволяет до пяти другим пользователям протестировать ее. Когда приложение будет готово к выпуску, мы можем подать заявку на получение статуса продукта, и команда Dropbox проверит его, чтобы убедиться в том, что оно соответствует их условиям и рекомендациям по брендингу.

Кодирование приложения

Я поместил свой код в подкаталог моей локальной установки Apache, чтобы он был доступен по адресу http://localhost/mydropbox . Структура каталогов:

Файл bootstrap.php выполняет запуск приложения и будет включен в каждый из интерфейсных файлов, поэтому давайте начнем с обсуждения этого.

Я инициализирую глобальную переменную $config как пустой массив и затем указываю некоторые значения конфигурации. Первые три связаны с Dropbox: ключ доступа, секретный ключ и тип доступа со страницы сведений о вашем приложении. Затем я определяю некоторые другие полезные настройки: базовый корневой путь приложения, путь для сохранения некоторых данных и путь к файлу PHP, который будет содержать токен доступа для приложения.

Этот файл файла маркера доступа не существует в начале; он создается на странице authorize.php и заполняется учетными данными, предоставленными Dropbox. Это будет стандартный файл PHP, и, если он существует, он будет позже включен в этот скрипт. Содержимое файла токена будет похоже на:

 <?php $access_token = array ( "oauth_token_secret" => "abcdefghilmnopqr", "oauth_token" => "stuvwxyzabcdefgh", "uid" => "1234567" ); 

oauth_token и oauth_token_secret — это учетные данные для доступа, а uid — это уникальный идентификатор пользователя.

В следующем разделе файла начальной загрузки я устанавливаю поведение ошибок PHP и выполняю некоторые проверки требований; для запуска приложения каталог данных должен существовать и быть доступным для записи, а файл auth.php должен быть доступен для записи, если он существует. Это гарантирует, что приложение может выполнять свою работу свободно.

Наконец, я включаю наши библиотеки, инициализирую сеанс PHP, устанавливаю пустой $access_token (который будет заполнен позже) и, если он существует, auth.php файл auth.php .

Каждый сценарий внешнего интерфейса будет запускаться внутри основного блока try / catch . Прежде чем копаться в коде библиотеки, нам сначала нужно понять поток, поэтому я начну с цикла авторизации.

авторизация

При первом запуске нашего приложения будет выполнено следующее условие в файле index.php :

 <?php if (!isset($access_token)) { header("Location: authorize.php"); exit; } 

Маркер доступа пуст, поэтому я перенаправляю пользователя на страницу authorize.php которая будет управлять процессом авторизации.

После фазы начальной загрузки я делаю еще одну проверку на наличие токена. Это гарантирует, что этот скрипт будет запущен, только если у нас нет токена. Чтобы избежать бесконечного цикла перенаправления, файл auth.php удаляется основным блоком catch скрипта, если возвращен код ошибки 401 (неверный токен).

Первое, что я делаю в каждом скрипте, это создаю новый объект DropboxSession с нашими ключами API. Когда мы вызываем скрипт напрямую, первое условие будет ложным, а блок else выполняется. Объект сеанса подключается к Dropbox и запрашивает временный токен. Затем токен анализируется в массив и сохраняется в переменной $_SESSION для следующего этапа.

Мы создаем URL авторизации, используя этот токен. Пользователь должен быть перенаправлен или приглашен посетить URL-адрес, по которому он решает, разрешить или запретить доступ к своим данным.

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

Массив $token для этого запроса oauth_token с помощью этого нового oauth_token и предыдущего oauth_token_secret , затем он передается в метод obtainAccessToken() . В случае успеха у нас есть постоянный (до отзыва) токен доступа. Это должно храниться где-то; очевидный выбор — база данных, но для этого примера мы экспортируем ее как действительный код PHP, используя встроенную var_export() и запишем ее в наш файл auth.php . Затем пользователь перенаправляется на страницу индекса, которая является самым простым сценарием.

В начале нашего блока try / catch создается новый объект $access_token , на этот раз с постоянным $access_token качестве четвертого аргумента. Этот объект используется для создания объекта DropboxClient . Эти два шага являются общими для всех других сценариев.

Открытые методы клиента отображаются на соответствующие вызовы Dropbox API. Я accountInfo() метод accountInfo() который возвращает массив, содержащий сведения о пользователе: уникальный идентификатор, имя, адрес электронной почты, информацию о квоте и реферальную ссылку (более подробную информацию см. В официальной документации).

За кулисами: объекты REST и Session

Теперь, когда у нас есть обзор поверхностного потока, давайте посмотрим, что происходит под капотом, наша библиотека Dropbox содержится в каталоге lib/dropbox и состоит из трех классов.

ОТДЫХ

Класс самого низкого уровня нашей библиотеки — это клиент REST (см. lib/dropbox/rest.php ). Этот класс является простой оболочкой для cURL. Он выполняет HTTP-вызовы и возвращает выходные данные в необработанном или закодированном формате или создает исключение в случае ошибки.

Конструктор проверяет, установлен ли в системе cURL, или выдает исключение. Затем он пытается инициализировать внутренний обработчик $curl с параметром $curlDefaults . Обработчик не установлен внутри деструктора.

Методы error() , errno() и close() говорят сами за себя. Затем у нас есть ряд служебных методов get() , post() и put() , все простые обертки для метода main request() который выполняет настоящую работу.

Сначала мы устанавливаем URL для выборки, затем метод HTTP и необходимые параметры, дополнительные заголовки и поля POST (если есть). Параметры для методов GET и PUT передаются по URL-адресу методом вызывающей стороны.

Прежде чем сделать вызов, мы должны сказать cURL получить весь контент, включая заголовки HTTP (установить опцию CURLOPT_HEADER ), потому что некоторые методы API (ex file_get() ) помещают свою информацию в заголовки.

Запрос cURL выполняется с помощью curl_exec() сохраняющим результат в $response а переменная $info заполняется curl_info() подробностями о последнем выполнении. Если метод PUT, нам также придется закрыть дескриптор входного файла.

С помощью содержимого $response и значений $info мы анализируем результат и отделяем заголовки HTTP от тела. По умолчанию тело возвращается как JSON-декодированный, если аргумент $raw не установлен в true.

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

Объект сеанса

Объект DropboxSession расширяет базовый клиент REST для удовлетворения наших потребностей:

  • выполнить начальный процесс аутентификации / авторизации,
  • включать полученные данные аутентификации в каждый последующий запрос REST.

Конструктор просто инициализирует внутренние переменные. Другой простой метод — buildAuthorizeURL() который создает URL авторизации из временного токена. Наиболее важные методы класса:

  • obtainRequestToken() — запросить временный токен доступа OAuth.
  • obtainAccessToken() — запросить постоянный токен доступа OAuth для приложения.
  • fetch() — выполнить вызов REST, включая все необходимые параметры аутентификации и подписи.

Эти три метода имеют похожий поток. Сначала они создают базовый целевой URL и заполняют ассоциативный массив $params требуемыми ключами / значениями oauth_* для отправки. Каждый вызов API должен содержать временную метку и уникальный случайный хэш, параметр $nonce .

Затем подпись генерируется с использованием имени метода HTTP, URL-адреса и параметров. Затем он помещается в очередь в массив $params с oauth_signature ключа oauth_signature . URL выбирается с помощью данного метода HTTP, и возвращается часть тела ответа. Для методов GET и PUT строка запроса генерируется и добавляется к URL-адресу с помощью встроенной функции http_build_query() .

obtainRequestToken() и obtainAccessToken() практически идентичны: первый не использует токен и вызывается методом GET HTTP. Второй вызывается методом POST HTTP и должен включать токен, полученный при предыдущем вызове. Этот токен затем используется как часть ключа подписи для всех следующих вызовов API.

Метод fetch() выполняет несколько дополнительных задач. Сначала он принимает массив с именем $args с любыми дополнительными аргументами, требуемыми конкретным API, например путь к списку ресурсов или файл для загрузки / выгрузки. Эти параметры объединяются с массивом $params до создания подписи. Единственным исключением является аргумент входного файла, используемый методом PUT для загрузки файла, который извлекается и сохраняется для дальнейшего использования. Оператор switch используется, чтобы указать правильный HTTP-метод для вызова.

Класс DropboxSession также имеет два служебных метода, encodeParams() и getSignature() , которые вызываются основными методами, описанными выше. encodeParams() подготавливает параметры запроса для подписи, а getSignature() генерирует подпись запроса OAuth для данного вызова API.

Последний объект DropboxClient

Объект DropboxClient — это наш высокоуровневый интерфейс с Dropbox. Он предоставляет общедоступные методы API, использует объект DropboxSession среднего уровня для выполнения вызовов API и возвращает обработанный вывод вызывающему сценарию. Для этой статьи я реализовал ограниченный набор методов:

  • accountInfo() — получить данные текущего пользователя Dropbox.
  • metadata() — извлекает информацию об объекте Dropbox (файле или папке) и извлекает список содержимого для объектов папки.
  • getFile() — скачать файл и его метаданные и при необходимости сохранить его на диск.
  • putFile() — загрузить локальный файл в удаленный путь Dropbox.

Объект сеанса и базовые URL-адреса API хранятся как внутренние переменные и инициализируются конструктором.

Все методы следуют более или менее одинаковому подходу, поэтому я укажу на различия. Все методы, имеющие дело с путем, должны добавлять к каждому вызову корневой путь Dropbox. Корневой путь зависит от типа доступа к приложению и может быть «Dropbox», если приложение имеет полный доступ, или «Sandbox», если приложение имеет ограниченный доступ. Если это значение не соответствует удаленным настройкам приложения, возвращается ошибка.

Общие шаги, выполняемые каждым методом:

  1. Проверьте и подготовьте список аргументов.
  2. Выполните HTTP-вызов.
  3. Разбираем и возвращаем ответ.

Метод accountInfo() является самым простым; он вызывает свой URL без аргументов и возвращает ассоциативный массив с ответом.

Метод metadata() используется файлом list.php для извлечения и отображения содержимого каталога. Единственный обязательный параметр — это путь к файлу или каталогу для проверки, но он позволяет нам указать все остальные параметры соответствующего вызова API. Если аргумент $path является файлом, возвращаемый массив содержит его метаданные. Если это папка, ключ content содержит список его файлов, если аргумент $list равен false. Мы можем ограничить размер содержимого с $fileLimit аргумента $fileLimit (максимум 25 000) и можем запросить конкретную версию файла или папки (подробности см. В справочнике по API).

Важно отметить, что Dropbox API возвращает хеш-значение для каждого вызова. Если мы хотим отобразить содержимое папки и предоставить нашему методу параметр $hash , API проверяет, изменились ли выходные данные с момента последнего вызова. Если нет, возвращается код состояния 301 (не изменен). Команда Dropbox рекомендует кэшировать результаты и полагаться на эти значения в списках папок для оптимизации производительности.

Метод getFile() используется для извлечения файла, хранящегося в Dropbox пользователя. Все содержимое файла возвращается вызовом в случае успеха, и его метаданные хранятся в пользовательском заголовке HTTP x-dropbox-metadata в виде строки JSON. Возвращаемое значение этого метода — ассоциативный массив, который содержит имя, тип mime, метаданные и контент. Кроме того, я добавил параметр $outFile чтобы сохранить файл непосредственно на диске.

Файл download.php показывает демонстрацию этого метода в действии. В этом примере загруженный файл сохраняется непосредственно в каталог данных приложения, а часть содержимого ответа очищается.

Метод putFile() загружает файл из нашего локального хранилища в Dropbox пользователя, используя HTTP-метод PUT, который предпочитает команда Dropbox вместо POST. Этот метод проверяет, существует ли локальный файл и не превышает лимит API 150 МБ, перед любым другим обычным действием.

Поддерживаемые параметры для этого метода, помимо пути к исходному файлу, — это папка назначения, необязательное альтернативное имя и опция перезаписи. Если этот последний параметр имеет значение false и удаленный файл существует, загруженный файл переименовывается с прогрессивным номером (например, test.txt становится test (1).txt ). API также позволяет использовать необязательный параметр parent_rev для управления ревизиями, но для простоты я решил его опустить.

Резюме

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

Изображение через Fotolia