如何使用Java应用程序通过Gmail、Yahoo或Hotmail发送电子邮件?

219

我能否使用Gmail账户从我的Java应用程序发送电子邮件? 我已经配置了公司的邮件服务器,使Java应用程序能够发送电子邮件,但是在分发应用程序时这种方式并不可行。 接受任何使用Hotmail,Yahoo或Gmail的答案。

14个回答

200

首先下载JavaMail API,并确保相关的jar文件在您的类路径中。

这是使用Gmail的完整工作示例。

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;

public class Main {

    private static String USER_NAME = "*****";  // GMail user name (just the part before "@gmail.com")
    private static String PASSWORD = "********"; // GMail password
    private static String RECIPIENT = "lizard.bill@myschool.edu";

    public static void main(String[] args) {
        String from = USER_NAME;
        String pass = PASSWORD;
        String[] to = { RECIPIENT }; // list of recipient email addresses
        String subject = "Java send mail example";
        String body = "Welcome to JavaMail!";

        sendFromGMail(from, pass, to, subject, body);
    }

    private static void sendFromGMail(String from, String pass, String[] to, String subject, String body) {
        Properties props = System.getProperties();
        String host = "smtp.gmail.com";
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.host", host);
        props.put("mail.smtp.user", from);
        props.put("mail.smtp.password", pass);
        props.put("mail.smtp.port", "587");
        props.put("mail.smtp.auth", "true");

        Session session = Session.getDefaultInstance(props);
        MimeMessage message = new MimeMessage(session);

        try {
            message.setFrom(new InternetAddress(from));
            InternetAddress[] toAddress = new InternetAddress[to.length];

            // To get the array of addresses
            for( int i = 0; i < to.length; i++ ) {
                toAddress[i] = new InternetAddress(to[i]);
            }

            for( int i = 0; i < toAddress.length; i++) {
                message.addRecipient(Message.RecipientType.TO, toAddress[i]);
            }

            message.setSubject(subject);
            message.setText(body);
            Transport transport = session.getTransport("smtp");
            transport.connect(host, from, pass);
            transport.sendMessage(message, message.getAllRecipients());
            transport.close();
        }
        catch (AddressException ae) {
            ae.printStackTrace();
        }
        catch (MessagingException me) {
            me.printStackTrace();
        }
    }
}

自然地,你会希望在 catch 块中执行更多操作,而不只是像我在上面的示例代码中那样打印堆栈跟踪。(移除 catch 块,以查看哪些来自 JavaMail API 的方法调用会抛出异常,这样就可以更好地了解如何正确处理它们。)


感谢@jodonnel和其他所有回答者。我正在给他一份奖励,因为他的答案让我完成了大约95%的工作。


1
我是唯一一个在这里遇到AuthenticationFailedException的人吗? props.put("mail.smtp.auth", "true"); 如果true是字符串,那么它就没问题。如果是布尔值,也没关系。 - nyxz
1
除非session.getTransport("smtp")是session.getTransport("smtps"),否则这将无法工作。 - Michael Roller
3
请使用以下代码设置 SSL Gmail 邮件连接: props.put("mail.smtp.port", "465"); // 而不是 587 - Tomasz Dziurko
3
请参考 http://www.oracle.com/technetwork/java/faq-135477.html#getdefaultinstance,了解如何使用 Session.getDefaultInstance(properties) 方法。该 FAQ 建议使用 getInstance(..) 方法替代。 - Bryan
8
我无法使用以上及类似代码访问SMTP Gmail,一直遇到javax.mail.AuthenticationFailedException错误。最后我只好在我的Gmail设置中显式启用“不安全应用程序”:https://www.google.com/settings/security/lesssecureapps。一旦启用了“不安全的应用程序”,代码就可以正常运行了。 - Marcus Junius Brutus
显示剩余11条评论

115

像这样(听起来你只需要更改SMTP服务器):

String host = "smtp.gmail.com";
String from = "user name";
Properties props = System.getProperties();
props.put("mail.smtp.host", host);
props.put("mail.smtp.user", from);
props.put("mail.smtp.password", "asdfgh");
props.put("mail.smtp.port", "587"); // 587 is the port number of yahoo mail
props.put("mail.smtp.auth", "true");

