Http协议、HttpClient

news2025/1/29 13:48:01

HTTP请求协议包

http服务器

HTTP Server 也是我们常说的Web服务器

在网络中传递的信息都是以【二进制】形式存在的,接收方在接收信息后需要把二进制数据编译为原数据。

弊端:HTTP协议无法实现服务器主动向客户端发起消息。

http服务器需要

1、可以接收来自浏览器发送的Http请求协议包,并自动对Http请求协议包内容解析

2、并将定位的文件内容给写入到Http写入到HTTP响应协议包


请求协议包

在基于B/S结构下,互联网通信过程中,所有在网络中传递信息都是保存在HTTP网络协议包

浏览器 向 WEB服务器发送数据的时候,这个发送的数据需要遵循一套标准,这套标准中规定了发送的数据具体格式。

HTTP请求协议包自上而下划分为4个空间


请求行

请求行主要提供:请求地址请求方式

get方式参数存放在请求数据包的请求行的URI字段中,以?开始以param=value&parame2=value2的形式附加在URI字段之后


请求头

请求头的作用有以下几个方面:

  1. 进行身份验证:请求头中的Authorization字段常用于传递身份验证凭证,如基本认证(Basic Authentication)或令牌(Token)。服务器可以根据这些凭证对请求进行身份验证,以确定是否允许访问受保护的资源。

  2. 控制请求体的格式:Content-Type字段指定了请求体中数据的格式类型,如JSON、表单数据等。服务器可以根据Content-Type来正确解析请求体中的数据。

  3. 提供跳转来源信息:Referer字段指示了当前请求是从哪个URL页面发起的,可以帮助服务器识别请求的来源。

  4. User-Agent:标识客户端使用的浏览器和操作系统信息

如果请求方式是get,那么请求参数信息会保存到请求头中

x-forwarded-for:是一个 HTTP 扩展头部,主要是为了让 Web 服务器获取访问用户的真实 IP 地址

X-Forwarded-For 请求头格式

X-Forwarded-For: client, proxy1, proxy2

可以看到,XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP(用户真实 IP),然后是每一级代理设备的 IP。

 public static String getIpAddr(HttpServletRequest request) {
     if (request == null) {
         return "unknown";
     }
     String ip = request.getHeader("x-forwarded-for");
     if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
         ip = request.getHeader("Proxy-Client-IP");
     }
     if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
         ip = request.getHeader("X-Forwarded-For");
     }
     if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
         ip = request.getHeader("WL-Proxy-Client-IP");
     }
     if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
         ip = request.getHeader("X-Real-IP");
     }
     if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
         ip = request.getRemoteAddr();
     }
     return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
 }

CONTENT_TYPE


空白行

没有任何内容。是用来区分"请求头"和"请求体"的


请求体

如果请求方式是post,那么请求参数信息会保存到请求体中


HTTP响应协议包

响应协议包

WEB服务器 向 浏览器发送数据的时候,这个发送的数据需要遵循一套标准,这套标准中规定了发送的数据具体格式。


状态行

HTTP状态码


 响应头

Content-Type:指定浏览器采用对应编译器对响应体二进制数据进行解析


空白行

区分"响应头"和"响应体"


响应体

可能是被访问静态资源文件内容,可能是动态资源文件的运行结果


HttpClient

多种请求方法

HTTP 请求可以使用多种请求方法。

HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。

HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。

1GET请求指定的页面信息,并返回实体主体。
2HEAD类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
3POST向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。
4PUT从客户端向服务器传送的数据取代指定的文档的内容。
5DELETE请求服务器删除指定的页面。
6CONNECTHTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
7OPTIONS允许客户端查看服务器的性能。
8TRACE回显服务器收到的请求,主要用于测试或诊断。
9PATCH是对 PUT 方法的补充,用来对已知资源进行局部更新 。

HttpClient

HttpClient 是Apache HttpComponents 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

创建HttpClient对象。
创建请求方式的实例。创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
添加请求参数。如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
发送Http请求。调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
获取返回的内容。调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
释放资源。无论执行方法是否成功,都必须释放资源;
spring boot集成HttpClient

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
</dependency>

