198 lines
6.0 KiB
PHP
198 lines
6.0 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* S3-Compatible Storage Client
|
|||
|
|
* Клиент для работы с S3-совместимым хранилищем (TWC Storage)
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|||
|
|
|
|||
|
|
use Aws\S3\S3Client as AwsS3Client;
|
|||
|
|
use Aws\Exception\AwsException;
|
|||
|
|
|
|||
|
|
class S3Client {
|
|||
|
|
private $client;
|
|||
|
|
private $bucket;
|
|||
|
|
|
|||
|
|
public function __construct($config) {
|
|||
|
|
$this->bucket = $config['bucket'];
|
|||
|
|
|
|||
|
|
// Настройка для российского S3-совместимого хранилища
|
|||
|
|
$this->client = new AwsS3Client([
|
|||
|
|
'version' => $config['version'],
|
|||
|
|
'region' => $config['region'],
|
|||
|
|
'endpoint' => $config['endpoint'],
|
|||
|
|
'use_path_style_endpoint' => $config['use_path_style_endpoint'],
|
|||
|
|
'credentials' => [
|
|||
|
|
'key' => $config['key'],
|
|||
|
|
'secret' => $config['secret'],
|
|||
|
|
],
|
|||
|
|
'http' => [
|
|||
|
|
'verify' => true,
|
|||
|
|
]
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузка файла в S3
|
|||
|
|
*/
|
|||
|
|
public function uploadFile($localPath, $s3Key, $options = []) {
|
|||
|
|
try {
|
|||
|
|
$putObjectParams = [
|
|||
|
|
'Bucket' => $this->bucket,
|
|||
|
|
'Key' => $s3Key,
|
|||
|
|
'SourceFile' => $localPath,
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
// Добавляем ContentType если указан
|
|||
|
|
if (isset($options['ContentType'])) {
|
|||
|
|
$putObjectParams['ContentType'] = $options['ContentType'];
|
|||
|
|
} else {
|
|||
|
|
$putObjectParams['ContentType'] = $this->getMimeType($localPath);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Добавляем метаданные если указаны
|
|||
|
|
if (isset($options['Metadata']) && is_array($options['Metadata'])) {
|
|||
|
|
// AWS SDK ожидает все значения метаданных как строки
|
|||
|
|
$metadata = [];
|
|||
|
|
foreach ($options['Metadata'] as $key => $value) {
|
|||
|
|
$metadata[$key] = (string)$value;
|
|||
|
|
}
|
|||
|
|
$putObjectParams['Metadata'] = $metadata;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$result = $this->client->putObject($putObjectParams);
|
|||
|
|
|
|||
|
|
return [
|
|||
|
|
'success' => true,
|
|||
|
|
'url' => $this->getPublicUrl($s3Key),
|
|||
|
|
's3_key' => $s3Key,
|
|||
|
|
'etag' => $result['ETag']
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
} catch (AwsException $e) {
|
|||
|
|
return [
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => $e->getMessage()
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Создание временной ссылки для скачивания
|
|||
|
|
*/
|
|||
|
|
public function getPresignedUrl($s3Key, $expiresIn = 3600) {
|
|||
|
|
try {
|
|||
|
|
$cmd = $this->client->getCommand('GetObject', [
|
|||
|
|
'Bucket' => $this->bucket,
|
|||
|
|
'Key' => $s3Key
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$request = $this->client->createPresignedRequest($cmd, "+{$expiresIn} seconds");
|
|||
|
|
|
|||
|
|
return [
|
|||
|
|
'success' => true,
|
|||
|
|
'url' => (string) $request->getUri()
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
} catch (AwsException $e) {
|
|||
|
|
return [
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => $e->getMessage()
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Удаление файла из S3
|
|||
|
|
*/
|
|||
|
|
public function deleteObject($s3Key) {
|
|||
|
|
try {
|
|||
|
|
$result = $this->client->deleteObject([
|
|||
|
|
'Bucket' => $this->bucket,
|
|||
|
|
'Key' => $s3Key
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
return [
|
|||
|
|
'success' => true,
|
|||
|
|
'deleted_key' => $s3Key
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
} catch (AwsException $e) {
|
|||
|
|
return [
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => $e->getMessage()
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Скачивание файла во временную папку
|
|||
|
|
*/
|
|||
|
|
public function downloadToTemp($s3Key) {
|
|||
|
|
$tempFile = sys_get_temp_dir() . '/' . uniqid('s3_download_') . '_' . basename($s3Key);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$this->client->getObject([
|
|||
|
|
'Bucket' => $this->bucket,
|
|||
|
|
'Key' => $s3Key,
|
|||
|
|
'SaveAs' => $tempFile
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
return $tempFile;
|
|||
|
|
|
|||
|
|
} catch (AwsException $e) {
|
|||
|
|
throw new Exception('S3 download failed: ' . $e->getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Проверка существования файла
|
|||
|
|
*/
|
|||
|
|
public function fileExists($s3Key) {
|
|||
|
|
try {
|
|||
|
|
$this->client->headObject([
|
|||
|
|
'Bucket' => $this->bucket,
|
|||
|
|
'Key' => $s3Key
|
|||
|
|
]);
|
|||
|
|
return true;
|
|||
|
|
} catch (AwsException $e) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Получение публичного URL
|
|||
|
|
*/
|
|||
|
|
private function getPublicUrl($s3Key) {
|
|||
|
|
return $this->client->getObjectUrl($this->bucket, $s3Key);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Определение MIME типа файла
|
|||
|
|
*/
|
|||
|
|
private function getMimeType($filePath) {
|
|||
|
|
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|||
|
|
$mimeType = finfo_file($finfo, $filePath);
|
|||
|
|
finfo_close($finfo);
|
|||
|
|
return $mimeType ?: 'application/octet-stream';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Генерация ключа для S3 на основе CRM данных
|
|||
|
|
*/
|
|||
|
|
public function generateS3Key($crmData, $isNewVersion = false) {
|
|||
|
|
$module = $crmData['module'] ?? 'Documents';
|
|||
|
|
$recordId = $crmData['record_id'] ?? 'unknown';
|
|||
|
|
$fileName = $crmData['file_name'] ?? 'file';
|
|||
|
|
|
|||
|
|
$basePath = strtolower($module) . '/' . $recordId;
|
|||
|
|
|
|||
|
|
if ($isNewVersion) {
|
|||
|
|
$timestamp = date('Y-m-d_H-i-s');
|
|||
|
|
$fileName = pathinfo($fileName, PATHINFO_FILENAME) . '_v' . $timestamp . '.' . pathinfo($fileName, PATHINFO_EXTENSION);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $basePath . '/' . $fileName;
|
|||
|
|
}
|
|||
|
|
}
|