Session session = Session.getDefaultInstance(props, null);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));

InternetAddress[] to_address = new InternetAddress[to.length];
int i = 0;
// To get the array of addresses
while (to[i] != null) {
    to_address[i] = new InternetAddress(to[i]);
    i++;
}
System.out.println(Message.RecipientType.TO);
i = 0;
while (to_address[i] != null) {

    message.addRecipient(Message.RecipientType.TO, to_address[i]);
    i++;
}
message.setSubject("sending in a group");
message.setText("Welcome to JavaMail");
// alternately, to send HTML mail:
// message.setContent("<p>Welcome to JavaMail</p>", "text/html");
Transport transport = session.getTransport("smtp");
transport.connect("smtp.mail.yahoo.co.in", "user name", "asdfgh");
transport.sendMessage(message, message.getAllRecipients());
transport.close();

能否以HTML格式发送内容?如果我尝试编写一些HTML代码并将其发送,但在接收端,电子邮件的内容仅为HTML代码。 - Thang Pham
4
要发送HTML格式的邮件内容,需要将以下代码行进行更改:message.setText("Welcome to JavaMail"); 改为 message.setContent("<h1>Hello world</h1>", "text/html");。这样做可以将邮件正文内容设置为HTML格式,并添加相应的MIME类型声明。 - mist
4
这个需要添加的内容是 ("mail.smtp.starttls.enable", "true")。 - Sotomajor
需要导入某个Java库吗?@Sotomajor,我们在哪里使用那个缺失的行? - gumuruh
@gumuruh 在 props 中。应该是 props.put("mail.smtp.starttls.enable", "true") - Sotomajor

23

以上有其他人给出了很好的答案,但我想补充一下我的经验。当我将Gmail作为我的web应用程序的出站SMTP服务器时,我发现在发送 ~10条左右消息后,Gmail就会响应反垃圾邮件信息,要求我手动通过以重新启用SMTP访问。我发送的电子邮件不是垃圾邮件,而是网站“欢迎”邮件,当用户在我的系统中注册时发送。因此,您的情况可能有所不同,我不建议在生产web应用程序中使用Gmail。如果您代表用户发送电子邮件,比如安装的桌面应用程序(用户输入自己的Gmail凭据),那么您可能没问题。

另外,如果您正在使用Spring,以下是一个可行的配置,可以使用Gmail进行出站SMTP:

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="host" value="smtp.gmail.com"/>
    <property name="port" value="465"/>
    <property name="username" value="${mail.username}"/>
    <property name="password" value="${mail.password}"/>
    <property name="javaMailProperties">
        <value>
            mail.debug=true
            mail.smtp.auth=true
            mail.smtp.socketFactory.class=java.net.SocketFactory
            mail.smtp.socketFactory.fallback=false
        </value>
    </property>
</bean>

谢谢 Jason 提供的配置设置示例和有关出站邮件限制的警告。我之前从未遇到过这个限制,但我相信其他人会发现这些信息非常有用。 - Bill the Lizard
我想了解更多关于垃圾邮件限制的信息...如果我需要发送多封电子邮件,我应该将它们分成几组吗?还是等待一定的时间?有人知道谷歌邮件限制的详细信息吗? - opensas

13

尽管这个问题已经关闭,但我想发表一个对策,现在使用Simple Java Mail(开源JavaMail smtp包装器):

final Email email = new Email();

String host = "smtp.gmail.com";
Integer port = 587;
String from = "username";
String pass = "password";
String[] to = {"to@gmail.com"};

email.setFromAddress("", from);
email.setSubject("sending in a group");
for( int i=0; i < to.length; i++ ) {
    email.addRecipient("", to[i], RecipientType.TO);
}
email.setText("Welcome to JavaMail");

new Mailer(host, port, from, pass).sendMail(email);
// you could also still use your mail session instead
new Mailer(session).sendMail(email);

