1
0
Files
TaskBoard/backend/app/class/enity/class_fileUpload.php
Falknat 3bfa1e9e1b Комментарии, файлы и права проекта
- Система комментариев к задачам с вложенными ответами
- Редактирование и удаление комментариев
- Прикрепление файлов к задачам и комментариям (картинки, архивы до 10 МБ)
- Система прав проекта: админ проекта может удалять чужие комментарии и файлы
- Универсальный класс FileUpload для загрузки файлов
- Защита загрузки: только автор комментария может добавлять файлы
- Каскадное удаление: задача → комментарии → файлы
- Автообновление комментариев в реальном времени
2026-01-15 06:40:47 +07:00

224 lines
8.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
class FileUpload {
protected static $base_path = __DIR__ . '/../../../public/';
protected static $base_url = '/public/';
// === Маппинг сущностей (все параметры обязательны) ===
protected static $entities = [
'task' => [
'table' => 'cards_task',
'folder' => 'task',
'field' => 'file_img',
'allowed_ext' => ['png', 'jpg', 'jpeg', 'zip', 'rar'],
'archive_ext' => ['zip', 'rar'],
'max_size' => 10 * 1024 * 1024 // 10 MB
],
'comment' => [
'table' => 'comments',
'folder' => 'comment',
'field' => 'file_img',
'allowed_ext' => ['png', 'jpg', 'jpeg', 'zip', 'rar'],
'archive_ext' => ['zip', 'rar'],
'max_size' => 10 * 1024 * 1024 // 10 MB
]
];
// Получение конфигурации сущности
protected static function getConfig($entity_type) {
if (!isset(self::$entities[$entity_type])) {
return null;
}
return self::$entities[$entity_type];
}
// Получение данных сущности из БД
protected static function getEntityData($config, $entity_id) {
return Database::get($config['table'], '*', ['id' => $entity_id]);
}
// Обновление файлов сущности в БД
protected static function updateEntityFiles($config, $entity_id, $files) {
Database::update($config['table'], [
$config['field'] => json_encode($files, JSON_UNESCAPED_UNICODE)
], [
'id' => $entity_id
]);
}
// Генерация уникального имени файла
protected static function getUniqueName($upload_dir, $file_name) {
$ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
$base_name = pathinfo($file_name, PATHINFO_FILENAME);
$final_name = $file_name;
$counter = 1;
while (file_exists($upload_dir . '/' . $final_name)) {
$final_name = $base_name . '_' . $counter . '.' . $ext;
$counter++;
}
return $final_name;
}
// Форматирование размера файла для ошибки
protected static function formatSize($bytes) {
if ($bytes >= 1048576) {
return round($bytes / 1048576, 1) . ' МБ';
}
return round($bytes / 1024, 1) . ' КБ';
}
// === ЗАГРУЗКА ФАЙЛА ===
public static function upload($entity_type, $entity_id, $file_base64, $file_name) {
// Получаем конфигурацию
$config = self::getConfig($entity_type);
if (!$config) {
return ['success' => false, 'errors' => ['entity' => 'Неизвестный тип сущности']];
}
// Проверяем что сущность существует
$entity = self::getEntityData($config, $entity_id);
if (!$entity) {
return ['success' => false, 'errors' => ['entity' => 'Сущность не найдена']];
}
// Декодируем base64
$file_data = base64_decode(preg_replace('/^data:[^;]+;base64,/', '', $file_base64));
if (!$file_data) {
return ['success' => false, 'errors' => ['file' => 'Ошибка декодирования файла']];
}
// Проверка расширения
$ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
if (!in_array($ext, $config['allowed_ext'])) {
$allowed = strtoupper(implode(', ', $config['allowed_ext']));
return ['success' => false, 'errors' => ['file' => "Разрешены только: $allowed"]];
}
// Проверка размера
$file_size = strlen($file_data);
if ($file_size > $config['max_size']) {
$max_formatted = self::formatSize($config['max_size']);
return ['success' => false, 'errors' => ['file' => "Файл слишком большой. Максимум $max_formatted"]];
}
// Путь к папке
$upload_dir = self::$base_path . $config['folder'] . '/' . $entity_id;
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
// Уникальное имя
$final_name = self::getUniqueName($upload_dir, $file_name);
// Сохранение файла
$file_path = $upload_dir . '/' . $final_name;
if (!file_put_contents($file_path, $file_data)) {
return ['success' => false, 'errors' => ['file' => 'Ошибка сохранения файла']];
}
// Формируем URL
$file_url = self::$base_url . $config['folder'] . '/' . $entity_id . '/' . $final_name;
// Обновляем file_img в базе
$current_files = $entity[$config['field']] ? json_decode($entity[$config['field']], true) : [];
$current_files[] = [
'url' => $file_url,
'name' => $final_name,
'size' => $file_size
];
self::updateEntityFiles($config, $entity_id, $current_files);
return [
'success' => true,
'file' => [
'url' => $file_url,
'name' => $final_name,
'size' => $file_size
]
];
}
// === УДАЛЕНИЕ ФАЙЛОВ ===
public static function delete($entity_type, $entity_id, $file_names) {
// Получаем конфигурацию
$config = self::getConfig($entity_type);
if (!$config) {
return ['success' => false, 'errors' => ['entity' => 'Неизвестный тип сущности']];
}
// Проверяем что сущность существует
$entity = self::getEntityData($config, $entity_id);
if (!$entity) {
return ['success' => false, 'errors' => ['entity' => 'Сущность не найдена']];
}
// Приводим к массиву если передана строка
if (!is_array($file_names)) {
$file_names = [$file_names];
}
// Получаем текущие файлы
$current_files = $entity[$config['field']] ? json_decode($entity[$config['field']], true) : [];
$upload_dir = self::$base_path . $config['folder'] . '/' . $entity_id;
$deleted = [];
// Удаляем каждый файл
foreach ($file_names as $file_name) {
foreach ($current_files as $index => $file) {
if ($file['name'] === $file_name) {
// Удаляем файл с диска
$file_path = $upload_dir . '/' . $file_name;
if (file_exists($file_path)) {
unlink($file_path);
}
// Удаляем из массива
array_splice($current_files, $index, 1);
$deleted[] = $file_name;
break;
}
}
}
// Обновляем в базе
self::updateEntityFiles($config, $entity_id, $current_files);
// Удаляем папку если она пустая
if (is_dir($upload_dir) && count(scandir($upload_dir)) === 2) {
rmdir($upload_dir);
}
return ['success' => true, 'deleted' => $deleted];
}
// === УДАЛЕНИЕ ПАПКИ СУЩНОСТИ (при удалении самой сущности) ===
public static function deleteFolder($entity_type, $entity_id) {
// Получаем конфигурацию
$config = self::getConfig($entity_type);
if (!$config) {
return false;
}
$upload_dir = self::$base_path . $config['folder'] . '/' . $entity_id;
if (is_dir($upload_dir)) {
// Удаляем все файлы в папке
$files = glob($upload_dir . '/*');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
// Удаляем папку
rmdir($upload_dir);
}
return true;
}
}
?>