如果是maven工程,引入如下maven依赖

<!-- 此处使用的是 5.x 版本,可以根据自身情况引入版本 -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.1.1</version>
</dependency>

HttpClient 响应处理

在 HttpClient 中把响应封装成了 CloseableHttpResponse 对象,在此对象中可以获取如下数据:

  1. getCode() 获取响应状态
  2. getEntity() 获取响应数据

HttpClient 提供了 EntityUtils工具类,可以很好的把 响应的 HttpEntity 转换为 字节数组或者字符串

// 转换为字符串
EntityUtils.toString(response.getEntity());

// 转换为字节数组
EntityUtils.toByteArray(response.getEntity());

1


GET请求

GET请求的所有参数是直接拼接在 URL 后面的,在浏览器地址栏中可见。

GET请求url编码

使用GET请求时会将参数附加到URL的查询字符串中,以便将其发送给服务器。但是GET请求只接受ASCII字符作为参数值。

解决办法:

GET请求会把参数进行URL编码(也称为百分号编码或URL转义),以确保特殊字符不会干扰URL结构。
 public static void main(String[] args) throws Exception {
     //获取默认配置的 HttpClient
     CloseableHttpClient httpclient = HttpClients.createDefault();
     //设置单个请求的配置信息
     RequestConfig requestConfig = RequestConfig.custom()
             // 设置连接超时时间
             .setConnectTimeout(Timeout.of(3000, TimeUnit.MILLISECONDS))
             // 设置响应超时时间
             .setResponseTimeout(3000, TimeUnit.MILLISECONDS)
             // 设置从连接池获取链接的超时时间
             .setConnectionRequestTimeout(3000, TimeUnit.MILLISECONDS)
             .build();
     // 请求路径及参数
     String name = URLEncoder.encode("张三", "utf-8");
     String url = "http://localhost:10010/user/params?age=20&name=" + name;
     HttpGet httpget = new HttpGet(url);
     httpget.setConfig(requestConfig);
     // 调用 HttpClient 的 execute 方法执行请求
     CloseableHttpResponse response = httpclient.execute(httpget);
     try {
         HttpEntity entity = response.getEntity();
         String result = EntityUtils.toString(entity);
         EntityUtils.consume(entity);
         System.out.println(result);
     } finally {
         response.close();
     }
 }

通过 URIBuilder 构建请求路径


POST 请求

使用POST请求时,参数通过HTTP消息体进行传输,并不会直接显示在URL上。

POST支持多种编码方式。

熟悉常见的 Content-Type 类型对于处理 POST 请求至关重要,它可以帮助我们更好地使用和解决问题。

请求头字段有 Content-Type

具体的数据格式和编码方式由 Content-Type 字段确定。服务端会根据 Content-Type 字段使用不同的方式对请求体进行解析。

1、最常用的POST数据编码方式是 application/x-www-form-urlencoded ,它与GET相似,也会对非ASCII字符和特殊字符进行URL编码。

这种方式下,参数按照键值对形式出现在消息正文中,并用&符号连接起来,同时以 = 分隔键和值,

public static void main(String[] args) throws Exception {
    // 创建 ContentType 对象为 form 表单模式
    ContentType contentType = ContentType.create("application/x-www-form-urlencoded", StandardCharsets.UTF_8);
    String url = "http://localhost:10010/user/body";
    HttpPost httpPost = new HttpPost(url);
    //设置请求头,添加到 HttpPost 头中
    httpPost.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
    //HttpPost请求 要传的参数 这里先将参数封装起来
    List<BasicNameValuePair> params = new ArrayList<>();
    params.add(new BasicNameValuePair("name", "张三"));
    params.add(new BasicNameValuePair("age", "20"));
    //这一步才是 为post 请求设置参数  这是创建表单的实体对象 设置当前参数的编码格式
    //设置请求数据,把HttpEntity 设置到 HttpPost 中
    httpPost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
    //获取默认配置的 HttpClient
    CloseableHttpClient httpclient = HttpClients.createDefault();
    //调用 HttpClient 的 execute 方法执行请求
    CloseableHttpResponse response = httpclient.execute(httpPost);
    try {
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity);
        EntityUtils.consume(entity);
        System.out.println(result);
    } finally {
        response.close();
    }
}

