Статьи

Создайте свой собственный клиент Dropbox с API Dropbox

Существует множество решений для файлового хостинга, но мало что можно сравнить с Dropbox из-за его простоты, функции автоматической синхронизации, кроссплатформенной поддержки и других интересных функций.

Логотип Dropbox

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

Создание приложения Dropbox

Первое, что вам нужно сделать, это зайти на сайт разработчика Dropbox и создать новое приложение .

создать приложение Dropbox

Dropbox предлагает два API: API Dropbox, который является API для широкой публики, и Business API для команд. Эти два API-интерфейса практически одинаковы, с той лишь разницей, что бизнес-API специально используется для бизнес-учетных записей, поэтому такие функции команды, как доступ к информации о команде, доступ к файлам членов команды и управление членами команды, встроены в нее по умолчанию. Мы будем использовать первое.

Создав приложение, вы увидите страницу настроек приложения:

настройки приложения

Отсюда вы можете установить следующее:

  • Пользователи разработки — это позволяет добавлять пользователей Dropbox для тестирования вашего приложения. По умолчанию статус приложения — разработка . Это означает, что только вы можете проверить его функциональность. Если вы позволите любому другому пользователю получить доступ к вашему приложению, они не смогут его использовать.
  • Тип разрешения — это параметр, который вы выбрали ранее при создании приложения. Существует только два типа разрешений: папка и полный дропбокс. Папка означает, что ваше приложение имеет доступ только к указанной вами папке. Полный Dropbox означает, что ваше приложение имеет доступ ко всем файлам пользователя.
  • Ключ и секрет приложения — это уникальный ключ, используемый Dropbox для идентификации вашего приложения. Нам это понадобится позже.
  • URL-адреса перенаправления OAuth2 — здесь вы можете задать URL-адреса, на которые ваше приложение может перенаправлять сразу после того, как пользователь подтвердит необходимые разрешения. Оставьте это поле пустым, вы добавите к нему значение позже. А пока обратите внимание, что для перенаправления могут использоваться только те URL-адреса, которые вы указали здесь.
  • Разрешить неявное предоставление — следует ли автоматически генерировать токен доступа после того, как пользователь предоставил необходимые разрешения для вашего приложения. Если вы используете Dropbox на стороне клиента, это должно быть разрешено, чтобы вы могли получить токен доступа через JavaScript. Для целей этого проекта вы должны запретить его .
  • Сгенерированный токен доступа — вы можете использовать его для создания токена доступа для вашей учетной записи. Токен доступа можно использовать при отправке запросов к API.
  • Домены Chooser / Saver — если вы используете вставки, такие как Chooser и Saver , вам нужно указать домены, в которые вы их встраиваете.
  • Webhooks — вы можете использовать webhooks, если вы хотите, чтобы ваш сервер выполнял определенные действия всякий раз, когда файл в учетной записи пользователя Dropbox изменяется. Вы не будете проходить через веб-хуки в этом учебном пособии, поэтому я рекомендую вам изучить документацию о веб-хуках, если вам нужна функциональность в вашем приложении.

Сборка приложения

Теперь вы готовы к созданию приложения. Вы будете использовать Laravel .

Установка зависимостей

composer create-project --prefer-dist laravel/laravel pinch 

После установки вам также необходимо установить Guzzle , Purl и Carbon .

 composer require nesbot/carbon jwage/purl guzzlehttp/guzzle 

Вы будете использовать Guzzle для отправки HTTP-запросов к API Dropbox, Purl для создания URL-адреса входа в Dropbox и Carbon для выражения дат файлов в часовом поясе пользователя.

конфигурация

После установки Laravel откройте файл .env в корне вашего проекта и добавьте конфигурацию dropbox:

 DROPBOX_APP_KEY="YOUR DROPBOX APP KEY" DROPBOX_APP_SECRET="YOUR DROPBOX APP SECRET" DROPBOX_REDIRECT_URI="YOUR DROPBOX LOGIN REDIRECT URL" 

