Как отправить email из программы java
Перейти к содержимому

Как отправить email из программы java

Электронная почта и работа с ней в Java-приложениях

Disclaimer
Статья написана для новичков и тех, кому хочется шаг за шагом понять как устроена работа с электронной почтой из Java-приложений. Желающие быстро понять как отправлять электронные письма из Spring-приложений могут сразу переходить к 3 части.

Эту статью я решил написать, потому что не нашел русскоязычных источников про работу c электронной почтой из Java, описывающих имеющиеся библиотеки достаточно полно. На хабре существует статья, посвященная очень узкой задаче по чтению писем (и выводу их содержимого в консоль) и статья с how-to по отправлению
письма с вложениями при помощи Spring Email. Также существует несколько статей (например) на тематических ресурсах, которые приводят порядок действий при работе
с электронной почтой. В этих источниках мне не хватало объяснения основ, на которых стоит
электронная почта и взгляда с высоты на существующие библиотеки для работы с почтой в Java.

Для таких же лапкообразных, как я, написана эта статья. Она дает общие представления о работе электронной почты, разъясняет основные сущности библиотеки Jakarta Mail и дает советы о том, как работать с электронной почтой в Spring-приложениях.

  1. Электронная почта
  2. Работа с электронной почтой с помощью Jakarta Mail
  3. Работа с электронной почтой в среде Spring

1. Электронная почта

Сегодня электронная почта кажется устаревшей технологией, которая нужна только для того чтобы зарегистрироваться на сайтах, куда забыли прикрутить авторизацию с помощью Google, Vk или Facebook -аккаунта. Возможно, так и есть, но наверняка вы до сих пор получаете электронные письма о падении сборки от вашего CI-инструмента или уведомления о пул-реквестах от вашего репозитория на свой почтовый ящик.

Электронная почта была создана на заре интернета и позаимствовала многие свои идеи из реального мира. Подобно тому как в оффлайне для отправки и получения писем нам требуются почтовые отделения, в электронной почте нам нужны почтовые сервера, т.е. электронная почта основана на модели клиент-сервер. В крупных компаниях сисадмины разворачивают свои собственные почтовые сервера, в маленьких компаниях используют почтовые сервера, предоставляемые специальными провайдерами, например: Яндекс.Почта, Gmail и др.

Популярные почтовые серверы: Postfix, Sendmail, Apache James, Zimbra.

Для работы с письмами на стороне клиента используются десктопные приложения вроде Outlook или The Bat!, либо веб-приложения вроде Gmail или Яндекс.Почта.

Предположим, что человек А решил отправить письмо человеку Б. Он пишет его в своем почтовом клиенте и нажимает кнопку «отправить». Письмо отправляется по протоколу SMTP на почтовый сервер клиента А. Этот почтовый сервер пересылает письмо почтовому серверу клиенту Б также при помощи протокола SMTP. Когда клиент Б решит проверить почту, он сделает запрос к своему почтовому серверу с помощью протокола POP3 или IMAP. В случае использования протокола POP3 письма будут выкачены на устройство клиента Б и удалены с почтового сервера. При использовании протокола IMAP клиенту Б будут переданы копии писем,
а оригиналы останутся храниться на почтовом сервере.

Email transition

Электронное письмо

Электронное письмо состоит из следующих частей:

  • Заголовки — тема письма, адрес отправителя, адреса получателей и служебная информация.
  • Тело письма — текст, html, картинки, вложения.

Раньше тело письма могло состоять только из символов ASCII, но с развитием почтовых систем, появилась возможность добавлять в тело html-разметку, изображения, а также прикладывать к письму различные файлы. Для того чтобы правильно выделять различные части из письма, были разработаны MIME-типы — многоцелевые расширения интернет-почты.

Для отправки писем используется протокол SMTP. А для их приема используется либо протокол POP3, либо IMAP.

Существуют и другие почтовые протоколы, но три перечисленных выше — наиболее популярны.

Почтовые протоколы

Все почтовые протоколы относятся к самому высокому уровню модели OSI и основаны на TCP.
У каждого из нижеперечисленных протоколов существует защищенная версия, основанная на SSL.

Simple Mail Transfer Protocol — простой протокол передачи писем. Используется для рассылки писем.

SMTP использует порт 25 или 587. А его защищенная версия SMTPS слушает порт 465.

