Содержание


Создание службы хранения фотографий в облаке с помощью PHP и IBM Bluemix. Часть 1

Создание веб-приложения для загрузки, просмотра и удаления фотографий

Comments

Все больше и больше приложений используют дешевые, надежные службы хранения объектов в облаке. Вы тоже можете добавить к своему приложению облачное хранилище, воспользовавшись службой IBM® Bluemix™ Object Storage. В этой серии руководств показано, как построить службу хранения фотографий в облаке с помощью IBM Bluemix и PHP. Для создания приложения используется микросреда PHP Silex, для поддержки нескольких пользователей – библиотека аутентификации HybridAuth, а для получения гибкого, дружественного к мобильным устройствам пользовательского интерфейса – построитель шаблонов Bootstrap.

В первой части мы создадим приложение-заготовку, организуем пользовательский интерфейс, обеспечим возможность добавления фотографий и начнем работать с ними. В Части 2 будет показано, как развернуть приложение в IBM Bluemix и добавить поддержку нескольких контейнеров и нескольких пользователей.

Что вам понадобится

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

Поскольку приложение работает в облаке, в идеале оно должно поддерживать нескольких пользователей. Служба Bluemix Object Storage позволяет легко это сделать благодаря встроенной поддержке выделения независимых хранилищ объектов и создания отдельных учетных подзаписей для каждого из них. Проверка подлинности пользователей выполняется внешней службой (в данном случае, Google+) с помощью OAuth 2.0 и API Google+.

Со стороны клиента я использую Bootstrap для создания пользовательского интерфейса, который работает как на мобильных устройствах, так и в настольных браузерах. На сервере я использую микросреду PHP Silex для управления работой приложения, шаблонизатор Twig для визуализации шаблонов страниц, клиентскую библиотеку PHP jStart для службы Bluemix Object Storage и библиотеку аутентификации с открытым исходным кодом HybridAuth для взаимодействия с API Google+.

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

Запустить приложениеПолучить код

Обзор службы и API Object Storage

Служба IBM Bluemix Object Storage основывается на OpenStack Swift и следует трехуровневой иерархии организации данных Swift: учетные записи, контейнеры и объекты. Вот как это работает.

  • Основная единица в иерархии – учетная запись. В широком смысле учетные записи соответствуют пользователям; для доступа к учетной записи пользователь должен ввести реквизиты доступа.
  • Учетная запись может содержать несколько контейнеров, которые в широком смысле соответствуют папкам или подпапкам традиционной файловой системы.
  • Каждый контейнер может содержать несколько объектов. Объекты могут содержать файлы или данные вместе с соответствующими метаданными, и у каждого объекта есть уникальный URL-адрес. Можно хранить неограниченное количество объектов.

Помимо стандартной иерархии, описанной выше, Bluemix Object Storage предлагает еще одну возможность: создавать внутри каждой родительской учетной записи дочерние учетные записи, или подзаписи. Это позволяет поддерживать несколько пользователей с одной и той же родительской учетной записью без дублирования контейнеров и объектов.

При создании нового экземпляра службы Bluemix Object Storage Bluemix автоматически создает учетную запись и предоставляет реквизиты доступа к ней через специальную переменную среды Bluemix VCAP_SERVICES. Вот как могут выглядеть эти реквизиты доступа:

Пример реквизитов доступа Bluemix
Пример реквизитов доступа Bluemix

Эти реквизиты доступа можно использовать для обращения к API Object Storage и хранения или извлечения объектов. Как правило, начинают с отправки запроса GET на URL-адрес аутентификации службы, добавления к URL-адресу учетной подзаписи пользователя и включения заголовка Authorization:, содержащего имя пользователя и пароль родительской учетной записи в кодировке Base64. Если проверка подлинности прошла успешно, сервер возвращает ответ с кодом 200 OK и двумя заголовками, которые можно использовать для последующих запросов.

