Javascript-форум (https://javascript.ru/forum/)
-   jQuery (https://javascript.ru/forum/jquery/)
-   -   Отправка письма с вложением через formData (https://javascript.ru/forum/jquery/70874-otpravka-pisma-s-vlozheniem-cherez-formdata.html)

KateU 09.10.2017 10:37

Отправка письма с вложением через formData
 
Здравствуйте.
Помогите, пожалуйста, с отправкой письма с вложением. Пытаюсь сделать через formData. Сама отправка срабатывает нормально, все отправляется, письмо доходит. Но вместо того, чтобы после нажатия на кнопку "отправить" появлялось сообщение об успешной отправке, страница перезагружается.
Вот код:
Форма отправки:
<form action method="post" name="calcprojectform1" id="calcprojectform1" enctype='multipart/form-data'>
                            <input class="pole validate[required]" data-prompt-position="Left:-65,35" name="name" type="text" placeholder="Имя" /><br><br>
                            <input class="pole validate[required]" data-prompt-position="Left:-65,35" name="phone" type="text" placeholder="Телефон" /><br><br>
                            <input class="pole validate[required,custom[email]]" data-prompt-position="Left:-65,35" name="mail" type="text" placeholder="Ваш e-mail" /><br><br>
                            <input class="pole" type='file' name='mail_file' data-prompt-position="Left:-65,35"/><br><br>
                            <input class="mainButton_form" name="calc-project-submit1" id="calc-project-submit1" type="submit" value="получить">
                        </form>


Скрипт обработки
$(document).on('click', 'input[name="calc-project-submit1"]', function () {
    if ($('form[name="calcprojectform1"]').validationEngine('validate'))
    {
var form = document.forms.namedItem("calcprojectform1");
        var formData = new FormData(form);
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "mail.php");

        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status == 200) {
                    data = xhr.responseText;
                    if (data == '1') {
                        $("#calcprojectform1").reset();
                        $("#calcprojectform1").replaceWith('<div style="padding:15px;background:#fff; font-size:14px;font-family:MuseoSansCyrl_700; color:green;">Спасибо, ваша заявка успешно отправлена.</div>');
style="padding:15px;background:#fff; font-size:14px;font-family:MuseoSansCyrl_700; color:green;">Спасибо, ваша заявка успешно отправлена.</div>')
                    } else {
                        $('#calc-project-submit1').validationEngine('showPrompt', 'Ошибка отправки данных. Попробуйте позже.');
                    }
                }
            }
        };
        xhr.send(formData);
    }
});


Файл отправки php
<?php

$mailto = "test@yandex.ru";
$charset = "utf-8";
$subject = "Заполнили форму Рассчитать проект";
$content = "text/html";
$status = "<br>";
if (!empty($_POST)) {
    $name = htmlspecialchars(stripslashes($_POST['name']));
    $email = htmlspecialchars(stripslashes($_POST['mail']));
    $phone = htmlspecialchars(stripslashes($_POST['phone']));
    $picture = "";
    $headers = "MIME-Version: 1.0\r\n";
    $headers .= "From: \"" . $name . "\" <test@yandex.ru>\r\n";
    $headers .= "Bcc: test@yandex.ru\r\n";
    $headers .= "X-Mailer: E-mail from my site \r\n";
    $sendmessage = "<html><body>
<p><b>Имя:</b> " . $name . "</p>
<p><b>Телефон:</b> " . $phone . "</p>
<p><b>E-mail:</b> " . $email . "</p>
</body></html>";

    if (!empty($_FILES['mail_file']['tmp_name'])) {
        $path = $_FILES['mail_file']['tmp_name'];
        $picture = $_FILES['mail_file']['name'];
    }
    $boundary = "--" . md5(uniqid(time()));
    $headers .="Content-Type: multipart/mixed; boundary=\"" . $boundary . "\"\n";
    $multipart .= "--" . $boundary . "\n";
    $multipart .= "Content-Type: text/html; charset=$charset\n";
    $multipart .= "Content-Transfer-Encoding: Quot-Printed\n\n";
    $multipart .= "$sendmessage\n\n";

    $message_part = '';
    $fp = fopen($path, "r");
    if (!$fp) {
        print "Файл " . $path . " не может быть прочитан";
        exit();
    }
    $file = fread($fp, filesize($path));
    fclose($fp);
    $message_part .= "--" . $boundary . "\n";
    $message_part .= "Content-Type: application/octet-stream\n";
    $message_part .= "Content-Transfer-Encoding: base64\n";
    $message_part .= "Content-Disposition: attachment; filename = \"" . $picture . "\"\n\n";
    $message_part .= chunk_split(base64_encode($file)) . "\n";

    $multipart .= $message_part . "--" . $boundary . "--\n";
    
    if (mail($mailto, $subject, $multipart, $headers)) {
       echo '1'; 
    } else {
        echo '2';
    }
}
?>