最主要的就是consume()这个方法,其功能就是关闭HttpEntity是的流,

2、使用 application/json 编码方式,可以将数据序列化成一个 JSON 字符串作为请求主体。

application/json 是一种常用的HTTP请求头,用于指定请求的内容类型为JSON格式

public static void main(String[] args) throws Exception {
    // 创建 ContentType 对象为 form 表单模式
    ContentType contentType = ContentType.create("application/json", StandardCharsets.UTF_8);
    String url = "http://localhost:10010/user/body";
    HttpPost httpPost = new HttpPost(url);
    //设置请求头,添加到 HttpPost 头中
    httpPost.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
    //设置报文和通讯格式
    String jsonParam = "{\"name\":\"John\", \"age\":30}";
    StringEntity stringEntity = new StringEntity(jsonParam, StandardCharsets.UTF_8);
    httpPost.setEntity(stringEntity);
    //获取默认配置的 HttpClient
    CloseableHttpClient httpclient = HttpClients.createDefault();
    //调用 HttpClient 的 execute 方法执行请求
    CloseableHttpResponse response = httpclient.execute(httpPost);
    try {
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity);
        EntityUtils.consume(entity);
        System.out.println(result);
    } finally {
        response.close();
    }
}

3、multipart/form-data

这种编码方式主要用于文件上传,可以有效地传输二进制(非字母数字)数据,同时还可以携带其他信息

最常见的 POST 提交数据的方式,也是典型的key-val键值对,无需转化,在request中直接获取,框架也会将同名属性封装到接收对象中,支持文件流上传

如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。

但是如果有type=file的话,就要用到multipart/form-data了。

4、 application/octet-stream

 二进制流数据(如常见的文件下载)


HTTP 中的 POST 请求的数据是包含在请求体中的。在 HttpClient 中 POST 请求发送数据是通过,调用 HttpPost 类的 setEntity(HttpEntity entity) 方法设置消息内容的

发送 JSON 数据


java 发邮件

SMTP

SMTP是一种提供可靠且有效的电子邮件传输的协议(只负责发送,不负责接收)

POP3协议,读取邮件的协议

用户必须首先设置 SMTP 服务器,然后才能配置电子邮件客户端与其连接。完成此操作后,用户按下电子邮件上的“发送”按钮,并在客户端和服务器之间建立 SMTP 连接以允许发送电子邮件。SMTP 连接建立在传输控制协议 (TCP)连接之上。

SMTP (Simple Mail Transfer Protocol),即简单邮件传输协议,默认端口是25,通过SSL协议加密之后的默认端口是465

465端口(SMTP SSL):465端口是为SMTP SSL(SMTP-over-SSL)协议服务开放的,这是SMTP协议基于SSL安全协议之上的一种变种协议,它继承了SSL安全协议的非对称加密的高度安全可靠性,可防止邮件泄露。

SMTPS和SMTP协议一样,也是用来发送邮件的,只是更安全些,防止邮件被黑客截取泄露,还可实现邮件发送者抗抵赖功能。防止发送者发送之后删除已发邮件,拒不承认发送过这样一份邮件。

SMTP 服务器,如smtp.163.com、smtp.qq.com

25端口(SMTP):25端口为SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)服务所开放的,是用于发送邮件。

如今绝大多数邮件服务器都使用该协议。当你给别人发送邮件时,你的机器的某个动态端口(大于1024)就会与邮件服务器的25端口建立一个连接,你发送的邮件就会通过这个连接传送到邮件服务器上,保存起来。


POP3

POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本,是TCP/IP协议族中的一员,默认端口是110,通过SSL协议加密之后的默认端口是995

POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,但是对邮件的操作并不会反馈到邮箱服务器上。


IMAP

IMAP (Internet Mail Access Protocol),即交互式邮件存取协议,是一个应用层协议,默认端口是143,通过SSL协议加密之后的默认端口是993

开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动作。

IMAP协议:即Internet Message Access Protocol(互联网邮件访问协议),您可以通过这种协议从邮件服务器上获取邮件的信息、下载邮件等


邮箱授权码

现在邮箱大多为邮箱客户端设置了独立密码或授权码(即通过Smtp方式发送邮件密码处不是填邮箱登录密码,而是要填授权码)

如下:

进去后,点击开启。开启完成后状态变为”已开启“


javax.mail

<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>

1


commons-email

commons-email它构建在Java Mail API 之上,主要是为了简化它。

pom文件依赖

    <dependency>
         <groupId>org.apache.commons</groupId>
          <artifactId>commons-email</artifactId>
          <version>1.5</version>
    </dependency>

Commons-Email的核心是Email类,它是一个抽象类,提供了发送邮件的核心功能。具体有以下几个实现类

SimpleEmail:发送简单邮件,即纯文本邮件

public static void main(String[] args) throws EmailException {
        Email email = new SimpleEmail();
        //设置邮箱服务器,在这里我使用的163邮箱
        email.setHostName("smtp.163.com");
        //填写邮件服务器端口:465和25选填
        email.setSmtpPort(25);
        //开启debug日志
        //email.setDebug(true);
        //设置用户名(邮箱)和授权码(授权码是用于登录第三方邮件客户端的专用密码)
        //邮箱开启授权只需要登陆邮件,在里边设置一下就行了.
        email.setAuthenticator(new DefaultAuthenticator("your_email@163.com", "your_auth_password"));
        //开启ssl连接
        email.setSSLOnConnect(true);
        //填写发送者的邮箱
        email.setFrom("your_email@163.com");
        //填写发送日期
        email.setSentDate(new Date());
        //填写邮件标题
        email.setSubject("TestMail");
        //邮件内容
        email.setMsg("你好这是测试邮件");

        //填写发送人,我用发给的是新浪邮箱,除了发送给QQ邮箱不行之外,你随意
        //注:发给QQ邮箱被退回了(腾讯邮箱服务对邮件检查比较严格导致)
        email.addTo("target_email_address@sina.com");
        //发送
        email.send();
    }

MultiPartEmail
MultiPartEmail类通常用来发送流媒体类型的邮件,允许附件和文本类型数据一起发送。

public static void main(String[] args) throws EmailException {
        //创建附件
        EmailAttachment attachment = new EmailAttachment();
        //填写附件位置
        attachment.setPath("path_to_your_attachment/attachment.txt");
        attachment.setDisposition(EmailAttachment.ATTACHMENT);
        //填写附件描述
        attachment.setDescription("附件描述");
        //填写附件名称,要与上面一致填写路径中的附件名称一致
        //要不然收到附件的时候会有问题
        attachment.setName("attachment.txt");

        //创建邮件信息
        MultiPartEmail email = new MultiPartEmail();
        email.setHostName("smtp.163.com");
        email.setSmtpPort(465);
        email.setDebug(true);
        //填写授权码和用户名
        email.setAuthenticator(new DefaultAuthenticator("your_email@163.com", "your_auth_password"));
        email.setSSLOnConnect(true);

        email.setFrom("your_email@163.com");
        email.addTo("target_email_address@sina.com");

        email.setSubject("附件");
        email.setMsg("我的附件发给你了.");

        // 添加附件
        email.attach(attachment);

        //发送邮件
        email.send();
    }
email.addCc("抄送人");

HtmlEmail
HtmlEmail类通常用来发送html格式的邮件,他也支持邮件携带普通文本、附件和内嵌图片。
ImageHtmlEmail
ImageHtmlEmail类通常是用来发送Html格式并内嵌图片的邮件,它拥有所有HtmlEmail的功能,但是图片主要是以html内嵌的为主。


POP3接收邮件

接收邮件常用的协议有pop3,imap和exchange。exchange是微软的邮箱协议,Jakarta Mail暂不支持。

JavaMail API中定义了一个java.mail.Store类,应用程序调用这个类的方法就可以获得用户邮箱中的各个邮件夹的信息。

javaMail中的使用Folder对象表示邮件夹,通过Folder对象的方法应用程序进而又可以获得该邮件夹中的所有邮件信息。

而每封邮件信息JavaMail又分别使用了一个Message对象进行封装