Например, если URL-адрес проверки подлинности службы https://swift.ng.bluemix.net/auth/xyz, отправьте запрос GET с соответствующим заголовком Authorization:, как описано выше, на https://swift.ng.bluemix.net/auth/xyz/me, где me – это подзапись, к которой нужно получить доступ или которую нужно создать. Если проверка подлинности пройдет успешно, сервер возвратит ответ, содержащий заголовки X-Auth-Token: и X-Storage-Url:.

Ниже приведен пример запроса и ответа:

Пример запроса и ответа
Пример запроса и ответа

Заголовок X-Auth-Token: содержит маркер аутентификации, который должен сопровождать все последующие запросы к службе Object Storage, а заголовок X-Storage-Url: указывает URL-адрес хранения этой подзаписи для всех манипуляций с контейнерами и объектами. Эти два элемента позволяют взаимодействовать со службой Object Storage с помощью API Swift OpenStack.

Например, чтобы добавить контейнер в учетную подзапись пользователя, отправьте запрос PUT в конечную точку X-Storage-Url, добавив в конец URL-адреса новое имя контейнера и не забыв включить в запрос заголовок X-Auth-Token:. Для того чтобы создать новый контейнер с именем container_1 для подзаписи с X-Storage-URL https://xyz.objectstorage.softlayer,net/AUTH_123, отправьте запрос PUT на адрес https://xyz.objectstorage.softlayer,net/AUTH_123/container_1.

Ниже приведен пример запроса и ответа:

Пример запроса и ответа
Пример запроса и ответа

Аналогично, чтобы перечислить все контейнеры в учетной подзаписи пользователя, отправьте запрос GET в соответствующую конечную точку X-Storage-Url без всяких параметров, как показано в этом примере, используя адрес https://xyz.objectstorage.softlayer, net/AUTH_123:

Пример запроса и ответа
Пример запроса и ответа

Для приложения, которое мы построим в этом руководстве, вам не придется иметь дело непосредственно с запросами и ответами, как описано выше. Клиентская библиотека PHP IBM jStart службы Bluemix Object Storage позаботится о решении всех этих задач посредством удобной оболочки, так что вам достаточно просто вызвать соответствующий метод — например, createContainer() или listContainers()—, а клиентская библиотека сама позаботиться о формировании запроса и декодирования ответа. Тем не менее, нужно знать, что происходит за сценой, для целей отладки и на тот случай, если вы захотите выполнить операцию, которая пока не поддерживается клиентом.

Шаг 1. Создание заготовки приложения

На первом шаге мы создадим заготовку приложения с PHP-микросредой Silex и другими компонентами. Ее можно легко загрузить и установить с помощью менеджера PHP-зависимостей Composer.

Перейдите в корневой каталог документов веб-сервера (обычно /usr/local/apache/htdocs в Linux или C:\Program Files\Apache\htdocs в Windows) и создайте новый каталог для своего приложения.

shell> cd /usr/local/apache/htdocs 
shell> mkdir photos

В этой статье он будет называться $APP_ROOT. При такой конфигурации ваше приложение будет напрямую доступно по URL-адресу http://localhost/photos. Помните, что когда вы развернете приложение в Bluemix, его URL-адрес изменится.

Создайте файл конфигурации Composer, который нужно сохранить в файле $APP_ROOT/composer.json:

{ 
    "require": { 
        "silex/silex": "*", 
        "twig/twig": "*",         
        "ibmjstart/zendservice-openstack": "dev-master" 
    }, 
    "minimum-stability" : "dev", 
    "prefer-stable": true 
}

Установите с помощью Composer компоненты Silex, Twig и клиентскую библиотеку PHP Object Storage:

shell> cd /usr/local/apache/htdocs/photos 
shell> php composer.phar install

Шаг 2. Добавление пользовательского интерфейса

Следующий шаг заключается в создании простого пользовательского интерфейса, который выдает список доступных фотографий и содержит элементы управления для добавления новых изображений и редактирования или удаления существующих. Вот базовая структура этого пользовательского интерфейса, которую нужно сохранить под именем $APP_ROOT/views/main.tpl.php:

<!DOCTYPE html> 
<html lang="en"> 
  <head> 
    <meta charset="utf-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> 
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> 
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> 
    <!--[if lt IE 9]> 
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> 
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> 
    <![endif]--> 
  </head> 
  <body> 
 
    <div class="panel panel-default"> 
      <div class="panel-heading clearfix"> 
        <h4 class="pull-left">Photos</h4> 
      </div> 
    </div> 
   <div class="container"> 
      <a href="{{ app.request.basepath }}/add" role="button" class="btn btn-default btn-sm"><span class="glyphicon glyphicon-plus"></span> Add</a> 
    </div> 

     
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> 
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>    
  </body> 
</html>

Эта заготовка страницы устанавливает Bootstrap и jQuery из соответствующих сетей доставки контента (CDN) и определяет универсальный шаблон страницы Bootstrap. Пока она содержит только заголовок и единственную кнопку для добавления новых фотографий; однако по мере разработки приложения мы добавим список фотографий и дополнительные элемента управления.

На сервере Silex позаботится о визуализации этого шаблона в ответ на запросы по таким URL-маршрутам, как / или /index. Вот код, который нужно сохранить в файле $APP_ROOT/index.php:

<?php 
// Использование автозагрузчика Composer 
require 'vendor/autoload.php'; 
 
// Загрузка классов 
use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\HttpFoundation\Response; 
use Silex\Application; 
use ZendService\OpenStack\ObjectStorage; 
 
// Инициализация приложения Silex 
$app = new Application(); 
 
// Регистрация источника шаблона Twig 
$app->register(new Silex\Provider\TwigServiceProvider(), array( 
  'twig.path' => __DIR__.'/views', 
)); 
 
// Регистрация генератора URL 
$app->register(new Silex\Provider\UrlGeneratorServiceProvider()); 
 
// Обработчики страницы index  
$app->get('/', function () use ($app) { 
  return $app->redirect($app["url_generator"]->generate('index')); 
}); 
 
$app->get('/index', function () use ($app) { 
  return $app['twig']->render('index.twig'); 
})->bind('index'); 
 
$app->run();

Приведенный выше сценарий представляет собой главный управляющий сценарий приложения. Он начинается с загрузки всех необходимых классов и инициализации нового объекта приложения Silex. В рамках процесса инициализации в приложении также регистрируются шаблонизатор Twig, путь к каталогу с шаблонами и службы генератора URL-адресов.

После настройки объекта приложения сценарий настраивает обратный вызов маршрутизатора для маршрута /index и определяет функцию, вызываемую в случае соответствия этого маршрута входящему запросу. Приведенный выше сценарий обратного вызова /index просто воспроизводит указанный шаблон главной страницы приложения. Сценарий также настраивает обратный вызов маршрутизатора для маршрута /, который просто выполняет перенаправление по маршруту /index. Обратите внимание на использование генератора URL-адресов Silex, который автоматически генерирует правильные URL-адреса для именованных маршрутов с учетом текущего пути приложения на сервере.

Silex зависит от перезаписи дружественных URL-адресов, так что это также подходящий момент для настройки правил перезаписи URL-адресов на веб-сервере. Так как приложение в конечном итоге будет развернуто на веб-сервере Apache, создайте файл $APP_ROOT/.htaccess и заполните его с следующим содержанием:

<IfModule mod_rewrite.c> 
    Options -MultiViews 

    RewriteEngine On 
    #RewriteBase /path/to/app 
    RewriteCond %{REQUEST_FILENAME} !-f 
    RewriteRule ^ index.php [QSA,L] 
</IfModule>

Теперь, когда у вас есть доступ к приложению через http://localhost/photos/index, вы должны увидеть созданный ранее шаблон страницы Bootstrap:

Шаблон страницы Bootstrap

Шаг 3. Процесс добавления фотографий