2
我在这段代码中遇到了错误:“消息:通用错误:530 5.7.0 必须首先发出STARTTLS命令” - 你如何使用vesijama启用starttls? - iddqd
1
我收到了“必须先发出STARTTLS命令”错误,因为在下面这行代码中,我将isStartTlsEnabled变量作为布尔值而不是字符串: props.put("mail.smtp.starttls.enable", isStartTlsEnabled); - user64141
这个答案中可以看到,要使用TLS,您可以执行类似于new Mailer(your login / your session, TransportStrategy.SMTP_TLS).sendMail(email);的操作。 - Benny Bottema
RecipientType cannot be resolved to a variable 无法解析为变量。 - Cardinal System

7

以下是完整的代码,已经正常工作:

package ripon.java.mail;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;

public class SendEmail
{
public static void main(String [] args)
{    
    // Sender's email ID needs to be mentioned
     String from = "test@gmail.com";
     String pass ="test123";
    // Recipient's email ID needs to be mentioned.
   String to = "ripon420@yahoo.com";

   String host = "smtp.gmail.com";

   // Get system properties
   Properties properties = System.getProperties();
   // Setup mail server
   properties.put("mail.smtp.starttls.enable", "true");
   properties.put("mail.smtp.host", host);
   properties.put("mail.smtp.user", from);
   properties.put("mail.smtp.password", pass);
   properties.put("mail.smtp.port", "587");
   properties.put("mail.smtp.auth", "true");

   // Get the default Session object.
   Session session = Session.getDefaultInstance(properties);

   try{
      // Create a default MimeMessage object.
      MimeMessage message = new MimeMessage(session);

      // Set From: header field of the header.
      message.setFrom(new InternetAddress(from));

      // Set To: header field of the header.
      message.addRecipient(Message.RecipientType.TO,
                               new InternetAddress(to));

      // Set Subject: header field
      message.setSubject("This is the Subject Line!");

      // Now set the actual message
      message.setText("This is actual message");

      // Send message
      Transport transport = session.getTransport("smtp");
      transport.connect(host, from, pass);
      transport.sendMessage(message, message.getAllRecipients());
      transport.close();
      System.out.println("Sent message successfully....");
   }catch (MessagingException mex) {
      mex.printStackTrace();
   }
}
}

3
//set CLASSPATH=%CLASSPATH%;activation.jar;mail.jar
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;

public class Mail
{
    String  d_email = "iamdvr@gmail.com",
            d_password = "****",
            d_host = "smtp.gmail.com",
            d_port  = "465",
            m_to = "iamdvr@yahoo.com",
            m_subject = "Testing",
            m_text = "Hey, this is the testing email using smtp.gmail.com.";
    public static void main(String[] args)
    {
        String[] to={"XXX@yahoo.com"};
        String[] cc={"XXX@yahoo.com"};
        String[] bcc={"XXX@yahoo.com"};
        //This is for google
        Mail.sendMail("venkatesh@dfdf.com", "password", "smtp.gmail.com", 
                      "465", "true", "true", 
                      true, "javax.net.ssl.SSLSocketFactory", "false", 
                      to, cc, bcc, 
                      "hi baba don't send virus mails..", 
                      "This is my style...of reply..If u send virus mails..");
    }

    public synchronized static boolean sendMail(
        String userName, String passWord, String host, 
        String port, String starttls, String auth, 
        boolean debug, String socketFactoryClass, String fallback, 
        String[] to, String[] cc, String[] bcc, 
        String subject, String text) 
    {
        Properties props = new Properties();
        //Properties props=System.getProperties();
        props.put("mail.smtp.user", userName);
        props.put("mail.smtp.host", host);
        if(!"".equals(port))
            props.put("mail.smtp.port", port);
        if(!"".equals(starttls))
            props.put("mail.smtp.starttls.enable",starttls);
        props.put("mail.smtp.auth", auth);
        if(debug) {
            props.put("mail.smtp.debug", "true");
        } else {
            props.put("mail.smtp.debug", "false");         
        }
        if(!"".equals(port))
            props.put("mail.smtp.socketFactory.port", port);
        if(!"".equals(socketFactoryClass))
            props.put("mail.smtp.socketFactory.class",socketFactoryClass);
        if(!"".equals(fallback))
            props.put("mail.smtp.socketFactory.fallback", fallback);

        try
        {
            Session session = Session.getDefaultInstance(props, null);
            session.setDebug(debug);
            MimeMessage msg = new MimeMessage(session);
            msg.setText(text);
            msg.setSubject(subject);
            msg.setFrom(new InternetAddress("p_sambasivarao@sutyam.com"));
            for(int i=0;i<to.length;i++) {
                msg.addRecipient(Message.RecipientType.TO, 
                                 new InternetAddress(to[i]));
            }
            for(int i=0;i<cc.length;i++) {
                msg.addRecipient(Message.RecipientType.CC, 
                                 new InternetAddress(cc[i]));
            }
            for(int i=0;i<bcc.length;i++) {
                msg.addRecipient(Message.RecipientType.BCC, 
                                 new InternetAddress(bcc[i]));
            }
            msg.saveChanges();
            Transport transport = session.getTransport("smtp");
            transport.connect(host, userName, passWord);
            transport.sendMessage(msg, msg.getAllRecipients());
            transport.close();
            return true;
        }
        catch (Exception mex)
        {
            mex.printStackTrace();
            return false;
        }
    }

}

3

最低要求:

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class MessageSender {

    public static void sendHardCoded() throws AddressException, MessagingException {
        String to = "a@a.info";
        final String from = "b@gmail.com";

        Properties properties = new Properties();
        properties.put("mail.smtp.starttls.enable", "true");
        properties.put("mail.smtp.auth", "true");
        properties.put("mail.smtp.host", "smtp.gmail.com");
        properties.put("mail.smtp.port", "587");

        Session session = Session.getInstance(properties,
                new javax.mail.Authenticator() {
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(from, "BeNice");
                    }
                });

        MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(from));
        message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
        message.setSubject("Hello");
        message.setText("What's up?");

        Transport.send(message);
    }

}

1
@AlikElizin-kilaka 我试图使用我的 Gmail ID 作为发件人和收件人,发现您还需要在 Google 帐户设置中将“允许不安全的应用程序”标志设置为 ON,因为您正在以不太安全的方式访问您的 Gmail 配置文件。只有这样,您才能从 Java 客户端看到发送的电子邮件。否则,您会收到 javax.mail.AuthenticationFailedException。 - kunal

2
发布的代码解决方案可能会在同一JVM内的任何地方设置多个SMTP会话时出现问题。
JavaMail FAQ建议使用。
Session.getInstance(properties);

替代

Session.getDefaultInstance(properties);

因为 getDefault 只会在第一次调用时使用给定的属性。后续对默认实例的所有使用都将忽略属性更改。
请参见http://www.oracle.com/technetwork/java/faq-135477.html#getdefaultinstance