用于返回指定名称的邮件夹(Folder)对象

 Folder folder = store.getFolder("inbox");
  1. 通过POP3协议获得的Store对象调用这个方法时,邮件夹名称只能指定为"INBOX"。
public class StoreMail {
 final static String USER = "robot"; // 用户名
 final static String PASSWORD = "password520"; // 密码
 public final static String MAIL_SERVER_HOST = "mail.***.com"; // 邮箱服务器
 public final static String TYPE_HTML = "text/html;charset=UTF-8"; // 文本内容类型
 public final static String MAIL_FROM = "[email protected]"; // 发件人
 public final static String MAIL_TO = "[email protected]"; // 收件人
 public final static String MAIL_CC = "[email protected]"; // 抄送人
 public final static String MAIL_BCC = "[email protected]"; // 密送人
 public static void main(String[] args) throws Exception {
  // 创建一个有具体连接信息的Properties对象
  Properties prop = new Properties();
  prop.setProperty("mail.debug", "true");
  prop.setProperty("mail.store.protocol", "pop3");
  prop.setProperty("mail.pop3.host", MAIL_SERVER_HOST);
  // 1、创建session
  Session session = Session.getInstance(prop);
  // 2、通过session得到Store对象
  Store store = session.getStore();
  // 3、连上邮件服务器
  store.connect(MAIL_SERVER_HOST, USER, PASSWORD);
  // 4、获得邮箱内的邮件夹
  Folder folder = store.getFolder("INBOX");
  folder.open(Folder.READ_ONLY);
  // 获得邮件夹Folder内的所有邮件Message对象
  Message[] messages = folder.getMessages();
  for (int i = 0; i < messages.length; i++) {
   String subject = messages[i].getSubject();
   String from = (messages[i].getFrom()[0]).toString();
   System.out.println("第 " + (i + 1) + "封邮件的主题:" + subject);
   System.out.println("第 " + (i + 1) + "封邮件的发件人地址:" + from);
  }
  // 5、关闭
  folder.close(false);
  store.close();
 }
}
  1. //由于POP3协议无法获知邮件的状态,所以getUnreadMessageCount()得到的是收件箱的邮件总数

  2. System.out.println("未读邮件数:" + folder.getUnreadMessageCount());

文件夹上的getPermanentFlags方法返回一个Flags对象,该对象包含该文件夹实现支持的所有标志。

mail.pop3.host

Flags.Flag.SEEN

邮件阅读标记,标识邮件是否已被阅读。


imap接收邮件

同样需要先开启授权码

public final static String MAIL_SERVER_HOST = "imap.163.com"; // 邮箱服务器

public static void main(String[] args) throws Exception {
    // 创建一个有具体连接信息的Properties对象
    Properties prop = new Properties();
      prop.setProperty("mail.debug", "true");
    //指定接收的邮件协议
    prop.setProperty("mail.store.protocol", "imap");
    prop.setProperty("mail.imap.host", MAIL_SERVER_HOST);
    prop.setProperty("mail.imap.port", "143");
    // 1、创建session
    Session session = Session.getInstance(prop);
    // 2、通过session得到Store对象
    Store store = session.getStore("imaps");
    // 3、连上邮件服务器
    store.connect(MAIL_SERVER_HOST, USER, PASSWORD);
    // 4、获得邮箱内的邮件夹 POP协议的话,这里只能是INBOX
    Folder folder = store.getFolder("INBOX");
    System.out.println("INBOX exist:" + folder.exists());
    //
    Folder defaultFolder = store.getDefaultFolder();
    Folder[] folders = defaultFolder.list("*");
    for (Folder folder1 : folders) {
        IMAPFolder imapFolder = (IMAPFolder) folder1;
        //javamail中使用id命令校验chekOpened,去掉
        imapFolder.doCommand(new IMAPFolder.ProtocolCommand() {
            @Override
            public Object doCommand(IMAPProtocol p) throws ProtocolException {
                p.id("FUTONG");
                return null;
            }
        });
    }
    //以只读方式打开收件箱
    folder.open(Folder.READ_ONLY);
    System.out.println("邮件总数:" + folder.getMessageCount());
    //由于POP3协议无法获知邮件的状态,所以getUnreadMessageCount()得到的是收件箱的邮件总数
    System.out.println("未读邮件数:" + folder.getUnreadMessageCount());
    //由于POP3协议无法获知邮件的状态,所以下面得到的结果始终都是为0
    System.out.println("删除邮件数:" + folder.getDeletedMessageCount());
    System.out.println("新邮件数:" + folder.getNewMessageCount());
    // 获得邮件夹Folder内的所有邮件Message对象
    Message[] messages = folder.getMessages();
    for (int i = 0; i < messages.length; i++) {
        String subject = messages[i].getSubject();
        String from = (messages[i].getFrom()[0]).toString();
        System.out.println("第 " + (i + 1) + "封邮件的主题:" + subject);
        System.out.println("第 " + (i + 1) + "封邮件的发件人地址:" + from);
        System.out.println("第 " + (i + 1) + "读写标识:" + messages[i].getFlags().contains(Flags.Flag.SEEN));
    }
    // 5、关闭
    folder.close(false);
    store.close();
}