Skipp 09.10.2017 11:49

Всё правильно, при нажатии на submit происходит стандартная отправка формы. Можно конечно click тормознуть, но в таком случаи с enter тоже отправиться
$(document).on('submit', '#calcprojectform1', function (e) {
    if ($('form[name="calcprojectform1"]').validationEngine('validate'))
    {
var form = document.forms.namedItem("calcprojectform1");
        var formData = new FormData(form);
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "mail.php");

        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status == 200) {
                    data = xhr.responseText;
                    if (data == '1') {
                        $("#calcprojectform1").reset();
                        $("#calcprojectform1").replaceWith('<div style="padding:15px;background:#fff; font-size:14px;font-family:MuseoSansCyrl_700; color:green;">Спасибо, ваша заявка успешно отправлена.</div>');
style="padding:15px;background:#fff; font-size:14px;font-family:MuseoSansCyrl_700; color:green;">Спасибо, ваша заявка успешно отправлена.</div>')
                    } else {
                        $('#calc-project-submit1').validationEngine('showPrompt', 'Ошибка отправки данных. Попробуйте позже.');
                    }
                }
            }
        };
        xhr.send(formData);
    }
	e.preventDefault();
	return false;
});

KateU 09.10.2017 12:17

Skipp, спасибо. Да, так сообщение отправляется и страница не перезагружается. Но сообщение о том, что форма отправлена не выскакивает. То есть, видимо, не срабатывает вот этот код:
if (data == '1') {
                        $("#calcprojectform1").reset();
                        $("#calcprojectform1").replaceWith('<div style="padding:15px;background:#fff; font-size:14px;font-family:MuseoSansCyrl_700; color:green;">Спасибо, ваша заявка успешно отправлена.</div>');
                    } else {
                        $('#calc-project-submit1').validationEngine('showPrompt', 'Ошибка отправки данных. Попробуйте позже.');
                    }


Или может его перенести в конец функции?

laimas 09.10.2017 12:25

Цитата:

Сообщение от KateU
То есть, видимо, не срабатывает вот этот код

Это потому, что вы не то событие обрабатываете и не блокируете действие по умолчанию.

А вот данные извне, а уж тем более при их хранении и использовании как параметров, а также отправляемые почтой обязательно подлежать проверке. Тему почтового отправления и имя в From нужно кодировать.

KateU 09.10.2017 12:28