Post Office Protocol v3 — почтовый протокол. Используется для чтения писем. POP3 при выкачивании письма на клиентский компьютер удаляет письмо с почтового сервера. Недостатком этого протокола является то, что при просмотре письма на одном клиентском компьютере это письмо уже нельзя будет посмотреть с другого устройства.

POP3 слушает порт 110. А его защищенная версия POP3S слушает порт 995.

Internet Message Access Protocol — протокол доступа к электронной почте. Альтернатива протоколу POP3. IMAP подгружает на клиент только мета-информацию письма, а остальные
данные предоставляет по требованию.

IMAP слушает порт 143. А его защищенная версия IMAPS слушает порт 993.

MIME-типы

Multipurpose Internet Mail Extensions — многоцелевые расширения интернет-почты. MIME используется, для того чтобы обозначить тип передаваемого контента с помощью протоколов, которые первоначально были предназначены только для передачи текстовой информации, например SMTP и HTTP.

MIME первоначально предназначался для электронной почты, но впоследствии начал использоваться не только в ней, но и в HTTP.

Тип определяется двумя словами, записанными через прямой слеш. Первое слово — это общий тип, а второе — уточнение.

MIME позволяет устанавливать иерархию вложенности одних блоков контента в другие.
При этом блоки контента могут быть смешанного содержимого. Для таких блоков используется общий тип multipart , нам еще встретится этот термин.

2. Работа с электронной почтой с помощью Jakarta Mail

Jakarta Mail — это библиотека / фреймворк, использующийся для создания, отправки и чтения электронной почты из Java-кода. Библиотека входит в состав Jakarta EE. Ранее библиотека называлась JavaMail, но с 2017 года она переехала под крыло Jakarta.

Как принято в Java EE, сама библиотека предоставляет только базовые сущности, не зависящие от конкретных реализаций. Эти основные классы находятся в пакете javax.mail . Помимо этого к библиотеке прикладывается Reference Implementation для основных протоколов — SMTP, POP3, IMAP. Все классы, относящиеся к этим протоколам, находятся в пакете com.sun.mail . Для использования других протоколов, не входящих в Reference Implementation, необходимо подключать сторонние библиотеки.

Для обработки различных форматов данных, которые могут быть вложены в электронное письмо, Jakarta Mail использует библиотеку JavaBeans Activation Framework — при использовании автоматического сборщика эта зависимость будет подтянута в ваш проект транзитивно.

На текущий момент актуальной версией Jakarta Mail является 1.6.5, но близится выпуск версии 2.0.0, главным «нововведением» которого станет переименование основного пакета с
javax.mail на jakarta.mail .

В Jakarta Mail не входит почтовый сервер. Для отправки писем по SMTP или получения по IMAP или POP3 требуется развернуть собственный почтовый сервер или использовать существующий.

Настройка почтовых серверов

Как следует из первой части, для отправки и получения электронных писем необходимо поддерживать соединение с почтовыми серверами. В Jakarta Mail почтовые сервера представляют объекты классов Transport и Store для отправления и чтения писем соответственно.

Настройка соединения с почтовыми серверами происходит с помощью пар ключ-значение, передаваемых в виде объекта класса Properties . Например для настройки SMTP-соединения с почтовым сервером Яндекс.Почты достаточно следующих параметров:

Полный список различных настроек и их описание можно найти в документации на пакеты основной библиотеки и ее реализаций для различных протоколов.

Сессия

На основе объекта Properties создается сессия:

Обычно сессия создается одна для всего приложения. Она представляет собой сеанс работы с почтовыми серверами. Почти каждый объект библиотеки должен принадлежать какой-либо сессии.

Аутентификация

Вторым аргументом при создании сессии необходимо передать объект абстрактного класса Authenticator , у которого требуется переопределить метод getPasswordAuthentication() (по умолчанию возвращает null, почему он не был сделан абстрактным — неизвестно).

Этот метод используется сессией для связи с почтовым сервером — метод будет вызываться при каждом обращении к серверу. При его вызове возвращается объект класса PasswordAuthentication , который представляет собой обычное DTO для хранения логина и пароля.

Разработчиками библиотеки подразумевается, что внутри этого метода можно запросить пароль у пользователя или достать его из хранилища. При получении пароля можно пользоваться значениями полей класса Authenticator : IP почтового сервера, тип протокола, используемый
порт и даже имя дефолтного пользователя.

