Вставка файлов в электронные сообщения средствами PHP
Обзор методов прикрепления файлов в PHP
Какой инструмент обеспечивает максимальную надежность и простоту для вложений?
Библиотека PHPMailer. Она абстрагирует работу с MIME, поддерживает множество транспортов и кодировок, а также предоставляет удобные методы для добавления вложений из файловой системы или строки.
// Установка: composer require phpmailer/phpmailer
require 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true);
try {
$mail->setFrom('from@example.com', 'Отправитель');
$mail->addAddress('to@example.com', 'Получатель');
$mail->Subject = 'Письмо с файлом';
$mail->addAttachment('/var/www/files/report.pdf', 'отчет.pdf');
$mail->Body = 'Во вложении отчет.';
$mail->send();
echo 'Письмо отправлено';
} catch (Exception $e) {
echo "Ошибка: {$mail->ErrorInfo}";
}
Типичные неполадки при использовании PHPMailer
Файл не найден. Если addAttachment указывает на несуществующий путь, возникает исключение. Решение: проверять наличие файла через file_exists() и использовать абсолютный путь.
Письмо уходит без вложения. Часто причина в том, что файл заблокирован или нет прав на чтение. Стоит задать корректные разрешения (например, 0644).
Цель использования:
PHPMailer подходит для проектов, где требуется стабильная отправка писем с вложениями любого типа и размера (в пределах лимитов почтового сервера).Как отправить вложение, используя только встроенную функцию mail()?
Можно вручную сформировать multipart/mixed письмо. Этот подход не требует внешних зависимостей, но требует внимательности при построении заголовков и кодировке.
$boundary = md5(uniqid(time()));
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed; boundary=\"{$boundary}\"\r\n";
$headers .= "From: from@example.com\r\n";
$message = "Это многочастное сообщение.\r\n\r\n";
$message .= "--{$boundary}\r\n";
$message .= "Content-Type: text/plain; charset=utf-8\r\n\r\n";
$message .= "Текст самого письма.\r\n\r\n";
$file = '/var/www/files/image.jpg';
$filename = basename($file);
$content = chunk_split(base64_encode(file_get_contents($file)));
$message .= "--{$boundary}\r\n";
$message .= "Content-Type: image/jpeg; name=\"{$filename}\"\r\n";
$message .= "Content-Disposition: attachment; filename=\"{$filename}\"\r\n";
$message .= "Content-Transfer-Encoding: base64\r\n\r\n";
$message .= $content . "\r\n\r\n";
$message .= "--{$boundary}--";
mail('to@example.com', 'Тема', $message, $headers);
Проблемы ручного формирования
Кириллическая тема. Функция mail() не кодирует тему автоматически. Используйте mb_encode_mimeheader(): $subject = mb_encode_mimeheader('Тема письма', 'UTF-8');
Большие файлы. При размере более 5-10 МБ почтовый сервер может отклонить письмо. Рекомендуется не превышать 3 МБ для стандартных хостингов.
Случаи применения:
когда недоступен Composer или требуется легковесное решение для простых вложений (небольшие PDF, изображения).Какие альтернативные библиотеки для вложений существуют?
SwiftMailer (сейчас поддерживается как Symfony Mailer) также предоставляет удобные методы для вложений. Пример для SwiftMailer 6.x:
require 'vendor/autoload.php';
$transport = (new Swift_SmtpTransport('smtp.example.com', 587, 'tls'))
->setUsername('user')
->setPassword('pass');
$mailer = new Swift_Mailer($transport);
$message = (new Swift_Message('Письмо с вложением'))
->setFrom(['from@example.com' => 'Sender'])
->setTo(['to@example.com'])
->setBody('Текст письма')
->attach(Swift_Attachment::fromPath('/path/to/file.pdf'));
$result = $mailer->send($message);
Устаревание. SwiftMailer больше не обновляется. Для новых проектов рекомендуется Symfony Mailer или PHPMailer.
Когда использовать:
для проектов, уже использующих Symfony или Laravel, где интеграция с Mailer естественна.Дополнительные примеры и сценарии
1. Множественные вложения и вложение из строки в PHPMailer
require 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true);
try {
$mail->setFrom('from@example.com', 'Sender');
$mail->addAddress('to@example.com');
$mail->Subject = 'Несколько вложений';
// Файл из файловой системы
$mail->addAttachment('/files/report.pdf', 'report.pdf');
// Вложение из строки (например, сгенерированный PDF)
$pdfContent = file_get_contents('/files/invoice.pdf');
$mail->addStringAttachment($pdfContent, 'invoice.pdf', 'base64', 'application/pdf');
$mail->Body = 'Проверьте вложения.';
$mail->send();
} catch (Exception $e) {
echo $mail->ErrorInfo;
}
Письмо отправлено (истина)
2. Отправка через SMTP с шифрованием и авторизацией (PHPMailer)
require 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = 'user@gmail.com';
$mail->Password = 'app_password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('user@gmail.com', 'Gmail User');
$mail->addAddress('recipient@example.com');
$mail->Subject = 'SMTP тест с вложением';
$mail->addAttachment('/tmp/file.txt', 'file.txt');
$mail->Body = 'Это письмо отправлено через Gmail SMTP.';
if ($mail->send()) {
echo 'Успешно';
} else {
echo $mail->ErrorInfo;
}
Успешно
3. Ручное формирование письма с поддержкой кириллицы (mail())
$boundary = md5(uniqid(time()));
$subject = mb_encode_mimeheader('Вложение с русским именем', 'UTF-8');
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed; boundary=\"{$boundary}\"\r\n";
$headers .= "From: from@example.com\r\n";
$textPart = "--{$boundary}\r\n";
$textPart .= "Content-Type: text/plain; charset=utf-8\r\n\r\n";
$textPart .= "Текст письма.\r\n\r\n";
$file = '/var/www/images/фото.jpg';
$filename = mb_encode_mimeheader(basename($file), 'UTF-8');
$content = chunk_split(base64_encode(file_get_contents($file)));
$attachPart = "--{$boundary}\r\n";
$attachPart .= "Content-Type: image/jpeg; name=\"{$filename}\"\r\n";
$attachPart .= "Content-Disposition: attachment; filename=\"{$filename}\"\r\n";
$attachPart .= "Content-Transfer-Encoding: base64\r\n\r\n";
$attachPart .= $content . "\r\n\r\n";
$attachPart .= "--{$boundary}--";
$message = $textPart . $attachPart;
mail('to@example.com', $subject, $message, $headers);
Письмо отправлено (true)
4. Проверка существования файла перед добавлением и обработка исключений
function safeAttach(PHPMailer $mail, string $filePath, string $displayName = ''): bool {
if (!file_exists($filePath)) {
throw new Exception("Файл не найден: {$filePath}");
}
if (!is_readable($filePath)) {
throw new Exception("Нет прав на чтение: {$filePath}");
}
$mail->addAttachment($filePath, $displayName);
return true;
}
$mail = new PHPMailer(true);
try {
$mail->setFrom('from@example.com');
$mail->addAddress('to@example.com');
$mail->Subject = 'Тест';
safeAttach($mail, '/tmp/data.xlsx', 'data.xlsx');
$mail->send();
} catch (Exception $e) {
echo 'Ошибка: ' . $e->getMessage();
}
Ошибка: Файл не найден: /tmp/data.xlsx