在开发中,我们使用的比较多的HTTP请求方式基本上就是GET、POST。其中GET用于从服务器获取数据,POST主要用于向服务器提交一些表单数据,例如文件上传等。而我们在使用HTTP请求时中遇到的比较麻烦的事情就是构造文件上传的HTTP报文格式,这个格式虽说也比较简单,但也比较容易出错。今天我们就一起来学习HTTP POST的报文格式以及通过Java来模拟文件上传的请求。

首先我们来看一个POST的报文请求,然后我们再来详细的分析它。

POST报文格式

  1. POST /api/feed/ HTTP/1.1
  2. Accept-Encoding: gzip
  3. Content-Length: 225873
  4. Content-Type: multipart/form-data; boundary=OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
  5. Host: www.myhost.com
  6. Connection: Keep-Alive
  7. --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
  8. Content-Disposition: form-data; name="lng"
  9. Content-Type: text/plain; charset=UTF-8
  10. Content-Transfer-Encoding: 8bit
  11. 116.361545
  12. --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
  13. Content-Disposition: form-data; name="lat"
  14. Content-Type: text/plain; charset=UTF-8
  15. Content-Transfer-Encoding: 8bit
  16. 39.979006
  17. --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
  18. Content-Disposition: form-data; name="images"; filename="/storage/emulated/0/Camera/jdimage/1xh0e3yyfmpr2e35tdowbavrx.jpg"
  19. Content-Type: application/octet-stream
  20. Content-Transfer-Encoding: binary
  21. 这里是图片的二进制数据
  22. --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp--

这里我们提交的是经度、纬度和一张图片(图片数据比较长,而且比较杂乱,这里省略掉了)。

格式分析

请求头分析

我们先看报文格式中的第一行:
 
  1. POST /api/feed/ HTTP/1.1

这一行就说明了这个请求的请求方式,即为POST方式,要请求的子路径为/api/feed/,例如我们的服务器地址为www.myhost.com,然后我们的这个请求的完整路径就是www.myhost.com/api/feed/,最后说明了HTTP协议的版本号为1.1。

  1. Accept-Encoding: gzip
  2. Content-Length: 225873
  3. Content-Type: multipart/form-data; boundary=OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
  4. Host: www.myhost.com
  5. Connection: Keep-Alive

这几个header的意思分别为服务器返回的数据需要使用gzip压缩、请求的内容长度为225873、内容的类型为"multipart/form-data"、请求参数分隔符(boundary)为OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp、请求的根域名为www.myhost.com、HTTP连接方式为持久连接( Keep-Alive)。

 
其中这里需要注意的一点是分隔符,即boundary。boundary用于作为请求参数之间的界限标识,例如参数1和参数2之间需要有一个明确的界限,这样服务器才能正确的解析到参数1和参数2。但是分隔符并不仅仅是boundary,而是下面这样的格式:-- + boundary。例如这里的boundary为OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp,那么参数分隔符则为:
  1. --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp

不管boundary本身有没有这个"--",这个"--"都是不能省略的。

我们知道HTTP协议采用“请求-应答”模式,当使用普通模式,即非KeepAlive模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议);当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后续请求时,Keep-Alive功能避免了建立或者重新建立连接。

如上图中,左边的是关闭Keep-Alive的情况,每次请求都需要建立连接,然后关闭连接;右边的则是Keep-Alive,在第一次建立请求之后保持连接,然后后续的就不需要每次都建立、关闭连接了,启用Keep-Alive模式肯定更高效,性能更高,因为避免了建立/释放连接的开销。

http 1.0中默认是关闭的,需要在http头加入"Connection: Keep-Alive",才能启用Keep-Alive;http 1.1中默认启用Keep-Alive,如果加入"Connection: close ",才关闭。目前大部分浏览器都是用http1.1协议,也就是说默认都会发起Keep-Alive的连接请求了,所以是否能完成一个完整的Keep- Alive连接就看服务器设置情况。

请求实体分析

请求实体其实就是HTTP POST请求的参数列表,每个参数以请求分隔符开始,即-- + boundary。例如下面这个参数。
  1. --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp
  2. Content-Disposition: form-data; name="lng"
  3. Content-Type: text/plain; charset=UTF-8
  4. Content-Transfer-Encoding: 8bit
  5. 116.361545

上面第一行为--OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp,也就是--加上boundary内容,最后加上一个换行 (这个换行不能省略),换行的字符串表示为"\r\n"。第二行为Content-Disposition和参数名,这里的参数名为lng,即经度。Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名,这里我们不过多关注。第三行为Content-Type,即WEB 服务器告诉浏览器自己响应的对象的类型,还有指定字符编码为UTF-8。第四行是描述的是消息请求(request)和响应(response)所附带的实体对象(entity)的传输形式,简单文本数据我们设置为8bit,文件参数我们设置为binary就行。然后添加两个换行之后才是参数的具体内容。例如这里的参数内容为116.361545。

注意这里的每行之间都是使用“\r\n”来换行的,最后一行和参数内容之间是两个换行。文件参数也是一样的格式,只是文件参数的内容是字节流。
这里要注意一下,普通文本参数和文件参数有如下两个地方的不同,因为其内容本身的格式是不一样的。
普通参数:
  1. Content-Type: text/plain; charset=UTF-8
  2. Content-Transfer-Encoding: 8bit
文件参数:
  1. Content-Type: application/octet-stream
  2. Content-Transfer-Encoding: binary
 
参数实体的最后一行是: --加上boundary加上--,最后换行,这里的 格式即为: --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp--。

模拟文件上传请求

  1. public static void uploadFile(String fileName) {
  2. try {
  3. // 换行符
  4. final String newLine = "\r\n";
  5. final String boundaryPrefix = "--";
  6. // 定义数据分隔线
  7. String BOUNDARY = "========7d4a6d158c9";
  8. // 服务器的域名
  9. URL url = new URL("www.myhost.com");
  10. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  11. // 设置为POST情
  12. conn.setRequestMethod("POST");
  13. // 发送POST请求必须设置如下两行
  14. conn.setDoOutput(true);
  15. conn.setDoInput(true);
  16. conn.setUseCaches(false);
  17. // 设置请求头参数
  18. conn.setRequestProperty("connection", "Keep-Alive");
  19. conn.setRequestProperty("Charsert", "UTF-8");
  20. conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
  21. OutputStream out = new DataOutputStream(conn.getOutputStream());
  22. // 上传文件
  23. File file = new File(fileName);
  24. StringBuilder sb = new StringBuilder();
  25. sb.append(boundaryPrefix);
  26. sb.append(BOUNDARY);
  27. sb.append(newLine);
  28. // 文件参数,photo参数名可以随意修改
  29. sb.append("Content-Disposition: form-data;name=\"photo\";filename=\"" + fileName
  30. + "\"" + newLine);
  31. sb.append("Content-Type:application/octet-stream");
  32. // 参数头设置完以后需要两个换行,然后才是参数内容
  33. sb.append(newLine);
  34. sb.append(newLine);
  35. // 将参数头的数据写入到输出流中
  36. out.write(sb.toString().getBytes());
  37. // 数据输入流,用于读取文件数据
  38. DataInputStream in = new DataInputStream(new FileInputStream(
  39. file));
  40. byte[] bufferOut = new byte[1024];
  41. int bytes = 0;
  42. // 每次读1KB数据,并且将文件数据写入到输出流中
  43. while ((bytes = in.read(bufferOut)) != -1) {
  44. out.write(bufferOut, 0, bytes);
  45. }
  46. // 最后添加换行
  47. out.write(newLine.getBytes());
  48. in.close();
  49. // 定义最后数据分隔线,即--加上BOUNDARY再加上--。
  50. byte[] end_data = (newLine + boundaryPrefix + BOUNDARY + boundaryPrefix + newLine)
  51. .getBytes();
  52. // 写上结尾标识
  53. out.write(end_data);
  54. out.flush();
  55. out.close();
  56. // 定义BufferedReader输入流来读取URL的响应
  57. //            BufferedReader reader = new BufferedReader(new InputStreamReader(
  58. //                    conn.getInputStream()));
  59. //            String line = null;
  60. //            while ((line = reader.readLine()) != null) {
  61. //                System.out.println(line);
  62. //            }
  63. } catch (Exception e) {
  64. System.out.println("发送POST请求出现异常!" + e);
  65. e.printStackTrace();
  66. }
  67. }