В шаблоне страницы, который мы создали, уже есть кнопка Add с гиперссылкой на URL-маршрут /add. Следующий логический шаг — конкретизировать эту функциональность путем добавления формы и процессора форм для добавления новых фотографий. Для этого создайте следующую простую веб-форму и сохраните ее под именем $APP_ROOT/views/add.twig:

<!DOCTYPE html> 
<html lang="en"> 
  <head> 
    <meta charset="utf-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> 

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> 
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> 
    <!--[if lt IE 9]> 
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> 
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> 
    <![endif]--> 
  </head> 
  <body> 
    <div class="panel panel-default"> 
      <div class="panel-heading clearfix"> 
        <h4 class="pull-left">Add Photo</h4> 
      </div> 
    </div> 
    <form enctype="multipart/form-data" action="{{ app.request.basepath }}/add" method="post"> 
      <div class="form-group"> 
        <div class="input-group"> 
          <span class="input-group-addon">Select photo</span> 
          <input type="file" name="file" class="form-control" style="height: auto" /> 
        </div> 
      </div>       
      <div class="form-group"> 
        <button type="submit" name="submit" class="btn btn-default">Upload</button> 
      </div>    
    </form> 
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> 
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> 
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>    
  </body> 
</html>

Эта форма содержит селектор файлов:

Форма для выбора файла
Форма для выбора файла

После ввода выбранный файл необходимо проверить, чтобы убедиться, что это изображение, а затем добавить в хранилище объектов, как показано в этой редакции файла $APP_ROOT/index.php:

<?php 
// Использование автозагрузчика Composer – в сокращении! 
// Загрузка классов – в сокращении! 
 
// Определение массива конфигурации 
// ... службы Object Storage 
$config["storage"] = array( 
  'service' => array( 
  ), 
  'adapter' => array( 
    'adapter' => 'Zend\Http\Client\Adapter\Curl', 
    'curloptions' => array(CURLOPT_SSL_VERIFYPEER => false, CURLOPT_TIMEOUT => 6000),   
  ) 
); 
 
// Использование среды BlueMix VCAP_SERVICES 
// Создание учетной подзаписи me в службе хранения 
if ($services = getenv("VCAP_SERVICES")) { 
  $services_json = json_decode($services, true); 
  $config["storage"]["service"]["url"] = $services_json["objectstorage"][0]["credentials"]["auth_uri"] . '/me'; 
  $config["storage"]["service"]["user"] = $services_json["objectstorage"][0]["credentials"]["username"]; 
  $config["storage"]["service"]["key"] = $services_json["objectstorage"][0]["credentials"]["password"]; 
} else { 
  throw new Exception('Not in Bluemix environment'); 
} 
 
// Инициализация приложения Silex 
$app = new Application(); 
 
// Регистрация источника шаблона Twig

$app->register(new Silex\Provider\TwigServiceProvider(), array( 
  'twig.path' => __DIR__.'/views', 
)); 
 
// Регистрация генератора URL 
$app->register(new Silex\Provider\UrlGeneratorServiceProvider()); 
 
// Инициализация клиента ObjectStorage 
// Присоединение к приложению Silex 
$app['os'] = new ObjectStorage( 
  $config["storage"]["service"], 
  new Zend\Http\Client('', $config["storage"]["adapter"]) 
); 
 
 
// Обработчики страницы index – в сокращении! 
 
// Форма загрузки файлов 
$app->get('/add', function () use ($app) { 
  return $app['twig']->render('add.twig'); 
}); 
 
// Процессор загрузки файлов 
// Получение и проверка загруженного файла 
// Создание контейнера и добавление файла, если все правильно 
$app->post('/add', function (Request $request) use ($app) { 
  $file = $request->files->get('file'); 
  if ($file && $file->isValid()) { 
    if (in_array($file->getClientMimeType(), array('image/gif', 'image/jpeg', 'image/png'))) { 
      $app['os']->createContainer('default'); 
      $app['os']->setObject('default', $file->getClientOriginalName(), file_get_contents($file->getRealPath())); 
    } else { 
      throw new Exception('Invalid image format'); 
    } 
  } else { 
    throw new Exception('Invalid upload'); 
  } 
  return $app->redirect($app["url_generator"]->generate('index'));     
}); 
 
