我也来说说文件上传的实现原理


placeholder image
admin 发布于:2019-01-22 13:54:18
阅读:loading

基本介绍

一直以来并没有想过提交一个表单参数到Java后台(Servlet)后,WEB容器是怎么去处理它的,好像都是知道去使用request.getParameter或者request.getHeader去获取数据,但是它们是如何获取到的数据的呢,我也是才关注到,可以参考一下这篇文章去了解一下JavaWeb的上传原理和实现(https://www.cnblogs.com/aotemanzhifu/p/9192355.html),本篇文章并不实现具体的文件上传代码,也假设你仔细看过此处的链接地址的内容了。

另外一点,关于表单提交的相关知识点,也是稍微有了个点点深度的了解,主要还是归功于我使用HttpURLConnection类去POST提交一个网页数据,发现这块知识一直都是使用GET的方式去抓取一些网页,对于POST的方式好像还真的是没有去实现过,所以我百度到了这篇文章,看完后让我模糊的视界眼前一亮,于是乎我就跟着写了一个简单的含有文件上传的示例去仔细分析它,于是就有了本篇文章。

我们先来看看表单提交的JSP页面,代码如下:

   <form action="testFile.do" method="post" enctype="multipart/form-data">

      <input type="file" name="logoFile" />

      <select name="logoPosition">

         <option value="centermiddle">正中间</option>

         <option value="rightbottom">右下角</option>

      </select>

   </form>

可以看到上述代码为一个简单的form表单提交,包含文件上传和下拉框两个元素,表单采用二进制的方式提交,其中参数名分别为:“logoFile”和“logoPosition”,submit按钮就直接隐掉了,后台配合使用文件上传的解析实现就能获取到文件和字符串参数了。

下面我们来使用浏览器F12的开发人员工具,去看一下这个表单在提交的过程中发生了什么,假设我选择的文件名称为“docx4j生成文档格式转换.docx”,参考提交截图如下:

image.png

接着再来一个Servlet处理后台表单逻辑获取,直接获取到表单提交过来的输入流,将其存储成文件,保存到磁盘里,参考代码如下:

@WebServlet("/testFile.do")

public class TestServlet extends HttpServlet {

 

   private static final long serialVersionUID = 1L;

 

   /**

    * 文件上传

    */

   @Override

   protected void service(HttpServletRequest req, HttpServletResponse respthrows ServletException, IOException {

     

      InputStream is = req.getInputStream();

      int len = -1;

      byte b[] = new byte[1024];

      OutputStream os = new FileOutputStream(new File("d:\\2.txt"));

      while((len = is.read(b)) != -1) {

         os.write(b, 0, len);

      }

      os.flush();

      os.close();

      is.close();

      System.out.println("TestServlet.service()----end");

   }

}

若如此做,我们将提交的表单数据就存储在d盘下的2.txt文件中了,先来用F12的开发人员工具看看,表单在提交到后台时传输了什么数据,重点关注的是Content-type处的form-data及boundary的值,该值每次提交不固定,但可作为请求数据分割的特殊字符串,参考如下:

image.png

image.png

回过头来我们再看看这个2.txt文件(随意命名为txt而已),它里面的数据取决于表单提交过来的东西(具体可以看看上文中的链接所述内容),本文表单含有文件上传,所以它肯定不会是一个纯文本类的字符文件(除非文件选择的是txt这种字符文件),而是含有一个docx文件的二进制文件,同时文件中还包括参数名等其他信息,我们可以将这个文件中的二进制部分的数据单独存储(即删除段前和段后的字符文件),再另存为格式为docx的文件,发现仍然可以打开,甚至于文件字节数大小也一致。参考2.txt文件内容如下图所示:

image.png

image.png

所以,了解到了这些信息,理论上我们就可以自行实现一个表单提交过来的数据解析了,包括它里面的文件上传实现(我相信一些框架里面对文件上传的包装应该也是这么自行实现的),但理论上可行之后,实际实现上时有个文件,反正我目前还是不知道怎么把这个文件中的上传文件部分给拿出来并且存储成单独的文件,于是乎看了下关于Apache的fileupload的具体实现上有这么一些相关的代码,它的实现原理是定义几个字节数组,根据字节去分割拷贝字节数组,参考代码如下:

image.png


上述代码(这是查看反编译后的class代码,如果查看源码的话这里全是一些常量定义,不便于分析)中,可以看到它在处理文件逻辑时,定义了一些基于特定规则的字节数组,如‘两个回车符号’、‘两个--’、‘回车加-’等去特殊处理表单提交传递过来参数,太复杂,有兴趣的可自行查看源码实现。

附件下载

附件下载.zip


 点赞


 发表评论

当前回复:作者

 评论列表


留言区