Почти все сущности, представляющие какие-либо объекты предметной области, представлены абстрактными классами, реализация которых зависит от конкретного протокола:

  • Message — электронное письмо;
  • BodyPart — фрагмент электронного письма: основная часть или вложение;
  • Address — адрес отправителя или получателя;
  • Folder — папка, в которой хранятся электронные письма;
  • SearchTerm — условие поиска писем в папке;
  • MailEvent — событие, произошедшее с письмом.
    Обработка событий основывается на паттерне Наблюдатель.

Примечание — в данной статье обработка событий не рассмотрена.

Письма

Message Inheritance

Электронные письма представляются с помощью абстрактного класса Message , у которого есть один наследник — MimeMessage (реализации различных протоколов могут дополнительно расширять этот класс).

Message scheme

Электронное письмо состоит из метаинформации: отправитель, получатели, тема, дата отправки и др.,- и основной части: текст и вложения. Основная часть письма представлена в виде фрагментов BodyPart , которые хранятся в контейнере Multipart . Подробнее о фрагментах — ниже.

Каждое письмо привязывается к конкретной сессии. При создании нового письма в конструктор передается объект сессии:

Для установки значений полей метаинформации используются обычные сеттеры. При установке получателей необходимо указывать еще тип адресантов, для этого у класса Message есть внутренний класс RecipientType , который представляет собой различные виды адресантов:

  • TO — прямой получатель;
  • CC — получатель копии;
  • BCC — получатель скрытой копии.

Поля с такими названиями можно найти в интерфейсе любого почтового клиента при написании письма.

Recipients types

Флаги

При чтении писем можно изменять их состояние, устанавливая им различные флаги. Стандартные флаги представлены в классе Flags :

  • ANSWERED — отвечено
  • DELETED — удалено
  • DRAFT — черновик
  • FLAGGED — выделено
  • RECENT — недавнее
  • SEEN — просмотрено
  • USER — пользовательский флаг

Флаги устанавливаются в сообщении в нужное положение с помощью метода setFlag(Flag, boolean set) .

Адреса

Адреса используются для идентификации получателя письма.

Класс Address является абстрактным и по сути имеет всего одну реализацию — InternetAddress (есть еще NewsAddress для мамонтов, пользующихся Usenet’ом).

InternetAddress представляет привычные нам адреса электронной почты, записанные в формате:

Помимо адреса объекты могут хранить в себе имя адресанта в любой кодировке:

Для того чтобы создать объект InternetAddress можно воспользоваться одним из конструкторов, либо распарсить строку с несколькими адресами в массив:

Примечание — для валидации адресов электронной почты, приходящих от пользователя в виде строк, можно пользоваться аннотацией @Email , объявленной в библиотеке Bean Validation.

Фрагменты письма

Фрагмент письма — это неделимая часть письма, несущая полезную нагрузку адресанту. Фрагмент письма может либо относиться к основной части письма, либо быть вложением.

В Jakarta Mail фрагмент письма представлен абстрактным классом BodyPart и его реализацией MimeBodyPart .
Тип фрагмента определяется параметром disposition :

  • INLINE для основной части письма и
  • ATTACHMENT для вложения.

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

Наиболее часто используемые MIME-типы для частей:

  • text/plain для основной части письма и
  • application/octet-stream для вложений.

Для создания фрагмента письма можно пользоваться обычным конструктором:

На UML-диаграмме наследования, приведенной выше, объявлен еще интерфейс Part , который реализуется почти всеми классами, относящимися к письму. В нем объявлены методы доступа к метаинформации письма. Это довольно странное решение — как-будто у различных фрагментов одного письма могут быть различные адресанты или темы письма.

Запись контента

После создания фрагмента необходимо записать в него полезную нагрузку. Для основной части используется метод setText() , а для вложения — attachFile() или setContent() .

MIME-тип вложения в большинстве случаев определяется автоматически по расширению файла.

Чтение контента

Для чтения контента из пришедшего письма можно воспользоваться методом
void saveFile(File) для сохранения контента на диск или
void writeTo(OutputStream) для перенаправления контента в указанный поток.

Мультифрагменты

Фрагменты BodyPart для помещения в письмо объединяются в структуру Multipart . MimeBodyPart соответственно объединяются в структуру MimeMultipart .

После этого мультипарт вкладывается в письмо:

Папка с письмами

