Javamail 详解

最近研究JBPM的mail node节点,使用时老是发不出邮件,后台总是报错....
于是研究了一下javamail.看了一下网上的一篇文章<<深入浅出javamail>>
[quote]
JavaMail 深入浅出
在开始Javamail之前,首先介绍几个跟邮件传输有关的协议以及一些相关的知识.......
SMTP:简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)由RFC821定义,它定义了发送邮件的机制,在JavaMail环境中,基于JavaMail的程序将和因特网服务供应商ISP(internet Service Provider)SMTP服务器通信.SMTP服务器会中转消息给接收方SMTP服务器以便最终让用户经由POP或者IMAP获得.
POP:代表邮局协议(Post Office Protocol).目前的版本是3.所以一般都称之为POP3.这个协议是由RFC1939定义的.POP是一种机制,因特网上多大数用户用它得到邮件.它规定每个用户一个邮箱的支持.使用POP协议的时候,用户的许多性能并不是由POP协议支持的,如查看几封新邮件消息这个功能,这些功能内建在如Eudora或MicrosoftOutlook之类的程序中,它们记住一些事.所以在用JavaMail的时候,如果你想要这些信息,你就必须自己算了.
IMAP:是更高级的用户接收消息的协议,被定义在RFC2060中,IMAP代表因特网消息访问协议(Internet Message Access Protocol),目前用的版本是4,所以也叫做IMAP4.在用到IMAP的时候,邮件服务器必须支持这个协议,不能仅仅把使用POP的程序用于IMAP,并指望它支持IMAP所有性能.
MIME:是因特网邮件扩展标准(Multipurpose Internet Mail Extensions).它不是邮件传输协议,但是对于传输的内容的消息,附件以及其他的内容定义了格式.可以理解成一个定义合适的标准.
NNTP:因为JavaMail将供应商和所有其它的东西分开了,您就能轻松添加额外的协议支持.NNTP 就是网络新闻传输协议.
JavaMail API可以到 http://java.sun.com/products/javamail/index.html 进行下载,并将
mail.jar添加到classpath即可.
JAF框架可以到 http://java.sun.com/products/javabeans/glasgow/jaf.html 进行下载,并将
activation.jar添加到classpath即可.

一些简单的知识点:
1,获取系统Properties.
Properties props = System.getProperties();
2,将您的SMTP服务器名添加到mail.smtp.host关键字的属性中.
Props.pout(“mail.smtp.host”,host);
3,获取基于Properties Session对象.
Session session = Session.getDefaultInstance(props,null);
4,从Session创建一个MimeMessage.
MimeMessage message = new MimeMessage(session);
5,设置消息from域.
Message.setForm(new InternetAddress(from));
6,设置to域.
Message.addRecipient(Message.RecipientType.TO,new InternetAddress(to));
7,设置消息主题.
Message.setSubject( “ HelloJavaMail ” );
8,设置消息内容.
Message.setText( “ Welcome to JavaMail ” );
9发送消息.
Transport.send(message);
10,在编译用运的时候传递MSTP服务器,from地址,to地址.

