Files
crm.clientright.ru/include/Storage/S3StorageService.php.backup

276 lines
9.9 KiB
Plaintext
Raw Normal View History

<?php
/**
* S3 Storage Service
* Сервис для работы с S3 хранилищем в vTiger CRM
*
* Функции:
* - Загрузка файлов в S3 с retry механизмом
* - Получение presigned URLs для скачивания
* - Логирование ошибок
*
* @author AI Assistant
* @date 2025-09-20
*/
require_once __DIR__ . '/../../crm_extensions/file_storage/S3Client.php';
class S3StorageService {
private $s3Client;
private $bucket;
private $prefix;
private $config;
public function __construct() {
// Загружаем конфигурацию S3
try {
file_put_contents('logs/debug.log', '[' . date('Y-m-d H:i:s') . '] S3StorageService: Loading config...' . PHP_EOL, FILE_APPEND);
$this->config = require __DIR__ . '/../../crm_extensions/file_storage/config.php';
file_put_contents('logs/debug.log', '[' . date('Y-m-d H:i:s') . '] S3StorageService: Config loaded, S3 key=' . $this->config['s3']['key'] . PHP_EOL, FILE_APPEND);
$this->s3Client = new S3Client($this->config['s3']);
$this->bucket = $this->config['s3']['bucket'];
$this->prefix = 'crm2/CRM_Active_Files/Documents';
file_put_contents('logs/debug.log', '[' . date('Y-m-d H:i:s') . '] S3StorageService: Constructor completed successfully' . PHP_EOL, FILE_APPEND);
} catch (Exception $e) {
file_put_contents('logs/debug.log', '[' . date('Y-m-d H:i:s') . '] S3StorageService ERROR: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
throw $e;
}
// Создаем папку для логов если её нет
$logDir = __DIR__ . '/../../logs';
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
}
/**
* Загрузка файла в S3 с retry механизмом
*
* @param string $tmpFile Временный файл на сервере
* @param int $notesId ID записи документа
* @param string $filename Имя файла
* @param int $maxRetries Максимальное количество попыток
* @return array Результат загрузки
* @throws Exception При неудачной загрузке
*/
public function put($tmpFile, $notesId, $filename, $maxRetries = 3) {
$key = $this->prefix . '/' . $notesId . '/' . $filename;
$this->logInfo("Starting S3 upload: key=$key, file=$tmpFile");
for ($i = 0; $i < $maxRetries; $i++) {
try {
$this->logInfo("S3 upload attempt " . ($i + 1) . "/$maxRetries");
// Проверяем, что временный файл существует
if (!file_exists($tmpFile)) {
throw new Exception("Temporary file not found: $tmpFile");
}
// Получаем MIME тип
$mimeType = $this->getMimeType($tmpFile);
// Загружаем файл в S3
$result = $this->s3Client->uploadFile($tmpFile, $key, [
'ContentType' => $mimeType,
'Metadata' => [
'notesId' => (string)$notesId,
'originalName' => (string)$filename,
'uploadedAt' => (string)date('Y-m-d H:i:s')
]
]);
if ($result['success']) {
$this->logInfo("S3 upload successful: key=$key, etag=" . $result['etag']);
return [
'key' => $key,
'etag' => $result['etag'],
'url' => $result['url'],
'bucket' => $this->bucket,
'size' => filesize($tmpFile),
'mimeType' => $mimeType
];
} else {
throw new Exception("S3 upload failed: " . ($result['error'] ?? 'Unknown error'));
}
} catch (Exception $e) {
$this->logError("S3 upload attempt " . ($i + 1) . " failed: " . $e->getMessage());
if ($i < $maxRetries - 1) {
$delay = pow(2, $i); // 1s, 2s, 4s
$this->logInfo("Retrying in {$delay}s...");
sleep($delay);
}
}
}
$errorMsg = "S3 upload failed after $maxRetries attempts for key: $key";
$this->logError($errorMsg);
throw new Exception($errorMsg);
}
/**
* Получение presigned URL для скачивания
*
* @param string $key S3 ключ файла
* @param string $ttl Время жизни URL
* @return string Presigned URL
*/
public function presignGet($key, $ttl = '+10 minutes') {
try {
$this->logInfo("Generating presigned URL for key: $key, TTL: $ttl");
$presignedUrl = $this->s3Client->getPresignedUrl($key, $ttl);
// Если getPresignedUrl возвращает массив, берем URL
if (is_array($presignedUrl)) {
$presignedUrl = $presignedUrl['url'] ?? $presignedUrl[0] ?? '';
}
$this->logInfo("Presigned URL generated successfully");
return $presignedUrl;
} catch (Exception $e) {
$this->logError("Failed to generate presigned URL for key $key: " . $e->getMessage());
throw $e;
}
}
/**
* Удаление файла из S3
*
* @param string $key S3 ключ файла
* @return bool Результат удаления
*/
public function delete($key) {
try {
$this->logInfo("Deleting S3 object: key=$key");
$result = $this->s3Client->deleteObject($key);
if ($result['success']) {
$this->logInfo("S3 object deleted successfully: key=$key");
return true;
} else {
$this->logError("Failed to delete S3 object: key=$key, error=" . ($result['error'] ?? 'Unknown error'));
return false;
}
} catch (Exception $e) {
$this->logError("Exception while deleting S3 object $key: " . $e->getMessage());
return false;
}
}
/**
* Проверка существования файла в S3
*
* @param string $key S3 ключ файла
* @return bool Файл существует
*/
public function exists($key) {
try {
return $this->s3Client->fileExists($key);
} catch (Exception $e) {
$this->logError("Error checking S3 object existence $key: " . $e->getMessage());
return false;
}
}
/**
* Получение MIME типа файла
*
* @param string $filePath Путь к файлу
* @return string MIME тип
*/
private function getMimeType($filePath) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filePath);
finfo_close($finfo);
// Fallback для случаев, когда finfo не работает
if (!$mimeType) {
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$mimeTypes = [
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'ppt' => 'application/vnd.ms-powerpoint',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'txt' => 'text/plain',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif'
];
$mimeType = isset($mimeTypes[$extension]) ? $mimeTypes[$extension] : 'application/octet-stream';
}
return $mimeType;
}
/**
* Логирование информационных сообщений
*
* @param string $message Сообщение
*/
private function logInfo($message) {
$this->log('INFO', $message);
}
/**
* Логирование ошибок
*
* @param string $message Сообщение об ошибке
*/
private function logError($message) {
$this->log('ERROR', $message);
}
/**
* Общее логирование
*
* @param string $level Уровень лога
* @param string $message Сообщение
*/
private function log($level, $message) {
$logFile = __DIR__ . '/../../logs/s3_storage.log';
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[$timestamp] [$level] $message\n";
file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
/**
* Получение конфигурации S3
*
* @return array Конфигурация
*/
public function getConfig() {
return $this->config['s3'];
}
/**
* Получение префикса для S3 ключей
*
* @return string Префикс
*/
public function getPrefix() {
return $this->prefix;
}
/**
* Получение имени bucket'а
*
* @return string Имя bucket'а
*/
public function getBucket() {
return $this->bucket;
}
}
?>