$app->error(function (\Exception $e, $code) use ($app) { 
  return $app['twig']->render('error.twig', array('error' => $e->getMessage())); 
}); 
 
$app->run();

Здесь происходит много всего, так что давайте рассмотрим этот листинг подробнее.

  • Сценарий начинается с инициализации нового объекта клиента ObjectStorage и настройки его на использование подключенной службы Object Storage в Bluemix. URL-адрес соответствующей службы Object Service получаем из специальной переменной среды Bluemix VCAP_SERVICES. Поскольку мы еще не добавили к приложению поддержку нескольких пользователей, для хранения будет использоваться временная учетная подзапись с именем me; это имя подзаписи добавляется в конец URL-адреса службы.
  • Сценарий добавляет обработчик маршрутов GET для конечной точки /add. Это просто воспроизведение шаблона формы, показанной выше.
  • Сценарий также добавляет обработчик маршрутов POST для той же конечной точки, которая отвечает за обработку загружаемого файла. Этот обработчик извлекает загруженный файл из объекта Request в виде объекта File и использует метод объекта File getClientMimeType() для проверки того, что это действительно файл изображения.
  • Если загружено допустимое изображение, обработчик использует настроенный клиент ObjectStorage для создания контейнера с именем default с помощью метода клиента createContainer(). (Помните, что каждый объект должен быть связан с контейнером). На последнем шаге используется метод setObject() объекта клиента для сохранения загруженного файла в контейнер с использованием оригинального имени и двоичного содержания файла.

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

Если загрузка файла не удалась или если загруженный файл не распознается как допустимое изображение, обработчик POST вызывает исключение. Это исключение обрабатывается методом error() приложения, который просто воспроизводит шаблон сообщения об ошибке и кнопку Try Again. Здесь этот шаблон для краткости опущен, но его можно найти в репозиторий исходного кода приложения. Вот как он выглядит в действии:

Шаблон сообщения об ошибке
Шаблон сообщения об ошибке

Шаг 4. Вывод списка и отображение фотографий

Шаблон главной странице (пока) не содержит никакого кода для вывода списка загруженных объектов, так что после загрузки новой фотографии вы будете по-прежнему видеть пустую страницу. Давайте исправим это, отредактировав обработчик /index в файле $APP_ROOT/index.php так, чтобы выводить список фотографий из контейнера default:

<?php 
// Инициализация и настройка приложения – в сокращении! 
 
// Обработчик страницы index  
$app->get('/index', function () use ($app) { 
  $containers = array( array('name' => 'default') ); 
  foreach ($containers as &$c) { 
    $objects = json_decode($app['os']->listObjects($c['name'])); 
    foreach ($objects as &$o) { 
      $o = (array) $o; 
      $o['url'] = $app['os']->getObjectUrl($c['name'], $o['name']); 
    } 
    $c['objects'] = $objects; 
  } 
  return $app['twig']->render('index.twig', array( 
    'containers' => $containers 
    )); 
})->bind('index'); 
 
// Обработчики маршрутов – в сокращении! 
 
$app->run();

Здесь метод клиентского объекта listObjects() возвращает все сохраненные объекты из контейнера default с их уникальными URL-адресами. Затем эта информация передается в шаблон страницы index.

Шаблон страницы index теперь тоже необходимо обновить для отображения списка фотографий. Вот обновленный шаблон, который нужно сохранить в файле $APP_ROOT/views/index.twig:

<!DOCTYPE html> 
<html lang="en"> 
  <head> 
    <meta charset="utf-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> 
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> 
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> 
    <!--[if lt IE 9]> 
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> 
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> 
    <![endif]--> 
  </head> 
  <body> 
 
    <div class="panel panel-default"> 
      <div class="panel-heading clearfix"> 
        <h4 class="pull-left">Photos</h4> 
      </div> 
    </div> 

    {% if containers|length > 0 %} 
    {% for c in containers %} 
    <div class="container"> 
      <p class="clearfix"> 
          <span class="btn btn-success"> {{ c.name }} <span class="badge">{{ c.objects|length }}</span></span> 
      </p> 
      <div class="clearfix"> 
          <a href="{{ app.request.basepath }}/add" role="button" class="btn btn-primary btn-sm"><span class="glyphicon glyphicon-plus"></span> Add Photo</a> 
      </div> 
      <hr/> 
      {% for o in c.objects %}         
      <ul class="list-group row clearfix"> 
        <li class="list-group-item col-xs-3 clearfix" style="border:none"><img src="{{ o.url }}" class="img-responsive" /></li> 
        <li class="list-group-item col-xs-5 clearfix" style="border:none"> 
          <p> {{ o.name }} </p> 
          <h6> {{ o.bytes }} bytes </h6> 
        </li> 
        <li class="list-group-item col-xs-4 clearfix" style="border:none"> 
          <p> 
            <a href="{{ o.url }}" role="button" class="btn btn-primary btn-sm">View</a> <br/> 
          </p> 
          <p> 
            <a href="{{ app.request.basepath }}/delete/{{ c.name|url_encode }}/{{ o.name }}" role="button" class="btn btn-primary btn-sm">Delete</a> 
          </p>           
        </li> 
      </ul> 
      <hr/> 
      {% endfor %} 
    </div> 
    {% endfor %} 
    {% else %} 
    <div class="container"> 
      <h4>No photos found.</h4> 
      <a href="{{ app.request.basepath }}/add" role="button" class="btn btn-default btn-sm"><span class="glyphicon glyphicon-plus"></span> Add</a> 
    </div> 
    {% endif %} 

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> 
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> 
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>    
  </body> 
</html>

Здесь цикл foreach() перебирает массив с информацией, полученной от службы Object Storage, и выводит имя и размер каждой фотографии. Кроме того, к каждой записи прикреплены две кнопки для редактирования и удаления фотографии с гиперссылками на конечные точки URL уникального объекта и URL /delete соответственно. Обратите внимание, что гиперссылка /delete включает в качестве параметров маршрута как имя объекта, так и имя контейнера — например, /delete/default/file.png.

Шаг 5. Удаление существующих фотографий

Как говорилось выше, рядом с каждой фотографией есть кнопка Delete с гиперссылкой на конечную точку URL /delete и у каждой фотографии есть имя контейнера и имя объекта. Нам достаточно добавить обработчик для данного маршрута, который считывает эти параметры и вызывает метод deleteObject() объекта клиента ObjectStorage, чтобы удалить соответствующий объект из хранилища объектов. Вот код, который нужно добавить в файл $APP_ROOT/index.php:

<?php 
// Инициализация и настройка приложения – в сокращении! 
 
// Обработчик удаления 
$app->get('/delete/{container}/{object}', function ($container, $object) use ($app) { 
  $container = urldecode($container); 
  $object = urldecode($object); 
  $app['os']->deleteObject($container, $object);   
  return $app->redirect($app["url_generator"]->generate('index')); 
}); 
 
// Обработчики маршрутов – в сокращении! 
 
$app->run();

Теперь у вас есть приложение, которое поддерживает добавление, вывод списка и удаление фотографий с помощью службы Bluemix Object Storage. Правда, им пока нельзя пользоваться, так как для его правильной работы нужна платформа Bluemix с подключенным экземпляром службы Object Storage. Я покажу, как это сделать, в Части 2, из которой вы также узнаете, как добавить поддержку нескольких контейнеров и нескольких пользователей.


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Облачные вычисления
ArticleID=1030378
ArticleTitle=Создание службы хранения фотографий в облаке с помощью PHP и IBM Bluemix. Часть 1
publish-date=04222016