Используйте ключ приложения и секрет приложения, которые вы получили ранее с веб-сайта разработчика Dropbox, в качестве значения для DROPBOX_APP_KEY и DROPBOX_APP_SECRET . Для DROPBOX_REDIRECT_URI вы должны указать http-URL, поэтому, если вы используете виртуальный хост, вам нужно будет использовать что-то вроде Ngrok для обслуживания приложения. Затем в конфигурации вашего виртуального хоста добавьте URL, предоставленный Ngrok, в качестве ServerAlias .

 <VirtualHost *:80> ServerName pinch.dev ServerAlias xxxxxxx.ngrok.io ServerAdmin wern@localhost DocumentRoot /home/wern/www/pinch/public </VirtualHost> 

Маршруты

Различные страницы в приложении определены в файле app/Http/routes.php :

 Route::get('/', 'HomeController@index'); Route::post('/', 'HomeController@postIndex'); Route::get('/login', 'HomeController@login'); Route::group( ['middleware' => ['admin']], function($app){ Route::get('/dashboard', 'AdminController@dashboard'); Route::get('/user', 'AdminController@user'); Route::get('/search', 'AdminController@search'); Route::get('/upload', 'AdminController@upload'); Route::post('/upload', 'AdminController@doUpload'); Route::get('/revisions', 'AdminController@revisions'); Route::get('/restore', 'AdminController@restoreRevision'); Route::get('/download', 'AdminController@download'); }); 

Разбирая код выше, сначала у вас есть маршруты, которые имеют дело с входом в Dropbox:

 //displays the view for logging in to dropbox Route::get('/', 'HomeController@index'); //generates the dropbox login URL Route::post('/', 'HomeController@postIndex'); //generates the access token based on the token provided by Dropbox Route::get('/login', 'HomeController@login'); 

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

 Route::group( ['middleware' => ['admin']], function($app){ ... }); 

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

 Route::get('/dashboard', 'AdminController@dashboard'); 

Страница сведений о пользователе:

 Route::get('/user', 'AdminController@user'); 

Страница для поиска файлов:

 Route::get('/search', 'AdminController@search'); 

Страница для загрузки файлов:

 Route::get('/upload', 'AdminController@upload'); Route::post('/upload', 'AdminController@doUpload'); 

Страница для просмотра различных версий определенного файла и страница для восстановления определенной версии:

 Route::get('/revisions', 'AdminController@revisions'); Route::get('/restore', 'AdminController@restoreRevision'); 

И, наконец, маршрут для загрузки файлов:

 Route::get('/download', 'AdminController@download'); 

Промежуточное программное обеспечение администратора

Далее идет промежуточное ПО администратора ( app/Http/Middleware/AdminMiddleware.php ):

 <?php namespace App\Http\Middleware; use Closure; class AdminMiddleware { public function handle($request, Closure $next) { if ($request->session()->has('access_token')) { return $next($request); } return redirect('/') ->with('message', ['type' => 'danger', 'text' => 'You need to login']); } } 

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

Dropbox Class

Класс Dropbox ( app/Dropbox.php ) используется для инициализации клиента Guzzle, используемого для отправки запросов в Dropbox. У него есть два метода: api и content . api используется для отправки запросов к API, а content — для работы с контентом, например, когда вы загружаете или скачиваете файлы.

 <?php namespace App; use GuzzleHttp\Client; class Dropbox { public function api() { $client = new Client([ 'base_uri' => 'https://api.dropboxapi.com', ]); return $client; } public function content() { $client = new Client([ 'base_uri' => 'https://content.dropboxapi.com' ]); return $client; } } 

HomeController

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

Откройте app/Http/Controllers/HomeController.php файл:

 <?php namespace App\Http\Controllers; use Purl\Url; use App\Dropbox; use Illuminate\Http\Request; class HomeController extends Controller { private $api_client; public function __construct(Dropbox $dropbox) { $this->api_client = $dropbox->api(); } public function index() { return view('index'); } public function postIndex() { $url = new Url('https://www.dropbox.com/1/oauth2/authorize'); $url->query->setData([ 'response_type' => 'code', 'client_id' => env('DROPBOX_APP_KEY'), 'redirect_uri' => env('DROPBOX_REDIRECT_URI') ]); return redirect($url->getUrl()); } public function login(Request $request) { if ($request->has('code')) { $data = [ 'code' => $request->input('code'), 'grant_type' => 'authorization_code', 'client_id' => env('DROPBOX_APP_KEY'), 'client_secret' => env('DROPBOX_APP_SECRET'), 'redirect_uri' => env('DROPBOX_REDIRECT_URI') ]; $response = $this->api_client->request( 'POST', '/1/oauth2/token', ['form_params' => $data] ); $response_body = json_decode($response->getBody(), true); $access_token = $response_body['access_token']; session(['access_token' => $access_token]); return redirect('dashboard'); } return redirect('/'); } } 