Для хранения писем на почтовом сервере используются папки. Обычно папка по умолчанию называется INBOX (в POP3 это единственная папка).

Папки организуют древовидную структуру. В некоторых реализациях папки могут хранить в себе что-то одно: папки или письма, — в других (например, IMAP) сразу и то, и другое.

Папка может находиться в открытом и закрытом состоянии. При извлечении папки она находится в закрытом состоянии. Чтобы прочитать письма, находящиеся в ней, необходимо сначала открыть ее open(int mode) в одной из двух режимов: только для чтения READ_ONLY или на чтение и запись — READ_WRITE .

Операции с содержимым папки могут выполняться только в открытом состоянии, а операции над самой папкой только в закрытом состоянии.

Для извлечения писем из папки можно использовать метод getMessages() , возвращающий все письма из папки:

Письма располагаются в папке аналогично списку и имеют номера, начиная с 1 . Для получения писем по их порядковым номерам используются разновидности приведенного выше метода.

При использовании протокола IMAP из папки достаются легковесные объекты
вместо полноценных сообщений. Необходимые части сообщения подтягиваются с почтового сервера по требованию, либо можно сразу загрузить полную версию письма, вызвав метод fetch() класса Folder, передав в него желаемые письма.

Папки позволяют удалять письма, но сделать это можно только с теми письмами, которые помечены флагом DELETED .

Поиск писем

Jakarta Mail позволяет проводить поиск по письмам в папках. Для составления поискового запроса предназначен абстрактный класс SearchTerm , имеющий множество реализаций для поиска по различным критериям: теме письма, дате отправления, отправителю, флагам и пр. Различные критерии поиска могут объединяться в один поисковый запрос.

После составление поискового запроса он запускается по указанной папке:

3. Работа с электронной почтой в среде Spring

Для удобства использования библиотеки Jakarta Mail в Spring-приложениях была разработана библиотека Spring Email, которая по сути является фасадом к API Jakarta Mail.
Все классы библиотеки находятся в пакете org.springframework.mail , который можно подтянуть с помощью зависимости spring-boot-starter-mail .

В большинстве Java-приложений не требуется читать письма с почтового сервера, чаще всего необходимо только рассылать электронную почту (например для подтверждения регистрации в вашем веб-приложении или для рассылки уведомлений). В связи с этим Spring Email предоставляет API только для отправки писем.

Интересный факт — в разработке Spring Mail принимал участие наш земляк — Дмитрий Копыленко.

Spring Email Scheme

Библиотека Spring Email предоставляет ряд классов и интерфейсов, которые хорошо вписываются в общую концепцию фреймворка. Все что нужно для отправки электронного сообщения из вашего кода, это заинжектить бин отправителя и передать ему письмо с помощью метода send() .

В библиотеке выделяются два вида писем: простые и MIME-письма. Простые не могут содержать в своем теле ничего кроме текста, MIME-письма могут содержать в теле html-верстку, изображения и иметь вложения.

Отправка простых писем

Простые письма представлены классом SimpleMailMessage . Этот класс полностью независим от сущностей библиотеки Jakarta Mail — все его методы принимают примитивы и объекты стандартной библиотеки Java.

Отправить созданное письмо можно с помощью бина интерфейса MailSender .

Отправка MIME-писем

Для работы с MIME-письмами используется класс MimeMessage из Jakarta Mail. Для тех случаев, когда требуется одинаковый интерфейс для работы с простыми и MIME-письмами существует класс адаптер MimeMailMessage , который как и SimpleMailMessage реализует интерфейс MailMessage .

Для создания объектов MimeMessage в библиотеке есть класс-помощник — MimeMessageHelper . Этот класс предоставляет удобный фасад для работы с телом письма, его
мультимедийными вставками и вложениями. Пользоваться им очень просто: достаточно обернуть в него объект MIME-письма и задать все необходимые параметры:

Таким образом MimeMessageHelper избавляет разработчика от необходимости создания BodyPart , и их сборки в Multipart , предоставляя удобное линейное API.

Для отправки MIME-писем используется бин интерфейса JavaMailSender :

Помимо всего прочего в Spring Email существует callback-интерфейс для создания MIME-писем — MimeMessagePreparator . Этот интерфейс является функциональным и может быть заменен лямбдой Consumer , принимающей MimeMessage .

Обработка исключений

В отличие от Jakarta Mail, все методы Spring Email бросают непроверяемые исключения.
Эти исключения являются runtime обертками над проверяемыми исключениями Jakarta Mail.

