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

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

POST报文格式

[plain] view plain copy
  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--

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

格式分析

请求头分析

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

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

[plain] view plain copy 
  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,那么参数分隔符则为:
[plain] view plain copy 
  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。例如下面这个参数。
[plain] view plain copy 
  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”来换行的,最后一行和参数内容之间是两个换行。文件参数也是一样的格式,只是文件参数的内容是字节流。
这里要注意一下,普通文本参数和文件参数有如下两个地方的不同,因为其内容本身的格式是不一样的。
普通参数:
[plain] view plain copy 
  1. Content-Type: text/plain; charset=UTF-8
  2. Content-Transfer-Encoding: 8bit
文件参数:
[plain] view plain copy 
  1. Content-Type: application/octet-stream
  2. Content-Transfer-Encoding: binary
参数实体的最后一行是: --加上boundary加上--,最后换行,这里的 格式即为: --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp--。

模拟文件上传请求

[java] view plain copy 
  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. }

http post文件上传机制相关推荐

  1. struts2的文件上传机制

    Struts2的上传(基本流程例如以下) 1.Struts2默认採用了apache commons-fileupload 2.Struts2支持三种类型的上传组件 3.须要引入commons-file ...

  2. struts2 文件上传与下载 (初始文件上传的底层技术)——struts2第七讲

    2019独角兽企业重金招聘Python工程师标准>>> 文件上传 struts2 注:本文系作者在看了浪曦的风中叶老师的struts2视频的个人总结,希望能帮助广大struts2的初 ...

  3. [RFC1867] HTML中基于表单的文件上传

    网络工作组:E. Nebel 征求意见:1867 L. Masinter 类别:试验 施乐公司 十一月 1995 HTML中基于表单的文件上传 这个备忘录的状态 这个备忘录为互联网社区定义了一个试验协 ...

  4. jquery的ajax提交文件上传

    以前的项目大多的使用jquery的插件来进行文件上传,对于就只引用jquery而不使用插件来上传文件之前未有写过,最近项目里有写到和用到,就记录一下,以后方便查找. 提示:存在浏览器皆容问题,谨慎使用 ...

  5. 阿提拉公司 java_Atitit  文件上传  架构设计 实现机制 解决方案  实践java php c#.net js javascript  c++ python...

    Atitit 文件上传 架构设计 实现机制 解决方案 实践 java php c#.net js javascript c++ python 1 . 上传的几点要求 2 1 .1. 本地预览 2 1 ...

  6. 【基于SSH框架的个人博客系统06】头像文件上传与前后端分页机制

    注意:本项目为博主初学Web开发时所写,所使用的方法都比较笨,不符合主流开发方法.例如,包管理应该使用Maven进行管理而不是手动导入,对前端后端代码的架构也并不是很清晰.大家学习思想即可,可以不用浪 ...

  7. 利用 PHP POST 临时文件机制实现任意文件上传

    文章目录 原理 如何获取临时文件名 $_FILES phpinfo glob 如何利用该文件 组合请求 延长临时文件存在时间 参考 原理 向 PHP 发送 Post 数据包,如果数据包中包含文件,无论 ...

  8. 使用WinHttp接口实现HTTP协议Get、Post和文件上传功能

    我实现了一个最新版本的接口,详见<实现HTTP协议Get.Post和文件上传功能--使用WinHttp接口实现>.还有基于libcurl实现的版本<实现HTTP协议Get.Post和 ...

  9. struts2的文件上传和文件下载

    实现使用Struts2文件上传和文件下载: 注意点: (1)对应表单的file1和私有成员变量的名称必须一致 <input type="file" name="fi ...

最新文章

  1. 【读书笔记《Android游戏编程之从零开始》】16.游戏开发基础(动画)
  2. overlapped I/O的学习笔记
  3. linux 修改IP, DNS 命令
  4. RDIFramework.NET V3.3 Web版角色授权管理新增角色对操作权限项、模块起止生效日期的设置...
  5. java 读取 文本块_Java 13:文本块
  6. leetcode 164. 最大间距(桶排序)
  7. POI处理超过65536条记录
  8. 个人博客系统--项目实战
  9. Android 8款开源游戏引擎
  10. xp查看计算机mac地址查询,如何查看mac地址 xp系统查看查询mac地址方法介绍
  11. semg特征提取matlab zc,一种融合小波包和双谱分析的肌电信号特征提取方法与流程...
  12. Matlab中MatPower模块的安装流程
  13. PandoraBox潘多拉无线桥接(中继)使用方法和无法使用解决----小米mini小米3路由
  14. 偷梁换柱“Windows 11安装包”竟成了恶意程序?
  15. 气候制度的转变和森林的丧失放大了亚马逊森林的火灾
  16. odoo使用word中的MERGEFIELD合并域和python包mailmerge来生成word文档
  17. Exynos_4412——IIC总线概述
  18. 耳机接口规则_3.5mm耳机接口和2.5mm耳机接口有什么区别?
  19. BASH比较两位数大小
  20. 什么是卫片?什么是卫片执法?一文了解卫片执法基础知识

热门文章

  1. halcon脚本根据硬币估计圆形面积
  2. CentOS7新特性——systemd及systemctl
  3. Windows下 使用Python来调用PotPlayer.exe来播放音频
  4. 零基础如何学好python爬虫?之python爬取B站小视频
  5. win10默认浏览器设置中找不到谷歌浏览器
  6. 解决Eclipse安装或更新插件非常缓慢的问题
  7. 【C#语言】WinForm 个性皮肤
  8. 利用C语言编写放烟花的程序(春节专用)
  9. 最新版SIM卡管理工具MAGICSIM V25.0(新版多功能SIM卡读卡软件) 百度网盘下载地址
  10. layUi框架入门篇(二)