Разбивая код выше, определите приватную переменную с именем $api_client . При этом сохраняется ссылка на клиент Guzzle, возвращаемый путем вызова метода api в классе Dropbox .

 private $api_client; public function __construct(Dropbox $dropbox) { $this->api_client = $dropbox->api(); } 

Метод index возвращает представление индекса:

 public function index() { return view('index'); } 

Представление индекса ( resources/views/index.blade.php ) содержит форму, позволяющую пользователю войти в Dropbox. Он также имеет скрытое поле, которое используется для токена CSRF (используется для предотвращения атак подделки межсайтовых запросов).

 @extends('layouts.default') @section('content') <form method="POST"> <input type="hidden" name="_token" value="{{{ csrf_token() }}}" /> <button class="button">Login with Dropbox</button> </form> @stop 

Представление индекса наследуется от шаблона по умолчанию ( resources/views/layouts/default.blade.php ). Внутри #wrapper div находится форма входа в систему.

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ env('APP_TITLE') }}</title> <link rel="stylesheet" href="{{ url('assets/lib/picnic/picnic.min.css') }}"> <link rel="stylesheet" href="{{ url('assets/css/style.css') }}"> </head> <body> <div id="wrapper"> <h1>{{ env('APP_TITLE') }}</h1> @yield('content') </div> </body> </html> 

Этот шаблон использует CSS для пикника, чтобы все выглядело лучше. Основная public/assets/css/style.css стилей ( public/assets/css/style.css ) содержит некоторые базовые стили для основной оболочки и окон предупреждений:

 #wrapper { width: 900px; margin: 0 auto; } .alert { padding: 20px; } .alert-danger { background-color: #F55; } .alert-success { background-color: #60B152; } 

Возвращаясь к HomeController : у вас есть метод postIndex который отвечает за генерацию URL входа в Dropbox. Здесь вы используете библиотеку Purl для создания URL входа.

 public function postIndex() { $url = new Url('https://www.dropbox.com/1/oauth2/authorize'); $url->query->setData([ 'response_type' => 'code', 'client_id' => env('DROPBOX_APP_KEY'), 'redirect_uri' => env('DROPBOX_REDIRECT_URI') ]); return redirect($url->getUrl()); } 

Ниже приведены обязательные параметры запроса для URL входа в систему:

  • response_type — что Dropbox добавит к указанному вами URL перенаправления. Это может иметь значение либо token либо code . Использование token означает, что токен доступа будет передан в виде хэша в URL перенаправления. Это полезно для клиентских приложений, но в этом случае бесполезно, поскольку вы в основном работаете на стороне сервера, поэтому используйте вместо этого code . Это передает уникальный код URL перенаправления в качестве параметра запроса. Затем этот код можно использовать для обмена на токен доступа. Обратите внимание, что вы не можете иметь response_type token если вы запретили неявное предоставление в настройках вашего приложения.
  • client_id — ключ вашего приложения Dropbox.
  • redirect_uri — это может быть один из URL перенаправления, который вы указали в настройках своего приложения.

Как только это будет сделано, перенаправьте на последний URL, созданный Purl.

Далее идет метод login в login . Это ответственно за обработку запроса в указанном вами URL перенаправления. Это где код авторизации передается. Вы можете получить код с помощью класса Request чтобы добавить его в метод login в login .

 public function login(Request $request) { ... } 

Внутри метода проверьте, передан ли code в URL. Если это не так, перенаправьте обратно на домашнюю страницу.

 if ($request->has('code')) { ... } return redirect('/'); 