通过简单的接触了JavaMail相信大家多邮件发送也有了简单的了解和认识,下面我主要研究一下它的具体功能,也就是说具体的接口或类的含义.
Session类定义了一个基本的邮件会话,所有的其他类都是由这个session才生效的,Session对象用java.util.Properties对象获取信息,如邮件服务器,用户名,密码及整个应用程序中共享的其他信息.类的构造器是私有的.它能用getDefaultInstance()方法来共享.获取Session对象的方方法如下:
Properties props = new Properties();
Session session = Session.getDefaultInstance(props,null);
Null参数都是Authenticator对象,在这里没有使用.
对于大多数情况,共享的session已经足够用了.
Message消息类,在获得了Session对象后,就可以继续创建要发送的消息.因为Message是个抽象类,您必须用一个子类,多数情况下为java.mail.internet.MimeMessage.这个能理解成MIME类型和头的电子邮件消息.正如不同的RFC中定义的,虽然在某些头部域非ASCII字符也能被编译,但是Message头只能被限制用US-ASCII字符.要创建一个Message请将Session对象传递给MimeMessage的构造器.
MimeMessage message = new MimeMessage(session);
一旦获得消息,就可以设置各个部分了.最基本的就是setContent()方法,例如
message.setContent( “ Hello ” , ” text/plain ” );
如果知道使用的是MimeMessage,而且消息是纯文本格式,就可以用setText()方法,它只需要代表实际内容的参数.(Mime类型缺省为text/plain)
用setSubject()方法设置subject(主题);
message.setSubject( “主题”);
Address地址类,和Message一样也是一个抽象类,一旦创建了Session和Message并将内容填入消息后,就可以用Address确定信件的地址了,用javax.mail.internet.InternetAddress类.若创建的地址只包含电子邮件地址,只要传递电子邮件地址给构造器就可以了.
例如:Address address = new InternetAddress(“test@163.com”);
若希望名字挨着电子邮件显示,就可以把它传递给构造器,如下:
Address address = new InternetAddress(“test@163.com” ,”测试别名” );
需要为消息的from域和to域创建地址对象,除非邮件服务器阻止,没有什么能阻止你发送一段看上去是任何人的消息了呵呵.一旦创建address将他们域消息连接方法有两种,如要要识别发件人的就可以用setFrom()和setReplyTo方法.然后message.setFrom(address);
需要实用多个from地址的就用addFrom()方法.例子如下:
Address[] address = ,.,. ;
message.addFrom(address);
若要识别消息recipient收件人,就要实用addRecipient()方法了.例如:
message.addRecipient(type,address)
Authenticator与java.net类一样,JavaMailAPI也可以利用Authentcator通过用户名密码访问受保护的资源.对于JavaMail来说,这些资源就是邮件服务器,Authentcator类在javax.mail包中.要使用Authenticator,首先创建一个抽象的子类,并从
GetPasswordAuthentication方法中返回passwordAuthentication实例,创建完成后,您必须向session注册Authenticator,然后在需要认证的时候会通知它,其实说白了就是把配置的用户名和密码返回给调用它的程序.例如:
Properties props = new properties();
Authenticator auth = new MailAuthenticator(“userName”,”password”)//接口声明,创建自己新类的实例.
Session session = Session.getDefauItInstance(props,auth);
Transport消息发送传输类,这个类用协议指定的语言发送消息,通常是SMTP,它是抽象类,它的工作方式与Session有些类似,尽调用静态方法send()方法,就OK了.例如:
Transport.send(message);
或者也可以从针对协议的会话中获取一个特定的实例,传递用户名和密码.发送消息,然后关闭连接,例如:
message.saveChanges();
transport transport = session.getTransport( “ smtp” );//指定的协议
transport.connect(host,username,password);
transport.sendMessage(message,message.getAllRecipients());
transport.close();
如果要观察传到邮件服务器上的邮件命令,请用session.setDubug(true)设置调试标志.
Store和folder用session获取消息,与发送消息开始很相似,但是在session得到后,很可能实用用户名和密码或实用Authenticator连接到一个Store.类似于Transport,也是一样要告诉store用什么协议.
例如
Store store = session.getStore( “ pop3 ” );
Store.connect(host,username,password);
连接到Store之后,接下来,获得一个folder,必须打开它就可以读取里边的消息了.
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message[] message = folder.getMessages();
POP3唯一可用的文件夹就是INBOX,如果实用IMAP,还可以用其他的文件夹.
当读到了具体的message以后,就可以用getContent来获取内容,或者用writeTo()将内容写入 流,getContent()方法只能得到消息内容,而writeTo()的输出却包含消息头.
System.out.println(((MimeMessage)message).getConntent());
一旦读取完毕邮件,要关闭store和folder的连接.
folder.colse(boolean);
store.colse();
传递给folder的close()方法的boolean参数表示是否清楚已删除的消息从而更新folder.
[/quote]
上面就是JavaMail邮件操作的基本的常用类,我觉得理解了这几个类的机制,基本就可以处理一般的邮件操作了.下面附上自己封装的一个MailUtil类.
支持带附件的邮件的发送/接收

package com.royzhou.mail;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
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.internet.MimeUtility;

public class MailUtil {
private Properties props = null; //系统属性
private String hostName = null; //邮件主机
private String userName = null; //邮箱用户名
private String password = null; //邮箱密码
private String from = null; //发件人地址
private String fromName = null; //发件人名字
private String to = null; //收件人地址
private String copyTo = null; //抄送人地址,使用分号分割
private boolean debug = true; //在发送邮件的过程中在console处显示过程信息,供调试使用
private String dateformat = "yy-MM-dd HH:mm:ss"; //默认的日前显示格式
private String attachPath = null; //附件下载后的存放目录

public MailUtil(String smtpHost) {
this.hostName = smtpHost;
props = System.getProperties();
props.put("mail.smtp.host", smtpHost); //设置SMTP主机
/*
* 需要经过授权,也就是有户名和密码的校验,这样才能通过验证
* 如果没有设置成true可能会抛出异常:553 authentication is required
*/
props.put("mail.smtp.auth", "true");
}

/**
* 文本邮件
* @param subject
* @param content
* @return
*/
public boolean sendTextMail(String subject, String content) {
boolean flag = false;
MyAuthenticator myAuthenticator = new MyAuthenticator(userName, password); //邮箱认证类
Session session = Session.getInstance(props, myAuthenticator); //邮件会话对象
session.setDebug(debug);
MimeMessage message = new MimeMessage(session); //定义消息对象
try {
if(fromName!=null) {
message.setFrom(new InternetAddress(from,fromName));
} else {
message.setFrom(new InternetAddress(from));
}
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
if(copyTo!=null) {
message.addRecipients(Message.RecipientType.CC, InternetAddress.parse(copyTo));
}
message.setSubject(subject);
message.setText(content);
message.saveChanges();
Transport.send(message);
flag = true;
System.out.println("发送邮件成功!");
} catch (Exception e) {
System.out.println("发送文本邮件发生异常..............");
e.printStackTrace();
}
return flag;
}

/**
* html邮件
* 支持多附件
* @param subject
* @param bodyContent
* @param attachFiles
* @return
*/
public boolean sendHTMLMail(String subject, String bodyContent, List attachFiles) {
boolean flag = false;
try {
MyAuthenticator myAuthenticator = new MyAuthenticator(userName, password); //邮箱认证类
Session session = Session.getInstance(props, myAuthenticator);
session.setDebug(debug);
MimeMessage mimeMessage = new MimeMessage(session);
Multipart multiPart = new MimeMultipart();
//设置标题
mimeMessage.setSubject(subject);
//设置正文
BodyPart bodyPart = new MimeBodyPart();
bodyPart.setContent("" + bodyContent, "text/html;charset=GB2312");
multiPart.addBodyPart(bodyPart);
//设置附件,下面定义的enc对象用来处理中文附件名,否则名称是中文的附件在邮箱里面显示的会是乱码,
sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
for(int i=0; i<attachFiles.size(); i++) {
bodyPart = new MimeBodyPart();
FileDataSource filed = new FileDataSource(attachFiles.get(i).toString());
bodyPart.setDataHandler(new DataHandler(filed));
bodyPart.setFileName("=?GBK?B?"+enc.encode(filed.getName().getBytes())+"?=");
multiPart.addBodyPart(bodyPart);
}
if(fromName!=null) {
mimeMessage.setFrom(new InternetAddress(from,fromName));
} else {
mimeMessage.setFrom(new InternetAddress(from));
}
mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
if(copyTo!=null) {
mimeMessage.addRecipients(Message.RecipientType.CC, InternetAddress.parse(copyTo));
}
mimeMessage.setContent(multiPart);
mimeMessage.saveChanges();

/**
* 如果没有使用认证类,
* 即Session session = Session.getInstance(props,null);
* 则可以通过下面方式来认证
* Transport transport = session.getTransport("smtp");
* transport.connect((String) props.get("mail.smtp.host"), userName,
password);
* transport.sendMessage(mimeMessage, mimeMessage.getRecipients((Message.RecipientType.TO)));
* transport.close();
*/
Transport.send(mimeMessage);
flag = true;
System.out.println("发送邮件成功!");
} catch (Exception e) {
System.out.println("发送HTML邮件发生异常..............");
e.printStackTrace();
}
return flag;
}

/**
* 获取邮件对象数组
* @return
*/
public Message[] receiveMails(){
Session session = Session.getInstance(props, null);
Message[] messages = null;
Store store = null;
Folder folder = null;
try {
store = session.getStore("pop3");
store.connect(hostName,userName,password);
System.out.println("链接成功.........");
folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
messages = folder.getMessages();
folder.close(true);
store.close();
} catch (Exception e) {
System.out.println("获取邮件列表发生错误...........");
e.printStackTrace();
}
return messages;
}

/**
* 获取邮件详细信息集合
* @return
* @throws Exception
*/
public List getMailInfos() throws Exception {
Session session = Session.getInstance(props, null);
Message[] messages = null;
Store store = null;
Folder folder = null;
List mailInfos = new ArrayList();
try {
store = session.getStore("pop3");
store.connect(hostName,userName,password);
System.out.println("链接成功.........");
folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
messages = folder.getMessages();
MimeMessage mimeMessage = null;
for(int i=0; i<messages.length; i++) {
mimeMessage = (MimeMessage)messages[i];
Part part = (Part) mimeMessage;
Map<String,String> mailInfo = new HashMap<String,String>();
mailInfo.put("from", getFrom(mimeMessage));
mailInfo.put("to", getMailAddress(mimeMessage, "to"));
mailInfo.put("cc", getMailAddress(mimeMessage, "cc"));
mailInfo.put("bcc", getMailAddress(mimeMessage, "bcc"));
mailInfo.put("subject", getSubject(mimeMessage));
mailInfo.put("bodyContent", getMailContent(part));
mailInfo.put("messageId", getMessageId(mimeMessage));
mailInfo.put("sendDate", getSentDate(mimeMessage));
mailInfo.put("replySign", String.valueOf(getReplySign(mimeMessage)));
mailInfo.put("isNew", String.valueOf(isNew(mimeMessage)));
mailInfo.put("isContainAttach", String.valueOf(isContainAttach(part)));
mailInfo.put("fileNames", saveAttachMent(part));
}
folder.close(true);
store.close();
} catch (Exception e) {
System.out.println("获取邮件详细信息发生错误...........");
e.printStackTrace();
}
return mailInfos;
}

/**
* 获得发件人的地址和姓名
*/
public String getFrom(MimeMessage mimeMessage) throws Exception {
InternetAddress address[] = (InternetAddress[]) mimeMessage.getFrom();
String from = address[0].getAddress();
if (from == null)
from = "";
String personal = address[0].getPersonal();
if (personal == null)
personal = "";
String fromaddr = personal + "<" + from + ">";
return fromaddr;
}

/**
* 获得邮件的收件人,抄送,和密送的地址和姓名,
* 根据所传递的参数的不同
* "to"----收件人
* "cc"---抄送人地址
* "bcc"---密送人地址
*/
public String getMailAddress(MimeMessage mimeMessage, String type) throws Exception {
String mailaddr = "";
String addtype = type.toUpperCase();
InternetAddress[] address = null;
if (addtype.equals("TO") || addtype.equals("CC")|| addtype.equals("BCC")) {
if (addtype.equals("TO")) {
address = (InternetAddress[]) mimeMessage.getRecipients(Message.RecipientType.TO);
} else if (addtype.equals("CC")) {
address = (InternetAddress[]) mimeMessage.getRecipients(Message.RecipientType.CC);
} else {
address = (InternetAddress[]) mimeMessage.getRecipients(Message.RecipientType.BCC);
}
if (address != null) {
for (int i = 0; i < address.length; i++) {
String email = address[i].getAddress();
if (email == null)
email = "";
else {
email = MimeUtility.decodeText(email);
}
String personal = address[i].getPersonal();
if (personal == null)
personal = "";
else {
personal = MimeUtility.decodeText(personal);
}
String compositeto = personal + "<" + email + ">";
mailaddr += "," + compositeto;
}
mailaddr = mailaddr.substring(1);
}
} else {
throw new Exception("Error emailaddr type!");
}
return mailaddr;
}

/**
* 获得邮件主题
*/
public String getSubject(MimeMessage mimeMessage) throws MessagingException {
String subject = "";
try {
subject = MimeUtility.decodeText(mimeMessage.getSubject());
if (subject == null)
subject = "";
} catch (Exception exce) {}
return subject;
}

/**
* 解析邮件,把得到的邮件内容保存到一个StringBuffer对象中,
* 解析邮件 主要是根据MimeType类型的不同执行不同的操作,一步一步的解析
*/
public String getMailContent(Part part) throws Exception {
StringBuffer bodyText = new StringBuffer();
String contenttype = part.getContentType();
int nameindex = contenttype.indexOf("name");
boolean conname = false;
if (nameindex != -1)
conname = true;
System.out.println("CONTENTTYPE: " + contenttype);
if (part.isMimeType("text/plain") && !conname) {
bodyText.append((String) part.getContent());
} else if (part.isMimeType("text/html") && !conname) {
bodyText.append((String) part.getContent());
} else if (part.isMimeType("multipart/*")) {
Multipart multipart = (Multipart) part.getContent();
int counts = multipart.getCount();
for (int i = 0; i < counts; i++) {
getMailContent(multipart.getBodyPart(i));
}
} else if (part.isMimeType("message/rfc822")) {
getMailContent((Part) part.getContent());
}
return bodyText.toString();
}

/**
* 获得邮件发送日期
*/
public String getSentDate(MimeMessage mimeMessage) throws Exception {
Date sentdate = mimeMessage.getSentDate();
SimpleDateFormat format = new SimpleDateFormat(dateformat);
return format.format(sentdate);
}

/**
* 判断此邮件是否需要回执,
* 如果需要回执返回"true",否则返回"false"
*/
public boolean getReplySign(MimeMessage mimeMessage) throws MessagingException {
boolean replysign = false;
String needreply[] = mimeMessage
.getHeader("Disposition-Notification-To");
if (needreply != null) {
replysign = true;
}
return replysign;
}

/**
* 获得此邮件的Message-ID
*/
public String getMessageId(MimeMessage mimeMessage) throws MessagingException {
return mimeMessage.getMessageID();
}

/**
* 【判断此邮件是否已读,如果未读返回返回false,反之返回true】
*/
public boolean isNew(MimeMessage mimeMessage) throws MessagingException {
boolean isnew = false;
Flags flags = ((Message) mimeMessage).getFlags();
Flags.Flag[] flag = flags.getSystemFlags();
for (int i = 0; i < flag.length; i++) {
if (flag[i] == Flags.Flag.SEEN) {
isnew = true;
System.out.println("seen Message.......");
break;
}
}
return isnew;
}

/**
* 判断此邮件是否包含附件
*/
public boolean isContainAttach(Part part) throws Exception {
boolean attachflag = false;
String contentType = part.getContentType();
if (part.isMimeType("multipart/*")) {
Multipart mp = (Multipart) part.getContent();
for (int i = 0; i < mp.getCount(); i++) {
BodyPart mpart = mp.getBodyPart(i);
String disposition = mpart.getDisposition();
if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)) || (disposition
.equals(Part.INLINE))))
attachflag = true;
else if (mpart.isMimeType("multipart/*")) {
attachflag = isContainAttach((Part) mpart);
} else {
String contype = mpart.getContentType();
if (contype.toLowerCase().indexOf("application") != -1)
attachflag = true;
if (contype.toLowerCase().indexOf("name") != -1)
attachflag = true;
}
}
} else if (part.isMimeType("message/rfc822")) {
attachflag = isContainAttach((Part) part.getContent());
}
return attachflag;
}

/**
* 【保存附件】
*/
public String saveAttachMent(Part part) throws Exception {
String fileNames = "";
String fileName = "";
if (part.isMimeType("multipart/*")) {
Multipart mp = (Multipart) part.getContent();
for (int i = 0; i < mp.getCount(); i++) {
BodyPart mpart = mp.getBodyPart(i);
String disposition = mpart.getDisposition();
if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)) || (disposition
.equals(Part.INLINE)))) {
fileName = mpart.getFileName();
//编码转换 ?gb2312或者?gbk的情况
if (fileName.toLowerCase().indexOf("gb") != -1) {
fileName = MimeUtility.decodeText(fileName);
}
saveFile(fileName, mpart.getInputStream());
} else if (mpart.isMimeType("multipart/*")) {
saveAttachMent(mpart);
} else {
fileName = mpart.getFileName();
if ((fileName != null)
&& (fileName.toLowerCase().indexOf("gb") != -1)) {
fileName = MimeUtility.decodeText(fileName);
saveFile(fileName, mpart.getInputStream());
}
}
fileNames += fileName + ",";
}
} else if (part.isMimeType("message/rfc822")) {
saveAttachMent((Part) part.getContent());
}
if(fileNames !=null && !"".equals(fileNames)) {
fileNames = fileNames.substring(0,fileNames.length()-1);
}
return fileNames;
}