Особый интерес представляет исключение MailSendException . Оно позволяет извлечь конкретные письма, при отправке которых были инициированы исключения, с помощью метода getFailedMessages() :

How can I send an email by Java application using GMail, Yahoo, or Hotmail?

Is it possible to send an email from my Java application using a GMail account? I have configured my company mail server with Java app to send email, but that’s not going to cut it when I distribute the application. Answers with any of using Hotmail, Yahoo or GMail are acceptable.

14 Answers 14

First download the JavaMail API and make sure the relevant jar files are in your classpath.

Here’s a full working example using GMail.

Naturally, you’ll want to do more in the catch blocks than print the stack trace as I did in the example code above. (Remove the catch blocks to see which method calls from the JavaMail API throw exceptions so you can better see how to properly handle them.)

Thanks to @jodonnel and everyone else who answered. I’m giving him a bounty because his answer led me about 95% of the way to a complete answer.

Something like this (sounds like you just need to change your SMTP server):

Other people have good answers above, but I wanted to add a note on my experience here. I’ve found that when using Gmail as an outbound SMTP server for my webapp, Gmail only lets me send

10 or so messages before responding with an anti-spam response that I have to manually step through to re-enable SMTP access. The emails I was sending were not spam, but were website «welcome» emails when users registered with my system. So, YMMV, and I wouldn’t rely on Gmail for a production webapp. If you’re sending email on a user’s behalf, like an installed desktop app (where the user enters their own Gmail credentials), you may be okay.

Also, if you’re using Spring, here’s a working config to use Gmail for outbound SMTP:

Как отправить письмо из Java-приложения (с примером)

Как отправить письмо из Java-приложения (с примером) - 1

Отправка электронного сообщения из Java-приложения это распространенное требование. Не имеет значения работаешь ли ты над core Java-приложением, веб-приложением или enterprise Java-приложением, тебе может понадобится отправить письмо для оповещения персонала из тех.поддержки об ошибках, или просто отправить письмо пользователям при регистрации, восстановить пароль или попросить их подтвердить адрес электронной почты после регистрации. Есть очень много сценариев(случаев), когда тебе нужна возможность отправить электронные письма из Java-приложения. В готовых приложениях у тебя уже есть модуль или библиотека, работающая со всеми функциями для отправки электронных писем, например, возможность отправлять вложения, изображения, включая подписи и форматированный текст в электронных письмах, НО если тебе нужно написать что-то с нуля, тогда Java Mail API это прекрасный вариант. В этой статье мы научимся как отправлять электронные письма из Java-приложения, используя почтовый API (javax.mail). Перед написанием кода ты должен знать некоторые основы работы электронной почты, например, тебе нужен SMTP (простой протокол передачи почты) сервер. Если ты запускаешь свое Java-приложение под Linux, тогда ты должен знать, что SMTP демон (прим. «компьютерная программа в системах класса UNIX, запускаемая самой системой и работающая в фоновом режиме без прямого взаимодействия с пользователем») использует порт 25. Ты можешь использовать любой почтовый сервер для отправки сообщений применяя Java, включая общедоступные почтовые сервера, такие как GMail, Yahoo или любой другой поставщик услуг, все что тебе нужно это детали их SMTP сервера, например, имя хоста, порт, параметры соединения и т.д. Так же ты можешь использовать SSL (англ. secure sockets layer — уровень защищённых сокетов), TLS (англ. Transport Layer Security — безопасность транспортного уровня) для безопасного соединения и отправки писем, но этот пример мы сделали простым и cфокусировались на минимальной логике для отправки письма из Java-приложения. В дальнейших статьях мы научимся как отправлять письмо используя вложения, как отправлять форматированное с помощью HTML письмо, как прикрепить изображения в письмо, как использовать SSL аутентификацию для соединения с сервером GMail и отправки писем и т.д. А сейчас, давай поймем (разберем) этот простой пример Java Mail API.

Пример Java-кода для отправки электронного сообщения

Создай объект Session вызывая Session.getDefaultInstance(properties) , где properties — содержит все важные свойства, например, имя хоста SMTP сервера.

Создай объект MimeMessage передавая ему объект Session , полученный на предыдущем шаге. У нас есть набор различных свойств в этом объекте, таких как получатель письма, тема, тело сообщения, вложения и т.д.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *