|
|
|
| Добрый день,
есть задача отправить письмо с вложением,
пытаюсь использовать следующее:
function XMail($from, $to, $subj, $text, $filename) {
$f = fopen($filename,"rb");
$un = strtoupper(uniqid(time()));
$head = "From: $from\n";
$head .= "To: $to\n";
$head .= "Subject: $subj\n";
$head .= "X-Mailer: PHPMail Tool\n";
$head .= "Reply-To: $from\n";
$head .= "Mime-Version: 1.0\n";
$head .= "Content-Type:multipart/mixed;";
$head .= "boundary=\"----------".$un."\"\n\n";
$zag = "------------".$un."\nContent-Type:text/html;\n";
$zag .= "Content-Transfer-Encoding: 8bit\n\n$text\n\n";
$zag .= "------------".$un."\n";
$zag .= "Content-Type: application/octet-stream;";
$zag .= "name=\"".basename($filename)."\"\n";
$zag .= "Content-Transfer-Encoding:base64\n";
$zag .= "Content-Disposition:attachment;";
$zag .= "filename=\"".basename($filename)."\"\n\n";
$zag .= chunk_split(base64_encode(fread($f,filesize($filename))))."\n";
if (!@mail("$to", "$subj", $zag, $head))
return 0;
else
return 1;
}
|
Проблема заключается в том, что файл $filename размером порядка 20 Mb, а ограничение на оперативную память, если я не ошибаюсь, 10 Mb, письмо не отправляется...
Что делать?
Насколько я понимаю письмо можно отправить и напрямую через сокеты, возможно ли в этом случае отправлять файл кусками, т.е. не загружая целиком и есть ли хороший кусок-пример? | |
|
|
| |
|
|
|
|
для: wyfinger
(03.01.2011 в 10:19)
| | >Проблема заключается в том, что файл $filename размером порядка 20 Mb, а ограничение на оперативную память, если я не ошибаюсь, 10 Mb, письмо не отправляется...
>Насколько я понимаю письмо можно отправить и напрямую через сокеты,
Это несовсем равноценные методы.
В частности, все проблемы по корректному оформлению заголовка письма, отправляемого от имени Вашего сервера Вам придется решать самостоятельно.
Кроме того, на хостинге должен быть разрешен fsockopen на 25-й порт.
Это, в целях борьбы со спаммерами, тоже делают не всегда.
Но тем не менее, такой подход имеет право на существование.
>возможно ли в этом случае отправлять файл кусками, т.е. не загружая целиком
Безусловно, так и следует делать.
http://www.faqs.org/rfcs/rfc2821.html | |
|
|
|
|
|
|
|
для: Trianon
(03.01.2011 в 11:25)
| | Написал вот что, должно работать, но не работает. Я подозреваю, что smtp сервер меня временно банит (много подключался при отладке).
Посмотрите пожалуйста:
// Отправление письма с вложением
function SMail($server, $from, $from_pass, $to, $subject, $message, $file_name) {
// Если файл указан, он должен существовать и читаться
if($file_name != "")
if(!file_exists($file_name) || !is_readable($file_name)) {
return -1;
}
$f = fopen($file_name,"r");
$un = strtoupper(uniqid(time()));
$connect = fsockopen ($server, 25, $errno, $errstr, 30);
fputs($connect, "EHLO localhost\r\n");
fputs($connect, "AUTH LOGIN\r\n");
fputs($connect, base64_encode($from)."\r\n");
fputs($connect, base64_encode($from_pass)."\r\n");
fputs($connect, "MAIL FROM:$from\r\n"); // Может быть MAIL FROM:<$from> ????
fputs($connect, "RCPT TO:$to\r\n"); // Может быть RCPT TO::<$to> ????
fputs($connect, "DATA\r\n");
$headers = "Date: ".date("D, j M Y G:i:s")." +0700\r\n";
$headers .= "From: <$from>\r\n";
$headers .= "Reply-To: <$from>\r\n";
$headers .= "X-Priority: 3 (Normal)\r\n";
$headers .= "To: <$to>\r\n";
$headers .= "Subject: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode($subject)))."?=\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed;\r\n";
$headers .= "boundary=\"----------".$un."\"\n\n";
$body = "------------".$un."\nContent-Type:text/html;\r\n";
$body .= "Content-Transfer-Encoding: 8bit\n\n$message\r\n\r\n";
$body .= "------------".$un."\r\n";
$body .= "Content-Type: application/octet-stream; name=\"".basename($file_name)."\"\r\n";
$body .= "Content-Transfer-Encoding:base64\r\n";
$body .= "Content-Disposition:attachment; filename=\"".basename($file_name)."\"\r\n\r\n";
// Отправка текста письма и заголовков
fputs($connect, $headers);
fputs($connect, $body);
// Теперь читаем и отправляем файл кусками с буфером в 50 кб
while($string = fgets($f, 50*1024)) {
fputs($connect, chunk_split(base64_encode($string)));
}
// Завершение
fputs($connect, "\r\n------------".$un."\r\n");
fputs($connect, ".\r\n");
while($str = fgets($connect,515)) // DEBUG ONLY !!!
{ // DEBUG ONLY !!!
echo $str; // DEBUG ONLY !!!
if(substr($str,3,1) == " ") { break; } // DEBUG ONLY !!!
} // DEBUG ONLY !!!
fputs($connect, 'quit'); // Может быть QUIT ????
fsockopen($connect);
fclose($f);
}
|
Вызывать приблизительно так:
SMail("smtp-server", "from@mail.ru", 'from-password', "to-email", "subject", "message-text", 'file-to-attach');
|
| |
|
|
|
|
|
|
|
для: Wyfinger
(04.01.2011 в 06:37)
| |
fputs($connect, "EHLO localhost\r\n");
fputs($connect, "AUTH LOGIN\r\n");
|
Это не дело. Следует не только отправлять команды, но и читать, воспринимать и анализировать отклик. | |
|
|
|
|
|
|
|
для: Trianon
(04.01.2011 в 08:02)
| | Я анализировал во время отладки, до отправки тела сообщения все вроде нормально.
Вообще мне нужно отправить письмо с конкретного ящика на конкретный ящик, особой универсальности не требуется.
Я экспериментирую с mail.ru. | |
|
|
|
|
|
|
|
для: Wyfinger
(04.01.2011 в 13:03)
| | анализировать нужно в процессе диалога.
Отклики, отличные от ожидаемых, писать в лог. | |
|
|
|
|
|
|
|
для: Wyfinger
(04.01.2011 в 06:37)
| | Данная, отладочная версия функции работает:
// Отправление письма с вложением
function SMail($server, $from, $from_pass, $to, $subject, $message, $file_name) {
// Если файл указан, он должен существовать и читаться
if($file_name != "")
if(!file_exists($file_name) || !is_readable($file_name)) {
return "File not found or not readable";
}
$un = strtoupper(uniqid(time()));
$connect = fsockopen ($server, 25, $errno, $errstr, 30);
fputs($connect, "EHLO localhost\r\n");
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fputs($connect, "AUTH LOGIN\r\n");
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fputs($connect, base64_encode($from)."\r\n");
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fputs($connect, base64_encode($from_pass)."\r\n");
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fputs($connect, "MAIL FROM:$from\r\n"); // Может быть MAIL FROM:<$from> ????
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fputs($connect, "RCPT TO:$to\r\n"); // Может быть RCPT TO::<$to> ????
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fputs($connect, "DATA\r\n");
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
$headers = "Date: ".date("D, j M Y G:i:s")." +0700\r\n";
$headers .= "From: <$from>\r\n";
$headers .= "X-Mailer: The Bat! (v3.99.3) Professional\r\n";
$headers .= "Reply-To: <$from>\r\n";
$headers .= "X-Priority: 3 (Normal)\r\n";
$header .= "Message-ID: <".rand().'.'.date("YmjHis")."@mail.ru>\r\n";
$headers .= "To: <$to>\r\n";
$headers .= "Subject: =?utf-8?Q?".str_replace("+","_",str_replace("%","=",urlencode($subject)))."?=\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed;\r\n";
$headers .= " boundary=\"----------".$un."\"\r\n\r\n";
$body = "------------".$un."\nContent-Type:text/html;\r\n";
$body .= "Content-Transfer-Encoding: 8bit\n\n$message\r\n\r\n";
$body .= "------------".$un."\r\n";
$body .= "Content-Type: application/octet-stream; name=\"".basename($file_name)."\"\r\n";
$body .= "Content-Transfer-Encoding:base64\r\n";
$body .= "Content-Disposition:attachment; filename=\"".basename($file_name)."\"\r\n\r\n";
// Отправка текста письма и заголовков
fputs($connect, $headers);
fputs($connect, $body);
// Теперь читаем и отправляем файл кусками с буфером в 50 кб
$file = fopen($file_name,"r");
while($string = fread($file, 1*1024)) {
fwrite($connect, chunk_split(base64_encode($string)));
}
// Завершение
fputs($connect, "\r\n------------".$un."\r\n");
//while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fputs($connect, ".\r\n");
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fputs($connect, 'quit'); // Может быть QUIT ????
while($str=fgets($connect,515)) { echo $str; if(substr($str,3,1)==" ") {break;} } echo "\r\n";
fsockopen($connect);
fclose($f);
}
|
Письмо отправляется, приложение тоже.
Однако обнаружилась проблема с функцией base64_encode().
Дело в том, что
base64_decode(base64_encode("string1").base64_encode("string2")) <> string1string2
|
Может кто-нибудь детально знаком со стандартом base64, это так должно быть и как этого избежать?
(возможно нужно писать свою функцию) | |
|
|
|
|
|
|
|
для: Wyfinger
(05.01.2011 в 13:23)
| | в догонку: есть еще какие-нибудь стандарты кодирования для писем, кроме base64 ? | |
|
|
|
|
|
|
|
для: Wyfinger
(05.01.2011 в 13:25)
| | Quoted-Printable
Но кодировать им двоичные данные не выйдет в принципе, а большие объемы текстов с применением национальных алфавитов хоть и получится, но будет весьма неэффективным решением. | |
|
|
|
|
|
|
|
для: Wyfinger
(05.01.2011 в 13:23)
| | Вот эта функция работает:
// Отправление письма с вложением
function SMail($server, $from, $from_pass, $to, $subject, $message, $file_name) {
// Если файл указан, он должен существовать и читаться
if($file_name != "")
if(!file_exists($file_name) || !is_readable($file_name)) {
return "File not found or not readable";
}
$un = strtoupper(uniqid(time()));
$log = "";
$connect = fsockopen ($server, 25, $errno, $errstr, 30);
fputs($connect, "EHLO localhost\r\n");
while($str=fgets($connect,515)) {if(substr($str,3,1)==" ") {break;} $log .= "\r\n".$str; };
fputs($connect, "AUTH LOGIN\r\n");
while($str=fgets($connect,515)) {if(substr($str,3,1)==" ") {break;} $log .= "\r\n".$str; };
fputs($connect, base64_encode($from)."\r\n");
while($str=fgets($connect,515)) {if(substr($str,3,1)==" ") {break;} $log .= "\r\n".$str; };
fputs($connect, base64_encode($from_pass)."\r\n");
while($str=fgets($connect,515)) {if(substr($str,3,1)==" ") {break;} $log .= "\r\n".$str; };
fputs($connect, "MAIL FROM:$from\r\n"); // Может быть MAIL FROM:<$from> ????
while($str=fgets($connect,515)) {if(substr($str,3,1)==" ") {break;} $log .= "\r\n".$str; };
fputs($connect, "RCPT TO:$to\r\n"); // Может быть RCPT TO::<$to> ????
while($str=fgets($connect,515)) {if(substr($str,3,1)==" ") {break;} $log .= "\r\n".$str; };
fputs($connect, "DATA\r\n");
while($str=fgets($connect,515)) {if(substr($str,3,1)==" ") {break;} $log .= "\r\n".$str; };
$headers = "Date: ".date("D, j M Y G:i:s")." +0700\r\n";
$headers .= "From: <$from>\r\n";
$headers .= "X-Mailer: The Bat! (v3.99.3) Professional\r\n";
$headers .= "Reply-To: <$from>\r\n";
$headers .= "X-Priority: 3 (Normal)\r\n";
$headers .= "Message-ID: <".rand().'.'.date("YmjHis")."@mail.ru>\r\n";
$headers .= "To: <$to>\r\n";
$headers .= "Subject: =?utf-8?Q?".str_replace("+","_",str_replace("%","=",urlencode($subject)))."?=\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed;\r\n";
$headers .= " boundary=\"----------".$un."\"\r\n\r\n";
$body = "------------".$un."\nContent-Type:text/html;\r\n";
$body .= "Content-Transfer-Encoding: 8bit\n\n$message\r\n\r\n";
$body .= "------------".$un."\r\n";
$body .= "Content-Type: application/octet-stream; name=\"".basename($file_name)."\"\r\n";
$body .= "Content-Transfer-Encoding:base64\r\n";
$body .= "Content-Disposition:attachment; filename=\"".basename($file_name)."\"\r\n\r\n";
// Отправка текста письма и заголовков
fputs($connect, $headers);
fputs($connect, $body);
// Теперь читаем и отправляем файл кусками с буфером в 30 кб
// (размер буфера должен быть кратеy 3 из-за специфики base64)
$file = fopen($file_name,"r");
while($string = fread($file, 30*1024)) {
fwrite($connect, chunk_split(base64_encode($string)));
}
// Завершение
fputs($connect, "\r\n------------".$un."\r\n");
while($str=fgets($connect,515)) {if(substr($str,3,1)==" ") {break;} $log .= "\r\n".$str; };
fputs($connect, ".\r\n");
while($str=fgets($connect,515)) {
if(substr($str,3,1)==" ") {break;}
$log .= "\r\n".$str;
if(intval(substr($str,0,3))!=250) return $log; // Ошибка, вернем лог
};
fputs($connect, 'quit'); // Может быть QUIT ????
fsockopen($connect);
fclose($f);
}
|
Проблема была с тем, что буфер должен быть кратен 3 из-за специфики base64. | |
|
|
|
|
|
|
|
для: Wyfinger
(05.01.2011 в 15:28)
| | кратность буфера у Вас задает не мифическая тройка, а [умалчиваемый] параметр функции chunk_split() (и конечно сам алгоритм base64, который 3 байта кодирует четырьмя символами. )
Фактически, буфер у Вас должен быть размером, кратным 58368, ну иль на худой конец - 29184.
А если не сильно жалко - 233472.
Проверяйте.
Кроме того эти дикие циклы с условиями на каждой реплике диалога, которые безусловно вполне оправданы сами по себе, всё же достойны оказаться вынесенными в отдельную функцию.
Неужели не тянет?! | |
|
|
|
|
|
|
|
для: Trianon
(05.01.2011 в 16:12)
| | Поясните откуда цифры, пожалуйста. Не понял.
chunk_split(), режет длинную base64 строку на короткие, разделенные \r\n.
Но для base64 нет символа \r\n, поэтому
base64_decode(chunk_split(base64_encode($string))) = $string
|
(для строки > 76 символов)
Или я где-то не прав? Вышеприведенную функцию проверял для буфера = 30 байт
и файла ~10 кБ. Работает верно.
А по поводу функции - эти циклы меня нисколько не напрягают, да и не важно это.
P.S. С поставленной задачей пока не справился - скрипт говорит, что письмо отправленно, т.е. smtp сервер вернул "успешно", но письма так и нет. Файл всего 20 Мб, а Маил.Ру говорит, что готов принять 30. Загадка... | |
|
|
|
|
|
|
|
для: Wyfinger
(05.01.2011 в 16:57)
| | >Поясните откуда цифры, пожалуйста. Не понял.
>chunk_split(), режет длинную base64 строку на короткие, разделенные \r\n.
на короткие строки, какой именно длины?
Понятно же, что выход должен быть кратен этой длине.
Иначе возникнут обрывки ничем не оправданные.
>(для строки > 76 символов)
Сколько байт исходного потока создаст 76 символов потока сообщения?
Совершенно очевидно, это не единственная кратнось, которую желательно предусмотреть при буферизации.
Какой еще кратный множитель выберем (читайте - Вы выбрали уже)? | |
|
|
|
|
|
|
|
для: Trianon
(05.01.2011 в 17:00)
| | Так ведь совершенно не обязательно, чтобы в закодированном виде все строки были
одинаковой длины. Важно чтобы исходные данные были кратны трем, тогда Base64 не будет выравнивать блок до 3 байтов нулями.
Строки:
3fLuIO/w7uLl8O737e7l
IPHu7uH55e3o5Swg5Ovo
7e3gIOru8u7w7uPuIOHu
6/z45SAyMCDh4Ony
и
3fLuIO/w7uLl8O737e7lIPHu7uH55e3o5Swg5Ovo7e 3gIOru8u7w7uPuIOHu6/z45SAyMCDh4Ony
|
декодируются одинаково. | |
|
|
|
|
|
|
|
для: Wyfinger
(05.01.2011 в 17:34)
| | >Так ведь совершенно не обязательно, чтобы в закодированном виде все строки были
>одинаковой длины.
Почему?
>Важно чтобы исходные данные были кратны трем, тогда Base64 не будет выравнивать блок до 3 байтов нулями.
Это ваше "Важно" важно не более и не менее, чем мое "обязательно" из абзаца выше.
Если Вы заглянете в стандарт, то увидите, что исходные данные вовсе не обязаны быть кратны трем, как и выходные - четырем:
----
The encoded output stream must be represented in lines of no more
than 76 characters each. All line breaks or other characters not
found in Table 1 must be ignored by decoding software.
----
Выходной поток (закодированные байты) должен иметь длину строк не более 76 символов.
Все признаки перевода строки и другие символы, отсутствующие в таблице 1, должны
быть проигнорированы декодером base64. | |
|
|
|
|
|
|
|
для: Trianon
(05.01.2011 в 17:50)
| | Вы извините, я Ваших доводов не понимаю.
Вы приводите факты, подтверждающие лишь то, что кодировать нужно кратное 3 число байт,
а также то, что символы перевода строки (и все остальное) игнорируются при декодировании.
Вкупе это подтверждает мой довод Выше Вашего последнего сообщения о том, что буфер
нужно выбирать кратным трем, а строки длинной менее 76 симавлов, которые могут появиться
в середине закодированного текста (на границах блока) не на что не повлияют.
Почему же:
> Фактически, буфер у Вас должен быть размером, кратным 58368,
> ну иль на худой конец - 29184.
> А если не сильно жалко - 233472.
?
P.S. Мои письма с файлом 20 Мб так и не доходят, хотя с небольшими (1-2) Мб все нормально
и на большие smtp сервер говорит "Ok". Экспериментирую с разными почтовыми сервисами.
Но письма не доходат не из-за обсуждаемого нами сейчас вопроса (размера буфера), поскольку в этом случае теряется аттач, но письмо приходит. | |
|
|
|
|
|
|
|
для: Wyfinger
(06.01.2011 в 01:28)
| | >Вы извините, я Ваших доводов не понимаю.
>Вы приводите факты, подтверждающие лишь то, что кодировать нужно кратное 3 число байт,
>а также то, что символы перевода строки (и все остальное) игнорируются при декодировании.
Если переводы строки все сплошь игнорировать , все кусочки окажутся вытянутыми в одну большую строку.
При этом абсолютно все равно, где были разрывы - через 3 символа, через 33 или через 3000.
Понятно, почему при некратном буфере происходит ошибка - у Вас просто не заполняется битами до конца четверка символов.
Непонятно, почему Вы считаете 3 осмысленной кратностью. Непонятно, но не особо интересно.
76 символов (выбранных функцией chunk_split) это 76/4 * 3 = 57 байт.
Вот эти 57 байт и есть осмысленная кратность.
Другая осмысленная кратность - размер физического сектора устройства - 512 байт (следствие архитектуры устройств внешней памяти)
Третья осмысленная кратность - распространенный размер страницы виртуальной памяти - 4096 байт (следствие процессорной архитектуры ) .
Умножьте одно на другое - получите эти числа.
Дальнейший спор считаю бессмысленным.
Ну а что двадцатиметровые письма не доходят - так может на сервере ограничение действует? | |
|
|
|
|
|
|
|
для: Trianon
(06.01.2011 в 01:40)
| | На счет размера страниц памяти я как-то не думал. Теперь не спорю.
А письма действительно не доходят из-за ограничений хостинга.
Буду переезжать. | |
|
|
|
|
|
|
|
для: Wyfinger
(06.01.2011 в 07:01)
| | Тема письма на кириллице в utf-8 отображается корректно, а тело письма (сообщение) на кириллице выдаёт "кракозябры". Почему??? | |
|
|
|
|