JAVA模拟HTTP post请求上传文件的更多相关文章

  1. Java模拟表单POST上传文件

    JAVA模拟表单POST上传文件 import java.awt.image.BufferedImage;import java.awt.image.ColorModel;import java.io ...

  2. Java模拟客户端向服务器上传文件

    先来了解一下客户端与服务器Tcp通信的基本步骤: 服务器端先启动,然后启动客户端向服务器端发送数据. 服务器端收到客户端发送的数据,服务器端会响应应客户端,向客户端发送响应结果. 客户端读取服务器发送 ...

  3. C#模拟HTTP Form请求上传文件

    using System; using System.Collections.Generic; using System.Text; using System.Net; using System.IO ...

  4. Java客户端通过Http发送POST请求上传文件到web服务器

    http://www.cnblogs.com/WilliamJiang/archive/2012/04/29/2475883.html 1.朋友的一个需求,让我给他实现,需求是这样的,需要用ASP.n ...

  5. SpringMVC实现PUT请求上传文件

    在JQuery中,我们可以进行REST ful中delete和put的请求,但是在java EE标准中,默认只有在POST请求的时候,servlet 才会通过getparameter()方法取得请求体 ...

  6. SSM框架下,使用ajax请求上传文件(doc\docx\excel\图片等)

    1.准备工作 1.1.添加上传必要jar包 <dependency> <groupId>commons-io</groupId> <artifactId> ...

  7. Postman Post请求上传文件

    Postman Post请求上传文件一.选择post请求方式,输入请求地址 二.填写Headers Key:Content-Type :Value:multipart/form-data 如下图 三. ...

  8. python中使用multipart/form-data请求上传文件

    最近测试的接口是上传文件的接口,上传单个文件,我主要使用了2种方法~ 接口例如: URL: http://www.baidu.com/*** method:post 参数: { "salar ...

  9. element-ui上传组件,通过自定义请求上传文件

    记录使用element-ui上传组件,通过自定义请求上传文件需要注意的地方. <el-upload ref="uploadMutiple" :auto-upload=&quo ...

随机推荐

  1. (转)对比MS Test与NUnit Test框架

    前言: 项目中进行Unit Test时,肯定会用到框架,因为这样能够更快捷.方便的进行测试. .Net环境下的测试框架非常多,在这里只是对MS Test和NUnit Test进行一下比较, 因为这两个 ...

  2. HDOJ 1004 Let the Balloon Rise

    Problem Description Contest time again! How excited it is to see balloons floating around. But to te ...

  3. Java 动态代理作用是什么?

    Java 动态代理作用是什么?   1 条评论 分享   默认排序按时间排序 19 个回答 133赞同反对,不会显示你的姓名 Intopass 程序员,近期沉迷于动漫ING 133 人赞同 ① 首先你 ...

  4. 谈谈LoveLive SIF以及即将诞生的LL练习器

    由于课程需要和自身需求以及广大的LLer的需求,这个学期我将做一个造福全世界LLer的安卓app,它的名字是——还没想好(喂),总之是个LL SIF的练习器.什么?你问我LL SIF是什么?看来你不是 ...

  5. SQLServer中系统存储过程sp_spaceused

    sp_spaceused 执行sp_spaceused存储过程的时候可以不用带参数,直接执行,或者exec sp_spaceused都可以,返回两个结果集: 列名 数据类型 描述 database_n ...

  6. 【皇甫】☀Struts_第一节课

    本章讲解内容: DTD是Docunent Type Defintion的缩写,即文档类型定义.DTD用来描述XML文档结构. DOM4J是一个非常优秀的javaXML API,具有性能优异,功能强大和 ...

  7. NLog输出目标及类型

    targets:输出目标节点 target:配置一个输出目标 Type输出类型: Console        输出到控制台 Debugger     输出到VS输出窗口 File        输出 ...

  8. Kolmogorov-Smirnov检验

    Kolmogorov-Smirnov检验(K-S检验)基于累积分布函数,用以检验一个经验分布是否符合某种理论分布或比较两个经验分布是否有显著性差异. 两样本K-S检验由于对两样本的经验分布函数的位置和 ...

  9. 自定义 JSON 对象

    针对 IE9 以下不支持 JSON 对象的处理方式,网上大部分自定义的方式无形之中都会将中文转码为 Unicode 编码格式的字符换,但是在浏览器中我们有无法察觉到(浏览器自己解析成 UTF8 了), ...

  10. cocopods 安装与使用

    iOS 最新版 CocoaPods 的安装流程 1.移除现有Ruby默认源 $gem sources --remove https://rubygems.org/ 2.使用新的源 $gem sourc ...