Затем добавьте все параметры, необходимые конечной точке API для получения токена доступа. Это включает code который является кодом авторизации, который передается в URL. Тип grant_type — это всегда authorization_code : это код, который передается в URL, который вы можете использовать для обмена на токен доступа. client_id и client_secret являются ключом и секретом приложения Dropbox. redirect_uri — это URL перенаправления.

 $data = [ 'code' => $request->input('code'), 'grant_type' => 'authorization_code', 'client_id' => env('DROPBOX_APP_KEY'), 'client_secret' => env('DROPBOX_APP_SECRET'), 'redirect_uri' => env('DROPBOX_REDIRECT_URI') ]; 

Сделайте POST запрос к конечной точке /1/oauth2/token и передайте $data качестве параметров формы:

 $response = $this->api_client->request( 'POST', '/1/oauth2/token', [ 'form_params' => $data ... ] )); 

Извлеките токен доступа из тела ответа, установите его в сеансе и перенаправьте на страницу панели администратора:

 $response_body = json_decode($response->getBody(), true); $access_token = $response_body['access_token']; session(['access_token' => $access_token]); return redirect('dashboard'); 

AdminController

Как только пользователь вошел в систему, все запросы обрабатываются AdminController ( app/Http/Controllers/AdminController.php ), который содержит следующий код:

 <?php namespace App\Http\Controllers; use App\Dropbox; use Illuminate\Http\Request; class AdminController extends Controller { private $api_client; private $content_client; private $access_token; public function __construct(Dropbox $dropbox) { $this->api_client = $dropbox->api(); $this->content_client = $dropbox->content(); $this->access_token = session('access_token'); } public function dashboard() { return view('admin.dashboard'); } public function user() { $response = $this->api_client->request('POST', '/2/users/get_current_account', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token ] ]); $user = json_decode($response->getBody(), true); $page_data = [ 'user' => $user ]; return view('admin.user', $page_data); } public function search(Request $request) { $page_data = [ 'path' => '', 'query' => '', 'matches' => [] ]; if ($request->has('path') && $request->has('query')) { $path = $request->input('path'); $query = $request->input('query'); $data = json_encode( [ 'path' => $path, 'mode' => 'filename', 'query' => $query ] ); $response = $this->api_client->request( 'POST', '/2/files/search', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Content-Type' => 'application/json' ], 'body' => $data ]); $search_results = json_decode($response->getBody(), true); $matches = $search_results['matches']; $page_data = [ 'path' => $path, 'query' => $query, 'matches' => $matches ]; } return view('admin.search', $page_data); } public function revisions(Request $request) { if ($request->has('path')) { $path = $request->input('path'); $data = json_encode([ 'path' => $path ]); $response = $this->api_client->request( 'POST', '/2/files/list_revisions', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Content-Type' => 'application/json' ], 'body' => $data ]); $revisions = json_decode($response->getBody(), true); $page_data = [ 'revisions' => $revisions['entries'], 'path' => $path ]; return view('admin.revisions', $page_data); } else { return redirect('search'); } } public function restoreRevision(Request $request) { if ($request->has('path') && $request->has('rev')) { $path = $request->input('path'); $rev = $request->input('rev'); $data = json_encode([ 'path' => $path, 'rev' => $rev ]); $response = $this->api_client->request( 'POST', '/2/files/restore', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Content-Type' => 'application/json' ], 'body' => $data ]); $response_data = json_decode($response->getBody(), true); if (!empty($response_data)) { return redirect("revisions?path={$path}") ->with('message', [ 'type' => 'success', 'text' => "File has been restored to the following revision: {$response_data['rev']}" ]); } else { return redirect("revisions?path={$path}") ->with('message', [ 'type' => 'danger', 'text' => 'The revision request failed. Please try again' ]); } } else { return redirect('search'); } } public function download(Request $request) { if ($request->has('path')) { $path = $request->input('path'); $data = json_encode([ 'path' => $path ]); $response = $this->content_client->request( 'POST', '/2/files/download', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Dropbox-API-Arg' => $data ] ]); $result = $response->getHeader('dropbox-api-result'); $file_info = json_decode($result[0], true); $content = $response->getBody(); $filename = $file_info['name']; $file_extension = substr($filename, strrpos($filename, '.')); $file = uniqid() . $file_extension; $file_size = $file_info['size']; return response($content) ->header('Content-Description', 'File Transfer') ->header('Content-Disposition', "attachment; filename={$file}") ->header('Content-Transfer-Encoding', 'binary') ->header('Connection', 'Keep-Alive') ->header('Content-Length', $file_size); } else { return redirect('search'); } } public function upload() { return view('admin.upload'); } public function doUpload(Request $request) { if ($request->hasFile('file') && $request->has('path')) { $valid_mimetypes = [ 'image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp' ]; $valid_size = 5000000; //5Mb $mime_type = $request->file('file')->getMimeType(); $size = $request->file('file')->getSize(); $filename = $request->file('file')->getClientOriginalName(); $path = $request->input('path') . '/' . $filename; if (in_array($mime_type, $valid_mimetypes) && $size <= $valid_size) { $data = json_encode([ 'path' => $path, 'mode' => 'add', 'autorename' => true, 'mute' => false ]); $response = $this->content_client->request( 'POST', '/2/files/upload', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Content-Type' => 'application/octet-stream', 'Dropbox-API-Arg' => $data ], 'body' => fopen($request->file('file'), 'r') ]); $response_data = json_decode($response->getBody(), true); if (!empty($response_data['name'])) { $name = $response_data['name']; return redirect('upload') ->with('message', [ 'type' => 'success', 'text' => "File with the name {$name} was uploaded!" ]); } } } return redirect('upload') ->with('message', [ 'type' => 'danger', 'text' => 'There was a problem uploading the file' ]); } } 

Разбивая код выше, внутри класса вы объявляете три частные переменные и инициализируете их в конструкторе. Это клиент API, клиент контента и токен доступа.

 private $api_client; private $content_client; private $access_token; public function __construct(Dropbox $dropbox) { $this->api_client = $dropbox->api(); $this->content_client = $dropbox->content(); $this->access_token = session('access_token'); } 

Далее приведен метод обслуживания представления панели инструментов:

 public function dashboard() { return view('admin.dashboard'); } 

Представление панели мониторинга ( resources/views/admin/dashboard.blade.php ) содержит следующий код. Он ссылается на все страницы, что позволяет пользователям выполнять различные операции в API:

 @extends('layouts.admin') @section('content') <h3>What do you like to do?</h3> <ul> <li><a href="/user">View User Info</a></li> <li><a href="/search">Search Files</a></li> <li><a href="/upload">Upload Files</a></li> </ul> @stop 

Все представления, обслуживаемые AdminController наследуются от шаблона администратора ( resources/views/layouts/admin.blade.php ). Как и шаблон по умолчанию из предыдущих, здесь используются CSS для пикника и некоторые базовые стили. Разница лишь в том, что вы включаете частичное alert на странице.

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ env('APP_TITLE') }}</title> <link rel="stylesheet" href="{{ url('assets/lib/picnic/picnic.min.css') }}"> <link rel="stylesheet" href="{{ url('assets/css/style.css') }}"> </head> <body> <div id="wrapper"> <h1>{{ env('APP_TITLE') }}</h1> @include('partials.alert') @yield('content') </div> </body> </html> 

