Реализация модуля управления документами в PHP

Раздел: Управление документами на PHP -> Документы

Модуль документов в PHP: подходы и реализация

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

Основное решение: гибрид файловой системы и базы данных

Сочетание хранения файлов на диске и метаданных в таблице БД обеспечивает баланс производительности и управляемости. Файлы сохраняются в структурированном каталоге (например, /uploads/год/месяц/), а в базе записывается путь, имя, размер, тип, дата и владелец.

Реализация загрузки


// Загрузка файла с сохранением метаданных
$uploadDir = __DIR__ . '/uploads/' . date('Y/m');
if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0777, true);
}

$fileName = uniqid() . '_' . basename($_FILES['document']['name']);
$filePath = $uploadDir . '/' . $fileName;

if (move_uploaded_file($_FILES['document']['tmp_name'], $filePath)) {
    $stmt = $pdo->prepare('INSERT INTO documents (filename, filepath, filesize, mime_type, created_at) VALUES (?, ?, ?, ?, NOW())');
    $stmt->execute([
        $_FILES['document']['name'],
        $filePath,
        $_FILES['document']['size'],
        mime_content_type($filePath)
    ]);
    echo 'Document uploaded successfully.';
}

Document php module (модуль документов в php)

Выдача файла пользователю


// Скачивание документа по ID
$id = (int)$_GET['id'];
$stmt = $pdo->prepare('SELECT * FROM documents WHERE id = ?');
$stmt->execute([$id]);
$doc = $stmt->fetch();

if ($doc && file_exists($doc['filepath'])) {
    header('Content-Type: ' . $doc['mime_type']);
    header('Content-Disposition: attachment; filename="' . $doc['filename'] . '"');
    header('Content-Length: ' . $doc['filesize']);
    readfile($doc['filepath']);
    exit;
}

Типичные ошибки и их решения:

  • Ошибка прав доступа на каталог uploads. Решение: установить chmod 0755 или 0777 для каталога и убедиться, что веб-сервер является владельцем.
  • Загрузка файлов большого размера обрывается. Решение: увеличить лимиты upload_max_filesize и post_max_size в php.ini.
  • Конфликт имён файлов. Решение: использовать уникальный префикс (uniqid, md5 времени и т.п.).

Цель: обеспечить удобное администрирование, возможность поиска по метаданным, защиту от дублирования имён и быстрый доступ к файлам.

Вариант 1: Простая загрузка без базы данных

Вопрос: Как организовать загрузку документов, когда требуется только временное хранение или небольшой проект?

Файлы сохраняются в каталог, информация о них хранится в сессии или не хранится вовсе. Пример:


$targetDir = 'uploads/';
$targetFile = $targetDir . basename($_FILES['file']['name']);
move_uploaded_file($_FILES['file']['tmp_name'], $targetFile);
echo 'File saved: ' . $targetFile;

Проблемы: дублирование имён, отсутствие контроля версий, невозможность поиска. Решается только для одноразовых задач.

Вариант 2: Хранение файлов в базе данных (BLOB)

Вопрос: Когда необходимо обеспечить бэкап и переносимость данных вместе с файлами?

Файлы сохраняются в BLOB-поле таблицы. Это удобно для небольших файлов (до нескольких мегабайт), но серьёзно нагружает БД.


// Сохранение в BLOB
$data = file_get_contents($_FILES['file']['tmp_name']);
$stmt = $pdo->prepare('INSERT INTO documents_blob (filename, filedata, mime_type) VALUES (?, ?, ?)');
$stmt->execute([$_FILES['file']['name'], $data, $_FILES['file']['type']]);

// Выдача
$stmt = $pdo->prepare('SELECT * FROM documents_blob WHERE id = ?');
$stmt->execute([$id]);
$row = $stmt->fetch();
header('Content-Type: ' . $row['mime_type']);
echo $row['filedata'];

Проблемы: снижение производительности при росте размера файлов, сложность фрагментации БД, невозможность прямого доступа через веб-сервер. Рекомендуется только для файлов до 1-2 МБ.

Вариант 3: Использование сторонних библиотек для генерации документов

Вопрос: Как программно создавать PDF, Excel или Word документы?

Библиотеки TCPDF (PDF), PhpSpreadsheet (Excel) и PhpWord (Word) позволяют генерировать документы с данными из БД.


// Пример с TCPDF
require_once 'tcpdf/tcpdf.php';
$pdf = new TCPDF();
$pdf->AddPage();
$pdf->SetFont('dejavusans', '', 14);
$pdf->Write(0, 'Привет, мир документов!');
$pdf->Output('generated.pdf', 'D');

Проблемы: необходимость установки библиотек через Composer, потребление памяти при больших объёмах данных. Решение: генерировать частями или использовать потоковую запись.

Вариант 4: Интеграция с облачным хранилищем (Amazon S3, Google Drive)

Вопрос: Как масштабировать хранение документов для высоконагруженного проекта?

Использование API облачных сервисов снимает нагрузку с собственного сервера. Пример для AWS S3 через SDK:


use Aws\S3\S3Client;

$s3 = new S3Client([
    'version' => 'latest',
    'region' => 'us-east-1',
    'credentials' => [
        'key' => 'YOUR_KEY',
        'secret' => 'YOUR_SECRET'
    ]
]);

$result = $s3->putObject([
    'Bucket' => 'my-bucket',
    'Key' => 'documents/' . $_FILES['file']['name'],
    'Body' => fopen($_FILES['file']['tmp_name'], 'rb'),
    'ACL' => 'private'
]);

Проблемы: зависимость от внешнего провайдера, стоимость хранения и передачи, необходимость управления ключами доступа. Решение: использовать кэширование и подписанные URL для временного доступа.

Расширенные примеры работы с документами

1. Генерация многостраничного PDF с таблицей

Пример

require_once 'tcpdf/tcpdf.php';

$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false);
$pdf->SetCreator('MyApp');
$pdf->AddPage();

// Заголовок
$pdf->SetFont('dejavusans', 'B', 16);
$pdf->Cell(0, 10, 'Отчет по документам', 0, 1, 'C');

// Таблица
$pdf->SetFont('dejavusans', '', 10);
$header = array('ID', 'Имя', 'Дата');
$data = array(
    array(1, 'Договор.pdf', '2024-01-15'),
    array(2, 'Счёт.xlsx', '2024-02-20'),
);

$w = array(20, 100, 70);
for($i = 0; $i < count($header); $i++) {
    $pdf->Cell($w[$i], 7, $header[$i], 1, 0, 'C');
}
$pdf->Ln();
foreach ($data as $row) {
    foreach ($row as $j => $cell) {
        $pdf->Cell($w[$j], 6, $cell, 1);
    }
    $pdf->Ln();
}

$pdf->Output('report.pdf', 'I');
Результат: PDF-документ с таблицей, отображаемый в браузере (inline).

2. Импорт данных из Excel с помощью PhpSpreadsheet

Пример

use PhpOffice\PhpSpreadsheet\IOFactory;

$inputFileName = 'uploaded_contracts.xlsx';
$spreadsheet = IOFactory::load($inputFileName);
$worksheet = $spreadsheet->getActiveSheet();
$highestRow = $worksheet->getHighestRow();

$stmt = $pdo->prepare('INSERT INTO imported_docs (contract_id, client, amount) VALUES (?, ?, ?)');

for ($row = 2; $row <= $highestRow; $row++) { // пропускаем заголовок
    $contractId = $worksheet->getCell('A' . $row)->getValue();
    $client     = $worksheet->getCell('B' . $row)->getValue();
    $amount     = $worksheet->getCell('C' . $row)->getValue();
    $stmt->execute([$contractId, $client, $amount]);
}
echo 'Импортировано записей: ' . ($highestRow - 1);
Результат: данные из Excel перенесены в базу данных.

3. Многопоточная загрузка с проверкой безопасности

Пример

// Фильтрация типа файла и проверка на вирусы через ClamAV
$allowedMime = ['application/pdf', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);

if (!in_array($mime, $allowedMime)) {
    die('Недопустимый тип файла.');
}

// Дополнительная проверка через ClamAV (требуется расширение php-clamav)
if (function_exists('clamav_scan_file')) {
    $result = clamav_scan_file($_FILES['file']['tmp_name']);
    if ($result !== false) {
        die('Обнаружен вирус.');
    }
}
// Далее стандартное сохранение
Результат: загружаются только безопасные файлы разрешённых форматов.

4. Создание архива ZIP из нескольких документов

Пример

$zip = new ZipArchive();
$zipName = 'export_' . time() . '.zip';
if ($zip->open($zipName, ZipArchive::CREATE) === true) {
    // Добавляем файлы из БД
    $stmt = $pdo->query('SELECT filepath, filename FROM documents WHERE id IN (1,2,3)');
    while ($doc = $stmt->fetch()) {
        $zip->addFile($doc['filepath'], $doc['filename']);
    }
    $zip->close();
    
    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="' . $zipName . '"');
    readfile($zipName);
    unlink($zipName);
    exit;
}
Результат: пользователь скачивает архив ZIP, содержащий выбранные файлы.

5. Обработка изображений (поворот, ресайз) с помощью GD

Пример

$source = imagecreatefromjpeg('document_image.jpg');
// Поворот на 90 градусов
$rotated = imagerotate($source, 90, 0);
// Изменение размера до 800x600
$resized = imagescale($rotated, 800, 600);
imagejpeg($resized, 'processed_image.jpg', 90);
imagedestroy($source);
imagedestroy($rotated);
imagedestroy($resized);
Результат: файл processed_image.jpg с поворотом и новым размером.

Модуль документов в PHP - comments

En
Document php module (php)