В связи с тем, что предыдущий сервис с помощью которого я хранил изображения накрылся медным тазом (скорее всего из-за того, что был не прибыльный), мне пришлось искать другие варианты хранения изображений. Сервера я использую бюджетные и не хотелось бы мне платить приличную цену за дополнительные 10 ГБ дисковой памяти. Изучая рынок я наткнулся на Google Cloud Storage (GCS) и решил, что данный продукт мне подойдет (ну как минимум можно протестировать). В рунете (да и не только в нем) мало уделяется внимания для настройки GCS с использованием PHP, поэтому я решил внести свою лепту в это направление.
В данной статье будет рассмотрено 2 варианта настройки GCS для загрузки файлов (в примере будет реализована загрузка изображения) с помощью php-клиента и с помощью существующего sdk (утилита gsutil) используя shell. Итак, поехали!
Регистрация
Первое, что необходимо будет сделать — зарегистрироваться в Google Cloud Platform. Для этого можете перейти по ссылке. Возможно еще не кончилась акция, и вы сможете получить 300$ в подарок! Проблем у вас не должно возникнуть, поэтому процесс регистрации решил не описывать. Правда вам необходимо будет оставить свой номер телефона и кредитной карты.
Для проверки перейдите в раздел «Оплата» и если вы увидите подобное окно, значит вам желательно привязать платежный аккаунт, иначе вы не сможете использовать GCS JSON API.
Создание и настройка проекта
В шапке станицы у вас должен отобразиться блок Выберите проект, при нажатии на который вы увидите следующее окно. В моём случае вместо Выберите проект отображается Photo Project, т.к. у меня уже созданы два проекта.
Создайте свой новый проект, нажав на кнопку СОЗДАТЬ ПРОЕКТ. Введите название проекта, и нажмите на кнопку СОЗДАТЬ. В течении нескольких секунд будет создан ваш проект и вы сможете его выбрать.
После того, как вы выбрали созданный проект перейдите в раздел Оплата с помощью меню навигации слева на странице. Обратите внимание, чтобы в блоке Проекты в этом платежном аккаунте был ваш созданный проект. В моём случае новый проект с названием My Project 71698 был автоматически добавлен в этот блок.
Но, может быть и такая ситуация, например, когда проект был создан до момента привязки платежного аккаунта, но он не будет автоматически добавлен. Поэтому советую первоначально привязать платежный аккаунт.
В случае, если вашего проекта в блоке не появилось, то вам необходимо перейти в раздел Storage с помощью меню навигации и выбрать платежный аккаунт с помощью кнопки Включить оплату. В моём случае у проекта Share The Route платежный аккаунт создан не был.
Создать сегмент можно с помощью интерфейса или через запрос. Пока давайте создадим сегмент с помощью интерфейса нажав на кнопку Создать сегмент.
В зависимости от поставленной задачи, вы можете выбрать нужный вам Класс хранилища по умолчанию.
Если кратко описать классы хранилища, то:
Multi-Regional — подойдет для сайта, которым пользуется вся страна
Regional — сайт, который в большинстве случаев используется в одном каком-то регионе
Nearline — для данных, которые используются не чаще, чем раз в месяц.
Coldline — для данных, которые используются не чаще, чем раз в год.
Выбрав все необходимые настройки нажмите на кнопку Создать.
Настройка сервера используя php-клиент
Для настройки сервера вы можете обратиться к Cloud Storage Client Libraries, если вы захотите настроить проект под другой язык программирования.
Для начала, вам необходимо скачать библиотеку для работы с GCS используя composer. Если вдруг у кого его нет — поставьте.
composer require google/cloud-storage
Далее вам необходимо получить ключ, для этого перейдите по ссылке и во вкладке Учетные данные нажмите на кнопку Создать учетные данные и выберите Ключ сервисного аккаунта.
В новом окне выберите Новый сервисный аккаунт и заполните поле Название сервисного аккаунта. В Роли укажите Владелец (Проект->Владелец). После этого будет создан ключ в формате json. Вы можете сохранить его в удобное для вас место.
Далее, согласно инструкции, необходимо создать переменную среды указав путь до файла с ключом:
Linux/Mac OS
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/[FILE_NAME].json"
Windows
set GOOGLE_APPLICATION_CREDENTIALS="C:UsersnameDownloads[FILE_NAME].json"
Но в связи с тем, что по какой-то причине на моей Mac OS Sierra это не сработало, я покажу и альтернативный вариант, если вдруг кто-то столкнется с подобной проблемой.
На этом все необходимые настройки закончены и мы можем переходить к написанию кода:
Создадим index.html для загрузки изображения:
<html>
<head>
<title>File Upload</title>
</head>
<body>
<h1>File Upload</h1>
<form method="post" action="gcs.php" enctype="multipart/form-data">
<label for="inputfile">Upload File</label>
<input type="file" id="file" name="file"></br>
<input type="submit" value="Click To Upload">
</form>
</body>
</html>
И скрипт gcs.php, который будет загружать файл в облако:
# Подключаем autoloader для GCS
require __DIR__ . '/vendor/autoload.php';
# Подключаем необходимый класс для работы
use GoogleCloudStorageStorageClient;
# Проверяем, загрузил ли пользователь файл
if(isset($_FILES) && $_FILES['file']['error'] == 0) {
$allowed = array('png', 'jpg', 'gif','zip', 'jpeg');
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
if(!in_array(strtolower($ext), $allowed)) {
echo 'The file is not an image.';
die;
}
# Указываем название проекта
$projectId = 'crafty-booth-205017';
# Создаем соединение с GCS
$storage = new StorageClient([
'projectId' => $projectId,
'keyFilePath' => '/home/user/Downloads/[FILE_NAME].json' # Наш ключ
]);
# Наш сегмент
$bucketName = 'my-project-for-img';
$bucket = $storage->bucket($bucketName);
# Код загрузки файла
$uploader = $bucket->getResumableUploader(
fopen($_FILES['file']['tmp_name'], 'r'), [
'name' => 'images/load_image.png',
'predefinedAcl' => 'publicRead',
]
);
# Отправка файла в облоко GCS
try {
$uploader->upload();
echo 'File Uploaded';
} catch (GoogleException $ex) {
$resumeUri = $uploader->getResumeUri();
$object = $uploader->resume($resumeUri);
echo 'No File Uploaded';
}
}
else{
echo 'No File Uploaded';
}
Теперь давайте немого разберем код.
$projectId — для того, чтобы узнать идентификатор вашего проекта, вы можете в шапке кликнуть на выпадающий список проектов и увидеть в новом окне столбцы Имя и Идентификатор. Столбец Идентификатор, как раз содержит необходимое нам значение.
keyFilePath — ссылка на наш ключ, который мы скачали. Данное свойство необходимо указывать, если установленную переменную среды не удается увидеть запущенному на сервере веб-сервису.
$bucketName — имя сегмента, который мы создали через интерфейс. Его вы можете увидеть в разделе Storage.
Метод getResumableUploader можно использовать и с одним параметром, тогда загружаемое изображение будет сохранено в корень сегмента с таким же именем. В моём примере использовались дополнительные свойства:
name — отвечает за новое имя файла. Но тут можно указывать не только имя, но и вместе с этим путь, относительно сегмента. В данном случае добавлена директория images, которая будет автоматически создана, если её ещё нет.
predefinedAcl — устанавливает уровень доступа к загружаемому файлу. Значение publicRead говорит о том, что данный файл может быть доступен по ссылке любому пользователю/сайту.
Более подробно метод можно изучить по следующей ссылке.
Плюсы подхода:
+ библиотека размером < 10 МБ;
+ можно использовать на сторонних хостингах.
Минусы подхода:
— библиотека находится в бете;
— сложная для понимания документация.
Таким образом, данный вариант я бы посоветовал для тех людей, у которых мало свободного пространства на сервере или если вы используете сторонний сервер без доступа к терминалу для установки утилиты gsutil.
Настройка сервера используя утилиту gsutil
Для начала нужно установить данную утилиту на сервер. Для этого скачиваем и устанавливаем sdk (пример для Linux/Mac OS. Под Debian/Ubuntu и Windows смотрите по ссылке):
curl https://sdk.cloud.google.com | bash
После установки сбрасываем shell:
exec -l $SHELL
Если при сбросе shell'а вам выпала ошибка о том, что модуль node не был найден, то попробуйте его удалить и снова установить через репозиторий.
Выполните инициализацию:
gcloud init
Во время инициализации необходимо будет авторизоваться. Если сервер поддерживает графическую оболочку, то автоматически откроется старица в браузере, где необходимо будет подтвердить доступ, иначе вы получите текст ссылки, по которой необходимо перейти. На этой странице будет находиться ключ, который необходимо будет ввести в терминал.
После того, как пройдет авторизация, вам будет предложено выбрать сегмент, с которым вы будете работать.
Теперь вы можете использовать все возможности утилиты gsutil. Ознакомиться со всеми командами можете по ссылке.
Для копирования изображений нам понадобиться команда cp. Она имеет следующую структуру:
gsutil cp [OPTION]... src_url dst_url
Для отправки изображения используем тот же index.html
<html>
<head>
<title>File Upload</title>
</head>
<body>
<h1>File Upload</h1>
<form method="post" action="gcs.php" enctype="multipart/form-data">
<label for="inputfile">Upload File</label>
<input type="file" id="file" name="file"></br>
<input type="submit" value="Click To Upload">
</form>
</body>
</html>
И существенно изменим gcs.php:
# Проверяем, загрузил ли пользователь файл
if(isset($_FILES) && $_FILES['file']['error'] == 0) {
$allowed = array('png', 'jpg', 'gif','zip', 'jpeg');
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
if(!in_array(strtolower($ext), $allowed)) {
echo 'The file is not an image.';
die;
}
$name = time() . "." . $ext;
$cmd = "gsutil cp -a public-read $_FILES['file']['tmp_name'] gs://my-project-for-img/images/{$name}";
$result = liveExecuteCommand($cmd);
if ($result['exit_status'] !== 0) {
echo 'No File Uploaded';
die;
}
echo 'File Uploaded';
}
else{
echo 'No File Uploaded';
}
Функция liveExecuteCommand позволяет отследить, как выполняется переданная команда с промежуточным выводом, но вы можете использовать и shell_exec.
public function liveExecuteCommand($cmd, $isLog = false)
{
while (@ ob_end_flush()); // end all output buffers if any
$proc = popen("$cmd 2>&1 ; echo Exit status : $?", 'r');
$live_output = "";
$complete_output = "";
while (!feof($proc))
{
$live_output = fread($proc, 4096);
$complete_output = $complete_output . $live_output;
if ($isLog)
echo "$live_output";
@ flush();
}
pclose($proc);
# get exit status
preg_match('/[0-9]+$/', $complete_output, $matches);
# return exit status and intended output
return array (
'exit_status' => intval($matches[0]),
'output' => str_replace("Exit status : " . $matches[0], '', $complete_output)
);
}
Плюсы подхода:
+ удобная документация;
+ возможность работы через терминал;
+ больше возможностей.
+ простой синтаксис
Минусы:
— вес sdk около 280 МБ;
— необходим доступ к терминалу на сервере
По моему мнению данный подход больше подходит тем, у кого нет проблем со свободным местом на сервере (т.к. sdk в 28 раз тяжелее) или необходима работа как через shell, так и через терминал.
Заключение
В статье я попытался подробно и по шагам расписать, как можно использовать GCS для сохранения изображений и в каком случае использовать тот или иной подход. Не претендую на абсолютную истину, но т.к. на данную тематику слишком мало информации в рунете — считаю, что статья должна быть интересна тем, кто захочет так же добавить GCS в свой проект.
Автор: AGENTxXx