Частичное alert ( resources/views/partials/alert.blade.php ) используется для вывода любых данных, которые были записаны в сеансе (временные данные, которые немедленно удаляются после завершения запроса). Часть предупреждения содержит следующий код:

 @if(session('message')) <div class="alert alert-{{ session('message.type') }}"> {{ session('message.text') }} </div> @endif 

Получение пользовательских данных

Возвращаясь к AdminController , у вас есть метод user . Это делает запрос POST к конечной точке API, которая возвращает данные текущего пользователя. Эти данные затем передаются в представление user ( resources/views/admin/user.blade.php ).

 public function user() { $response = $this->api_client->request('POST', '/2/users/get_current_account', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token ] ]); $user = json_decode($response->getBody(), true); $page_data = [ 'user' => $user ]; return view('admin.user', $page_data); } 

Пользовательское представление показывает данные пользователя, такие как идентификатор учетной записи, имя и адрес электронной почты:

 @extends('layouts.admin') @section('content') <h3>User Info</h3> <ul> <li>Account ID: {{ $user['account_id'] }}</li> <li>Name: {{ $user['name']['display_name'] }}</li> <li>Email: {{ $user['email'] }}</li> <li>Referral Link: <a href="{{ $user['referral_link'] }}">{{ $user['referral_link'] }}</a></li> <li>Account Type: {{ $user['account_type']['.tag'] }}</li> </ul> @stop 