1


附件下载

// 获得邮件夹Folder内的所有邮件Message对象
Message[] messages = folder.getMessages();
for (int i = 0; i < messages.length; i++) {
    String subject = messages[i].getSubject();
    String from = (messages[i].getFrom()[0]).toString();
    System.out.println("第 " + (i + 1) + "封邮件的主题:" + subject);
    System.out.println("第 " + (i + 1) + "封邮件的发件人地址:" + from);
    System.out.println("第 " + (i + 1) + "读写标识:" + messages[i].getFlags().contains(Flags.Flag.SEEN));
    Part mp = (Part) messages[i];
    boolean containAttach = isContainAttach(mp);
    if (containAttach) {
        //包含附件,进行附件下载
        System.out.println("第 " + (i + 1) + "封邮件,包含附件");
        saveAttachMent(mp, "C:\\Users\\Administrator\\Desktop\\Attach");
    }
}

这个不一定是static方法,我是为了在main方法里测试,才这么写的

    /**
     *  * 判断此邮件是否包含附件
     *
     * @throws MessagingException
     */
    public static boolean isContainAttach(Part part) throws Exception {
        boolean attachflag = false;
        String contentType = part.getContentType();
        if (part.isMimeType("multipart/*")) {
            Multipart mp = (Multipart) part.getContent();
            // 获取附件名称可能包含多个附件
            for (int j = 0; j < mp.getCount(); j++) {
                BodyPart mpart = mp.getBodyPart(j);
                String disposition = mpart.getDescription();
                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;
    }


    /**
     *  * 【保存附件】
     *
     * @throws Exception
     * @throws IOException
     * @throws MessagingException
     * @throws Exception
     */
    public static void saveAttachMent(Part part, String saveAttachPath) throws Exception {
        String fileName = "";
        if (part.isMimeType("multipart/*")) {
            Multipart mp = (Multipart) part.getContent();
            for (int j = 0; j < mp.getCount(); j++) {
                BodyPart mpart = mp.getBodyPart(j);
                String disposition = mpart.getDescription();
                if ((disposition != null)
                        && ((disposition.equals(Part.ATTACHMENT)) || (disposition
                        .equals(Part.INLINE)))) {
                    fileName = mpart.getFileName();
                    if (fileName.toLowerCase().indexOf("GBK") != -1) {
                        fileName = MimeUtility.decodeText(fileName);
                    }
                    saveFile(fileName, mpart.getInputStream(), saveAttachPath);
                } else if (mpart.isMimeType("multipart/*")) {
                    fileName = mpart.getFileName();
                } else {
                    fileName = mpart.getFileName();
                    if ((fileName != null)) {
                        fileName = MimeUtility.decodeText(fileName);
                        saveFile(fileName, mpart.getInputStream(), saveAttachPath);
                    }
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            saveAttachMent((Part) part.getContent(), saveAttachPath);
        }
    }

    /**
     *  * 【真正的保存附件到指定目录里】
     */
    private static void saveFile(String fileName, InputStream in, String storedir) throws Exception {
        String osName = System.getProperty("os.name");
        String separator = "";
        if (osName == null)
            osName = "";
        if (osName.toLowerCase().indexOf("win") != -1) {
            // 如果是window 操作系统
            separator = "/";
            if (storedir == null || storedir.equals(""))
                storedir = "c:\tmp";
        } else {
            // 如果是其他的系统
            separator = "/";
            storedir = "/tmp";
        }
        File strorefile = new File(storedir + separator + fileName);
        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(strorefile));
            bis = new BufferedInputStream(in);
            int c;
            while ((c = bis.read()) != -1) {
                bos.write(c);
                bos.flush();
            }
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            bos.close();
            bis.close();
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1383419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Apollo之原理和使用讲解

文章目录 1 Apollo1.1 简介1.1.1 背景1.1.2 简介1.1.3 特点 1.2 基础模型1.3 Apollo 四个维度1.3.1 application1.3.2 environment1.3.3 cluster1.3.4 namespace 1.4 本地缓存1.5 客户端设计1.5.1 客服端拉取原理1.5.2 配置更新推送实现 1.6 总体设计1.7 可用性考虑 2 操作使用…

鸿蒙应用开发尝鲜:初识HarmonyOS

初识HarmonyOS 来源:华为官方网站 : https://developer.huawei.com/ 相信大家对鸿蒙应用开发也不在陌生,很多身处互联网行业或者不了解的人们现在也一定都听说过华为鸿蒙.这里我将不再说废话,直接步入正题 鸿蒙应用开发语言 HarmonyOS应用开发采用的是ArkTS语言,ArkTS是在Typ…

sublime中添加GBK编码模式

当写代码的中文注释时&#xff0c;编译代码出现如下错误&#xff1a; 解决办法&#xff0c;添加GBK模式&#xff1a; &#xff11;. 点击Preferences -> Package Control&#xff1a; 2. 在跳出来的搜索框里搜索conver, 点击ConverToUTF8 3. File左上角会多出GBK的选项 由…

arcgis javascript api4.x加载天地图web墨卡托(wkid:3857)坐标系

效果&#xff1a; 示例代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&quo…

HarmonyOS-LocalStorage:页面级UI状态存储

管理应用拥有的状态概述 上一个章节中介绍的装饰器仅能在页面内&#xff0c;即一个组件树上共享状态变量。如果开发者要实现应用级的&#xff0c;或者多个页面的状态数据共享&#xff0c;就需要用到应用级别的状态管理的概念。ArkTS根据不同特性&#xff0c;提供了多种应用状态…

LeetCode讲解篇之2280. 表示一个折线图的最少线段数

文章目录 题目描述题解思路题解代码 题目描述 题解思路 折线图中如果连续的线段共线&#xff0c;那么我们可以可以将其合并成一条线段 首先将坐标点按照横坐标升序排序 然后遍历数组 我们可以通过计算前一个线段的斜率和当前线段的斜率来判断是否共线 如果二者相等&#x…

[NSSCTF Round#16 Basic]RCE但是没有完全RCE

[NSSCTF Round#16 Basic]RCE但是没有完全RCE 第一关 <?php error_reporting(0); highlight_file(__file__); include(level2.php); if (isset($_GET[md5_1]) && isset($_GET[md5_2])) {if ((string)$_GET[md5_1] ! (string)$_GET[md5_2] && md5($_GET[md…

【剑指offer】数组中重复的数字

&#x1f451;专栏内容&#xff1a;力扣刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、题目描述1、题目2、示例 二、题目分析1、双重for循环2、for-each 循环3、set集合 一、题目描述 1、题目 剑指offer&a…

2024.1.13力扣每日一题——构造限制重复的字符串

2024.1.13 题目来源我的题解方法一 计数模拟 题目来源 力扣每日一题&#xff1b;题序&#xff1a;2182 我的题解 方法一 计数模拟 因为字符串s由小写字母构成&#xff0c;因此使用一个int[26]的数组保存每个字符的数量&#xff0c;然后从最大的字符开始构造结果字符串sb&…

自编C++题目——输入程序

预估难度 简单 题目描述 小明编了一个输入程序&#xff0c;当用户的输入之中有<时&#xff0c;光标移动到最右边&#xff1b;当输入有>时&#xff0c;光标移动到最左边&#xff0c;当输入有^时&#xff0c;光标移动到前一个字符&#xff0c;当输入为#时&#xff0c;清…

深度学习中的稀疏注意力

稀疏注意力 文章目录 一、稀疏注意力的特点 1. 单头注意力&#xff08;Single-Head Attention&#xff09; 2. 多头注意力&#xff08;Multi-Head Attention&#xff09; 3. 稀疏注意力&#xff08;Sparse Attention&#xff09; 二、稀疏注意力的示意图 三、与Flash Attention…

React项目实战--------极客园项目PC端

项目介绍&#xff1a;主要将学习到的项目内容进行总结&#xff08;有需要项目源码的可以私信我&#xff09; 关于我的项目的配置如下&#xff0c;请注意下载的每个版本不一样&#xff0c;写的api也不一样 一、项目介绍 1.资料 1&#xff09;短信接收&M端演示&#xff1a…

Python爬虫学习笔记(一)---Python入门

一、pycharm的安装及使用二、python的基础使用1、字符串连接2、单双引号转义3、换行4、三引号跨行字符串5、命名规则6、注释7、 优先级not>and>or8、列表&#xff08;list&#xff09;9、字典&#xff08;dictionary&#xff09;10、元组&#xff08;tuple&#xff09;11…

1.10 Unity中的数据存储 XML

一、XML 1.介绍 XML是一个文档后缀名是*.xmlXML是一个特殊格式的文档XML是可扩展的标记性语言XML是Extentsible Markup Language的缩 写XML是由万维网联盟(W3C)创建的标记语言&#xff0c;用于定义编码人类和机器可以读取的文档的语法。它通过使用定义文档结构的标签以及如何…

力扣 第 121 场双周赛 解题报告 | 珂学家 | 数位DP

前言 整体评价 T3, T4 都是典题 T1. 大于等于顺序前缀和的最小缺失整数 思路: 模拟 class Solution { public:int missingInteger(vector<int>& nums) {set<int> s(nums.begin(), nums.end());int acc nums[0];for (int i 1; i < nums.size(); i) {if …

非常非常实用!不能错过,独家原创,9种很少人听过,但却实用的混沌映射!!!以鲸鱼混沌映射为例,使用简便

很多人在改进的时候&#xff0c;想着增加混沌映射&#xff0c;增加初始种群的多样性&#xff0c;可是&#xff0c;大多数论文中常见的映射&#xff0c;都被别人使用了&#xff0c;或者不知道被别人有没有使用&#xff0c; 本文介绍9种很少人知道&#xff0c;但非常实用混沌映射…

【Maven】006-Maven 依赖传递和依赖冲突

【Maven】006-Maven 依赖传递和依赖冲突 文章目录 【Maven】006-Maven 依赖传递和依赖冲突一、依赖传递1、概述2、案例&#xff1a;jackson 依赖引入依赖Maven 仓库详情页IDEA 中查看 Maven 依赖关系 二、依赖冲突1、概述2、冲突解决的两种方式 一、依赖传递 1、概述 概念&am…

【并发】共享模型之管程

共享模型之管程 共享问题 package 并发;public class Test1 {static int a0;public static void main(String[] args) throws InterruptedException {Thread t1new Thread(new Runnable() {Overridepublic void run() {for(int i0;i<5000;i){a;}}});Thread t2new Thread(n…

Linux scp命令 服务器之间通讯

目录 一. scp命令简介二. 本地服务器文件传输到远程服务器三. 本地服务器文件夹传输到远程服务器 一. scp命令简介 scp&#xff08;Secure Copy Protocol&#xff09;是用于在Unix或Linux系统之间安全地复制文件或目录的命令。 它使用SSH&#xff08;Secure Shell&#xff09;…

python基础-文件读写

总结&#xff1a; 文件操作掌握一个函数open&#xff0c;三个方法read,write,close; 1、操作文件的思路 打开文件&#xff1b; 注意&#xff1a; 计算机操作文件的步骤基本固定&#xff01;读取文件&#xff1b; 关闭文件&#xff1b; 2、 操作文件 在Python中…