/**
* 【真正的保存附件到指定目录里】
*/
private void saveFile(String fileName, InputStream in) throws Exception {
String osName = System.getProperty("os.name");
String storedir = getSaveAttachPath();
String separator = "";
if (osName == null)
osName = "";
if (osName.toLowerCase().indexOf("win") != -1) {
separator = "\\";
if (storedir == null || storedir.equals(""))
storedir = "c:\\tmp";
} else {
separator = "/";
storedir = "/tmp";
}
File storefile = new File(storedir + separator + fileName);
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(storefile));
bis = new BufferedInputStream(in);
int c;
while ((c = bis.read()) != -1) {
bos.write(c);
bos.flush();
}
} catch (Exception exception) {
exception.printStackTrace();
throw new Exception("文件保存失败!");
} finally {
bos.close();
bis.close();
}
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getFrom() {
return from;
}

public void setFrom(String from) {
this.from = from;
}

public String getTo() {
return to;
}

public void setTo(String to) {
this.to = to;
}

public String getCopyTo() {
return copyTo;
}

public void setCopyTo(String copyTo) {
this.copyTo = copyTo;
}

public String getFromName() {
return fromName;
}

public void setFromName(String fromName) {
this.fromName = fromName;
}

public boolean isDebug() {
return debug;
}

public void setDebug(boolean debug) {
this.debug = debug;
}

public String getDateformat() {
return dateformat;
}

public void setDateformat(String dateformat) {
this.dateformat = dateformat;
}

public String getSaveAttachPath() {
return attachPath;
}

public void setSaveAttachPath(String saveAttachPath) {
this.attachPath = saveAttachPath;
}
}

/**
* 邮箱认证类
* @author Administrator
*
*/
class MyAuthenticator extends Authenticator {
private String strUser;
private String strPwd;

public MyAuthenticator(String user, String password) {
this.strUser = user;
this.strPwd = password;
}

protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(strUser, strPwd);
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值