Поиск файлов

Далее приведен метод визуализации представления для поиска файлов в учетной записи пользователя Dropbox:

 public function search(Request $request) { ... } 

Внутри метода инициализируйте данные страницы по умолчанию:

 $page_data = [ 'path' => '', 'query' => '', 'matches' => [] ]; 

Проверьте, были ли path и query переданы в качестве параметров запроса. path — это путь, по которому будет выполняться поиск. Это должен быть существующий путь в Dropbox пользователя (например, /Files , /Documents , /Public ). Косая черта необходима, чтобы указать, что вы начинаете с корневого каталога. query — это имя файла, который пользователь хочет найти. Это не должно быть точным именем файла.

 if ($request->has('path') && $request->has('query')) { ... } 

Если path и query присутствуют, присвойте их собственным переменным и добавьте их в данные в кодировке JSON, требуемые поисковым запросом. mode который вы передаете, — это тип поиска, который вы хотите выполнить. Это может быть filename , filename_and_content или deleted_filename .

 $path = $request->input('path'); $query = $request->input('query'); $data = json_encode( [ 'path' => $path, 'mode' => 'filename', 'query' => $query ] ); 

Отправьте запрос:

 $response = $this->api_client->request( 'POST', '/2/files/search', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Content-Type' => 'application/json' ], 'body' => $data ]); 

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

 $search_results = json_decode($response->getBody(), true); $matches = $search_results['matches']; $page_data = [ 'path' => $path, 'query' => $query, 'matches' => $matches ]; 

Отобразить вид поиска:

 return view('admin.search', $page_data); 

Представление поиска ( resources/views/admin/search.blade.php ) содержит следующее:

 @extends('layouts.admin') @section('content') <h3>Search</h3> <form method="GET"> <p> <label for="path">Path</label> <input type="text" name="path" id="path" value="{{ $path }}"> </p> <p> <label for="query">Query</label> <input type="search" name="query" id="query" value="{{ $query }}"> </p> <button>Search</button> </form> @if(count($matches) > 0) <h5>Search Results</h5> <table> <thead> <tr> <th>Filename</th> <th>Revisions</th> <th>Download</th> </tr> </thead> <tbody> @foreach($matches as $match) <tr> <td>{{ $match['metadata']['name'] }}</td> <td><a href="/revisions?path={{ urlencode($match['metadata']['path_lower']) }}">view</a></td> <td><a href="/download?path={{ urlencode($match['metadata']['path_lower']) }}">download</a></td> </tr> @endforeach </tbody> </table> @endif @stop 

Из приведенного выше кода вы можете видеть, что у вас есть форма, которая представляет данные с помощью метода GET . Если число $matches превышает 0, отобразите таблицу результатов поиска. В таблице отображается имя файла, ссылка для отображения изменений в файле и ссылка для загрузки файла. В этих двух ссылках вы передаете строчную версию пути к файлу, поскольку это обязательный параметр для ревизий и запросов на загрузку.

Редакции файлов

Метод revisions отвечает за перечисление различных ревизий файла. Это проверяет, присутствует ли path в запросе и просто перенаправляет обратно на страницу поиска, если это не так.

 public function revisions(Request $request) { if ($request->has('path')) { ... } else { return redirect('search'); } } 

В противном случае он использует path в качестве параметра для запроса:

 $path = $request->input('path'); $data = json_encode([ 'path' => $path ]); 

Затем выполните запрос, извлеките данные из тела ответа, преобразуйте их в массив и передайте в качестве данных для представления revisions .

 $response = $this->api_client->request( 'POST', '/2/files/list_revisions', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Content-Type' => 'application/json' ], 'body' => $data ]); $revisions = json_decode($response->getBody(), true); $page_data = [ 'revisions' => $revisions['entries'], 'path' => $path ]; return view('admin.revisions', $page_data); 

Представление revisions ( resources/views/admin/revisions.blade.php ) выводит путь к файлу и отображает таблицу, содержащую детали ревизии, такие как идентификатор ревизии (уникальный идентификатор, назначаемый Dropbox каждый раз, когда файл пересматривается), модификация метка времени, размер файла в байтах и ​​ссылка для восстановления определенной ревизии.

 @extends('layouts.admin') @section('content') <h3>Revisions</h3> <strong>File: </strong> {{ $path }} <table> <thead> <tr> <th>Revision ID</th> <th>Modified</th> <th>Size (Bytes)</th> <th>Restore</th> </tr> </thead> <tbody> @foreach($revisions as $rev) <tr> <td>{{ $rev['rev'] }}</td> <td>{{ Carbon\Carbon::parse($rev['server_modified'])->setTimezone(env('APP_TIMEZONE'))->toDayDateTimeString() }}</td> <td>{{ $rev['size'] }}</td> <td><a href="/restore?path={{ urlencode($rev['path_lower']) }}&rev={{ $rev['rev'] }}">restore</a></td> </tr> @endforeach </tbody> </table> @stop 

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

Восстановление ревизий

Метод restoreRevision перечисляет различные ревизии файла. Конечная точка API /2/files/restore требует как пути к файлу, так и идентификатора ревизии, поэтому вы проверяете, были ли они переданы в запросе. Если нет, то просто перенаправьте обратно на страницу поиска.

 public function restoreRevision(Request $request) { if ($request->has('path') && $request->has('rev')) { ... } else { return redirect('search'); } } 

Если путь и идентификатор ревизии были переданы, добавьте их в параметры запроса, а затем сделайте запрос:

 $path = $request->input('path'); $rev = $request->input('rev'); $data = json_encode([ 'path' => $path, 'rev' => $rev ]); $response = $this->api_client->request( 'POST', '/2/files/restore', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Content-Type' => 'application/json' ], 'body' => $data ]); 

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

 $response_data = json_decode($response->getBody(), true); if (!empty($response_data)) { return redirect("revisions?path={$path}") ->with('message', [ 'type' => 'success', 'text' => "File has been restored to the following revision: {$response_data['rev']}" ]); } else { return redirect("revisions?path={$path}") ->with('message', [ 'type' => 'danger', 'text' => 'The revision request failed. Please try again' ]); } 

Загрузка файлов

Следующим является метод, который обрабатывает запросы на загрузку. Как и другие методы, которые вы использовали до сих пор, этот метод также требует пути к файлу.

 public function download(Request $request) { if ($request->has('path')) { ... } else { return redirect('search'); } } 

Если путь присутствует, добавьте его в параметры запроса:

 $path = $request->input('path'); $data = json_encode([ 'path' => $path ]); 

Затем выполните запрос. Обратите внимание, что в этот раз вы ничего не передаете в тело запроса. Вместо этого вы передаете его в заголовке запроса Dropbox-API-Arg .

 $response = $this->content_client->request( 'POST', '/2/files/download', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Dropbox-API-Arg' => $data ] ]); 

Тело ответа, которое вы получаете от этого запроса, является двоичными данными самого файла. Информация о файле передается в заголовке ответа, поэтому используйте метод getHeader предоставленный Guzzle, чтобы извлечь данные заголовка в виде массива. Информация о файле сохраняется в первом элементе в виде строки JSON, поэтому используйте json_decode чтобы преобразовать ее в массив.

 $result = $response->getHeader('dropbox-api-result'); $file_info = json_decode($result[0], true); 

Извлеките содержимое файла, сгенерируйте имя файла, которое будет использоваться для загрузки, и получите размер файла:

 $content = $response->getBody(); $filename = $file_info['name']; $file_extension = substr($filename, strrpos($filename, '.')); $file = uniqid() . $file_extension; $file_size = $file_info['size']; 

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

 return response($content) ->header('Content-Description', 'File Transfer') ->header('Content-Disposition', "attachment; filename={$file}") ->header('Content-Transfer-Encoding', 'binary') ->header('Connection', 'Keep-Alive') ->header('Content-Length', $file_size); 

Загрузка файлов

Теперь перейдите к последней операции, которую вы можете выполнить в этом приложении: загрузка файлов. Сначала представим представление upload :

 public function upload() { return view('admin.upload'); } 

Представление upload ( resources/views/admin/upload.blade.php ) имеет форму, которая запрашивает путь, по которому будет загружен файл, и сам файл.

 @extends('layouts.admin') @section('content') <h3>Upload</h3> <form method="POST" enctype="multipart/form-data"> <input type="hidden" name="_token" value="{{{ csrf_token() }}}" /> <p> <label for="path">Path</label> <input type="text" name="path" id="path"> </p> <p> <label for="file">File</label> <input type="file" name="file" id="file"> </p> <button>upload</button> </form> @stop 

После doUpload формы она обрабатывается методом doUpload . Это проверяет, был ли загружен файл и был ли указан путь. Если он не был установлен, перенаправьте обратно на страницу загрузки с сообщением об ошибке.

 public function doUpload(Request $request) { if ($request->hasFile('file') && $request->has('path')) { ... } return redirect('upload') ->with('message', [ 'type' => 'danger', 'text' => 'There was a problem uploading the file' ]); } 

Если путь и файл присутствуют, укажите допустимые типы MIME и допустимый размер файла:

 $valid_mimetypes = [ 'image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp' ]; $valid_size = 5000000; //5Mb 

Получить информацию о файле:

 $mime_type = $request->file('file')->getMimeType(); $size = $request->file('file')->getSize(); $filename = $request->file('file')->getClientOriginalName(); $path = $request->input('path') . '/' . $filename; 

Проверьте, имеет ли файл допустимый тип MIME и размер. Если это так, добавьте параметры запроса. Это включает в себя path к файлу, который будет сохранен в Dropbox пользователя. mode — это действие, которое нужно выполнить, если файл уже существует. Это может иметь значение либо add , overwrite или update . add означает, что файл будет добавлен, но Dropbox добавит номер к имени файла ( file (2).txt , file (3).txt и т. д.). Следующим является autorename , который всегда должен быть установлен в true если установлен mode add . Таким образом, новый файл будет автоматически переименован, если файл с таким именем уже существует. Последний вариант — mute которое просто сообщает Dropbox, уведомлять пользователя или нет. По умолчанию для этого параметра установлено значение false что означает, что приложение Dropbox уведомит пользователя о загрузке файла. Если для этого параметра установлено значение true , уведомление не отправляется.

 if (in_array($mime_type, $valid_mimetypes) && $size <= $valid_size) { $data = json_encode([ 'path' => $path, 'mode' => 'add', 'autorename' => true, 'mute' => false ]); } 

Сделайте запрос на загрузку файла. Обратите внимание, что здесь используется клиент контента, а не клиент API, поскольку вы имеете дело с файлами, а не с данными. Параметры загрузки передаются в заголовке Dropbox-API-Arg в виде строки JSON, а двоичные данные для самого файла передаются в теле запроса. Также укажите Content-Type как application/octet-stream когда вы передаете двоичные данные в тело запроса.

 $response = $this->content_client->request( 'POST', '/2/files/upload', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->access_token, 'Content-Type' => 'application/octet-stream', 'Dropbox-API-Arg' => $data ], 'body' => fopen($request->file('file'), 'r') ]); 

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

 $response_data = json_decode($response->getBody(), true); if (!empty($response_data['name'])) { $name = $response_data['name']; return redirect('upload') ->with('message', [ 'type' => 'success', 'text' => "File with the name {$name} was uploaded!" ]); } 

Вывод

Из этой статьи вы узнали, как работать с API Dropbox в PHP. В частности, вы узнали, как войти в Dropbox, получить информацию о текущем пользователе, выполнить поиск файлов, составить список версий файла, восстановить версию, загрузить файл и, наконец, загрузить файл.

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

Вопросов? Комментарии? Оставьте их под кнопкой «Мне нравится»!