Да, и отправка срабатывает только когда
$(document).on('click', '#calcprojectform1', function (e) {

Если писать
$(document).on('submit', '#calcprojectform1', function (e) {

то отправка не происходит, и страница перезагружается.

KateU 09.10.2017 12:36

laimas,
эм... можно подробнее? Какое тогда событие нужно обрабатывать?
Делала по аналогии, как здесь: https://javascript.ru/forum/jquery/4...v-s-formy.html

laimas 09.10.2017 12:39

Цитата:

Сообщение от KateU
.on('submit', '#calcprojectform1'

Это делегирование обработчика, у вас что форма динамически на страницу добавляется?

Цитата:

Сообщение от KateU
Да, и отправка срабатывает только когда

Не правильно вы бутерброд кушаете, дядя Федор, именно onsubmit и надо обрабатывать, и все будет работать.

KateU 09.10.2017 12:52

Нет, форма динамически не добавляется, просто показывается только по клику на кнопку.

Да, действительно срабатывает на submit. Это я вызывала событие клика на кнопку, а нужно submit формы. Теперь все отправляется, страница не перезагружается, но сообщение об отправке все равно не появляется.

laimas 09.10.2017 13:06

Цитата:

Сообщение от KateU
Нет, форма динамически не добавляется

Тогда зачем делегирование? Так и устанавливайте обработчик

$(function() {
    $('#calcprojectform1').on('submit', function(e){
        e.preventDefault(); //отменяем действие по умолчанию - отправление формы
        //и отправляем ее посредством Ajax, используя при этом jQuery  
        $.ajax({
            url: this.action, //url запроса можно брать из формы, таким образом можно использовать этот обработчик для многих форм
            type: this.method, //метод запроса также берем из формы
            contentType: false, //обязательно
            processData: false, //обязательно
            data: new FormData(this), //получаем данные формы
            dataType: 'здесь указать тип ожидаемых данных ответа сервера или убрать параметр передавая тип сервером',
            success: function(data){
                    //ответ сервера
                    alert(data);
            }
        });
    });
});

KateU 09.10.2017 21:19

laimas,
Спасибо.
Сделала, как вы пишете, но обработка все равно до конца не срабатывает. Сообщение отправляется, страница не перезагружается, но сообщение об отправке не выводится.
Чего-то даже alert работать перестал...

Делаю сейчас так (на отправке через ajax без файлов работает):
В файле отправки php в конце:
if (mail($mailto, $subject, $multipart, $headers)) {
        $message_ok = array("ok" => 1, "success_text" => "y");
        print json_encode($message_ok);
    } else {
        $err = array("ok" => 2, "error_text" => "n");
        print json_encode($err);
}

В js:
$('#calcprojectform1').on('submit', function (e) {
            e.preventDefault(); //отменяем действие по умолчанию - отправление формы
            //и отправляем ее посредством Ajax, используя при этом jQuery  
            $.ajax({
                url: this.action, //url запроса можно брать из формы, таким образом можно использовать этот обработчик для многих форм
                type: this.method, //метод запроса также берем из формы
                contentType: false, //обязательно
                processData: false, //обязательно
                data: new FormData(this), //получаем данные формы
                dataType: 'json',
                success: function (data) {
                    if (data.ok == '1') {
                        $("#calcprojectform1").reset();
                        $("#calcprojectform1").replaceWith('<div style="padding:15px;background:#fff; font-size:14px;font-family:MuseoSansCyrl_700; color:green;">Спасибо, ваша заявка успешно отправлена.</div>');
                    }
                    if (data.ok == '2') {
                        $('#calc-project-submit1').validationEngine('showPrompt', 'Ошибка отправки данных. Попробуйте позже.');
                    }
                    alert(data);
                    alert(data.ok);
                }
            });
        });


В чем еще может быть дело? Пробовала передавать и число, и boolean. Результат тот же. Где я туплю?

laimas 09.10.2017 21:43

1) Зачем именно так проверять - data.ok == '1'? Функции json_encode задайте опцию JSON_NUMERIC_CHECK и числа будут как числа - json_encode($message_ok, JSON_NUMERIC_CHECK);

2) print json_encode($message_ok); - такое допустимо только в том случае если это и есть завершение кода, иначе может быть не контролируемый вывод в браузер (например после закрывающего тега РНР есть пробелы, переводы строк, ...), что породит ошибку и вы ничего не получите в success. Ошибки либо в другом методе Ajax проверять, либо в success в соответствующем аргументе функции.

Уж коли это завершение работы, то лучше так:

$message = mail($mailto, $subject, $multipart, $headers) ? ["ok" => 1, "text" => "Все Ок"] : ["ok" => 2, "text" => "Увы, ошибка"];
       exit(json_encode($message, JSON_NUMERIC_CHECK));


Учтите, что mail возвращает информацию всего лишь об успешной или нет передаче отправления в sendmail. Эта функция не проверяет ошибок, ни того что почта отправлена. Ошибки могут прийти на сервер потом, после реального отправления почты.

KateU 10.10.2017 08:21

Большое спасибо! Все получилось!
Сделала так. Концовка php файла отправки:
if (!mail($mailto, $subject, $multipart, $headers)) {
        $err = array("ok" => 2, "message_text" => "n");
        exit(json_encode($err, JSON_NUMERIC_CHECK));
    } else {
        $message = array("ok" => 1, "message_text" => "y");
        exit(json_encode($message, JSON_NUMERIC_CHECK));
    }

Скрипт:
e.preventDefault(); //отменяем действие по умолчанию - отправление формы
        //и отправляем ее посредством Ajax, используя при этом jQuery 
        $.ajax({
            url: this.action, //url запроса можно брать из формы, таким образом можно использовать этот обработчик для многих форм
            type: this.method, //метод запроса также берем из формы
            contentType: false, //обязательно
            processData: false, //обязательно
            data: new FormData(this), //получаем данные формы
            dataType: 'json',
            success: function (data) {
                if (data.ok == 1) {
                    $('form[name="calcprojectform1"]')[0].reset();
                    $('form[name="calcprojectform1"]').replaceWith('<div style="padding:15px;background:#fff; font-size:14px;font-family:MuseoSansCyrl_700; color:green;">Спасибо, ваш вопрос успешно отправлен.</div>')
                }
                if (data.ok == 2) {
                    $('#calc-project-submit1').validationEngine('showPrompt', 'Ошибка отправки данных. Попробуйте позже.');
                }
            }
        });

Все работает! Даже не вериться...

laimas 10.10.2017 10:06

Цитата:

Сообщение от KateU
Даже не вериться...

Да нет ничего сверх естественного в этом, наоборот, это закономерность. :)
Обмен с сервером происходит в формате JSON и если до его или после его передачи будет любой вывод в браузер, то на клиенте в итоге получится невалидный json. jQuey в этом случае конечно же генерирует ошибку, но она передается не первым аргументом функции в success. Первый аргумент этой функции, это данные возвращенные сервером и в случае json, это будет объект - результат его декодирования. Но при ошибке этот аргумент будет пуст. Вы же не проверяете ошибок, видите что ничего нет, и вам и кажется, что сервер не отвечает, хотя на самом деле ответ есть (его можно и в отладчике браузера увидеть).

Проверить можно было бы и так - dataType: 'text' на время проверки, и вы увидите json-строку, но по ней вряд ли можно определить валидная ли она если "мусор" передающийся с ней, это непечатные символы: пробелы, переносы строк, ВОМ и т.п.

И зачем дважды прописывать операции - exit(json_encode... Определили по условиям данные, а затем вывод их один раз прописанный. В данном случае тернарный оператор более лаконичен, хотя это и не важно.

Если скрипт пишется под версию 5.4 и выше, то можно меньше нагружать пальцы свои, объявляя массивы точно также как это можно и в JS - ["ok" => 2, "message_text" => "n"]; вместо array("ok" => 2, "message_text" => "n");

А это просто замечание:

Один бит может иметь два состояния, двумя битами можно описать четыре состояния. То есть значением одной переменной "ok" можно описать два условия, а двумя - "ok" и "message_text" четыре. У вас же переменная "ok" уже сообщает клиенту результат выполнения, зачем при этом нужен "message_text", который в любом случае имеет одно и тоже значение?

$('#calc-project-submit1').validationEngine('showPrompt', 'Ошибка отправки данных. Попробуйте позже.'); - сподручнее это текст как раз и определять серверу причем не в скриптах обработчиках, а как определенные переменные сообщений, значений и т.п., дабы и в клиентских скриптах не оперировать строками, которые в случае их замены придется искать и править. И это как раз и можно было бы определять как ["ok" => 2, "message_text" => $error];, а в случае удачи ее просто не будет - не нужна, или же есть но сообщает о "Спасибо, ваш вопрос успешно отправлен" - ["ok" => 1, "message_text" => $message_mail];.

laimas 10.10.2017 10:49

Вот это - $('form[name="calcprojectform1"]')... тоже лишнее так как форма в обработчике изначально уже " в ваших руках", это this. Чтобы ее оперировать в последствии ее нужно просто "запомнить". То есть:

$('#calcprojectform1').on('submit', function (e) {
    e.preventDefault();
    var f = this; //это форма
    //далее этой переменной оперировать
    url: f.action,
    type: f.method,
    //......
    //и в результате
    f.reset(); //правда не понятно для чего, если далее она заменяется элементом DIV
    $(f).replaceWith(....


Но если с прицелом на "таким образом можно использовать этот обработчик для многих форм", то не должно быть $('#calcprojectform1'). Формы должны иметь общий для всех идентификатор, отправление которых будет обрабатываться этим обработчиком. Таким идентификатором может служить общее имя класса или часть имени класса/id.

Но и этого мало - должен быть единый "определенный протокол/логика" обмена данных между клиентом и сервером. Например, если формат всегда json (что не обязательно, и выгоднее не указывать его в dataType: 'json', а определять сервером через заголовки, клиент же на основе типа данных может поступать так или иначе), поместить некие данные в какой либо элемент на странице и выполнить некие функции сервером можно задать так:

['insert' =>['box1' => 'Text', 'box2' => '<p>HTML</p>'], 'exe' => ['fun1' => [1, 2], 'fun2' => 'abc']]

По приему такого json клиент определяет наличие в нем ключа 'insert' и если есть вставляет данные в элементы с id 'box1', 'box2', а также при наличии ключа 'exe' выполняет функции 'fun1' и 'fun2' с указанными аргументами, и которые прописаны например в каком либо объекте.

Это на заметку - так как ответ сервера не обязательно будет быстрым, на время запроса кнопку submit нужно делать недоступной, и вновь доступной после ответа сервера.

laimas 10.10.2017 11:10

Ну и самое главное - ваш скрипт почтового отправления не проверяет данные, а это очень плохо. Если вы рассчитываете на validationEngine(), то зря, это сервис для клиента чтобы не делать лишних запросов сервера. Но эта проверка рассчитана на "хороших парней", а для ботов это игрушка. Если сервер будет полагаться на проверку клиентом, та ваша почта становится потенциальной дырой для отправления через нее чего угодно и кому-угодно.

KateU 10.10.2017 14:12

Спасибо большое. Теперь все это нужно осознать :blink: и упростить код.
А что вы имеете в виду под проверкой данных? На какие условия рекомендуете проверять?

Nexus 10.10.2017 14:23

KateU, нужно проверять удовлетворяют ли полученные от пользователя данные вашим ожиданиям или нет.
Например если вы ожидаете от пользователя его возраст, то нужно удалить из полученных данных все, кроме цифр.
Если ожидаете строку без спец. символов, то нужно удалить все спец. символы из полученных данных.
Если после обработки вы собираетесь записать данные в бд, то нужно произвести экранизацию некоторого кол-ва символов (применения «mysqli_escape_string» обычно хватает).

laimas 10.10.2017 14:37

Цитата:

Сообщение от KateU
На какие условия рекомендуете проверять?

Например - я возьму и вот в это $_POST['mail'] вставлю множество адресов, и если ваш скрипт меня "обрадует" тем, что письмо успешно отправлено, то единственный вывод, который я сделаю, это "проверкой данных извне на сервере даже не пахнет". Думаю не трудно догадаться каким образом я использую отправку почты от имени вашего домена. Кстати, по причине "дырявости кода" с использованием функции mail, который к сожалению не редкость (только на этом форуме таких дырявых кодов показано не мало), хостеры запрещают использовать функцию mail, предлагая SMTP серверы таких гигантов как Гугл, Яндекс и т.д.

Ну а если "ради шутки", то могу вам слать всякий мусор, ваш же код добросовестно его будет принимать и отдавать sendmail. Ну разве же это почта? Порт 25, это тот порт, за которым ваш хостер следит, и если из-за этого порта у него появятся проблемы, не ждите ничего хорошего от хостера в свой адрес.

Есть готовые классы грамотно оформляющие почтовые отправления, тот же phpmailer, можете использовать его или же если сами, то более ответственно относитесь к данным извне.


Часовой пояс GMT +3, время: 06:30.