2
增值内容:
  • 主题、信息和显示名称的编码陷阱
  • 现代Java邮件库版本只需要一个神奇属性
  • Session.getInstance()建议使用而不是Session.getDefaultInstance()
  • 在相同的示例中附件
  • 谷歌关闭不安全应用程序仍然有效:在您的组织中启用2因素身份验证->开启2FA->生成应用程序密码。
    import java.io.File;
    import java.nio.charset.StandardCharsets;
    import java.util.Properties;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.activation.DataHandler;
    import javax.mail.Message;
    import javax.mail.Session;
    import javax.mail.Transport;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeBodyPart;
    import javax.mail.internet.MimeMessage;
    import javax.mail.internet.MimeMultipart;
    import javax.mail.util.ByteArrayDataSource;
    
    public class Gmailer {
      private static final Logger LOGGER = Logger.getLogger(Gmailer.class.getName());
    
      public static void main(String[] args) {
        send();
      }
    
      public static void send() {
        Transport transport = null;
        try {
          String accountEmail = "account@source.com";
          String accountAppPassword = "";
          String displayName = "Display-Name 東";
          String replyTo = "reply-to@source.com";
    
          String to = "to@target.com";
          String cc = "cc@target.com";
          String bcc = "bcc@target.com";
    
          String subject = "Subject 東";
          String message = "<span style='color: red;'>東</span>";
          String type = "html"; // or "plain"
          String mimeTypeWithEncoding = "text/" + type + "; charset=" + StandardCharsets.UTF_8.name();
    
          File attachmentFile = new File("Attachment.pdf");
          // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
          String attachmentMimeType = "application/pdf";
          byte[] bytes = ...; // read file to byte array
    
          Properties properties = System.getProperties();
          properties.put("mail.debug", "true");
          // i found that this is the only property necessary for a modern java mail version
          properties.put("mail.smtp.starttls.enable", "true");
          // https://javaee.github.io/javamail/FAQ#getdefaultinstance
          Session session = Session.getInstance(properties);
    
          MimeMessage mimeMessage = new MimeMessage(session);
    
          // probably best to use the account email address, to avoid landing in spam or blacklists
          // not even sure if the server would accept a differing from address
          InternetAddress from = new InternetAddress(accountEmail);
          from.setPersonal(displayName, StandardCharsets.UTF_8.name());
          mimeMessage.setFrom(from);
    
          mimeMessage.setReplyTo(InternetAddress.parse(replyTo));
    
          mimeMessage.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
          mimeMessage.setRecipients(Message.RecipientType.CC, InternetAddress.parse(cc));
          mimeMessage.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(bcc));
    
          mimeMessage.setSubject(subject, StandardCharsets.UTF_8.name());
    
          MimeMultipart multipart = new MimeMultipart();
    
          MimeBodyPart messagePart = new MimeBodyPart();
          messagePart.setContent(mimeMessage, mimeTypeWithEncoding);
          multipart.addBodyPart(messagePart);
    
          MimeBodyPart attachmentPart = new MimeBodyPart();
          attachmentPart.setDataHandler(new DataHandler(new ByteArrayDataSource(bytes, attachmentMimeType)));
          attachmentPart.setFileName(attachmentFile.getName());
          multipart.addBodyPart(attachmentPart);
    
          mimeMessage.setContent(multipart);
    
          transport = session.getTransport();
          transport.connect("smtp.gmail.com", 587, accountEmail, accountAppPassword);
          transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
        }
        catch(Exception e) {
          // I prefer to bubble up exceptions, so the caller has the info that someting went wrong, and gets a chance to handle it.
          // I also prefer not to force the exception in the signature.
          throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
        }
        finally {
          if(transport != null) {
            try {
              transport.close();
            }
            catch(Exception e) {
              LOGGER.log(Level.WARNING, "failed to close java mail transport: " + e);
            }
          }
        }
      }
    }

1

下面是发送带有附件的电子邮件时我通常所采用的方法,工作正常。:)

 public class NewClass {

    public static void main(String[] args) {
        try {
            Properties props = System.getProperties();
            props.put("mail.smtp.starttls.enable", "true");
            props.put("mail.smtp.host", "smtp.gmail.com");
            props.put("mail.smtp.auth", "true");
            props.put("mail.smtp.port", "465"); // smtp port
            Authenticator auth = new Authenticator() {

                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication("username-gmail", "password-gmail");
                }
            };
            Session session = Session.getDefaultInstance(props, auth);
            MimeMessage msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress("username-gmail@gmail.com"));
            msg.setSubject("Try attachment gmail");
            msg.setRecipient(RecipientType.TO, new InternetAddress("username-gmail@gmail.com"));
            //add atleast simple body
            MimeBodyPart body = new MimeBodyPart();
            body.setText("Try attachment");
            //do attachment
            MimeBodyPart attachMent = new MimeBodyPart();
            FileDataSource dataSource = new FileDataSource(new File("file-sent.txt"));
            attachMent.setDataHandler(new DataHandler(dataSource));
            attachMent.setFileName("file-sent.txt");
            attachMent.setDisposition(MimeBodyPart.ATTACHMENT);
            Multipart multipart = new MimeMultipart();
            multipart.addBodyPart(body);
            multipart.addBodyPart(attachMent);
            msg.setContent(multipart);
            Transport.send(msg);
        } catch (AddressException ex) {
            Logger.getLogger(NewClass.class.getName()).log(Level.SEVERE, null, ex);
        } catch (MessagingException ex) {
            Logger.getLogger(NewClass.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接