web应用中服务器端不能终止客户端继续上传的验证

    在查看iteye的时候,发现了这么一个帖子:文件上传:服务器如何终止客户端继续上传。在原文中,提到了这么一个问题:该场景为:当上传文件不满足要求(服务器端检查:如文件超过大小限制,扩展名不正确等),服务器在等待文件上传完成之前立即返回,目的是终止浏览器继续上传。
    这样就不必需要待到客户端在把所有数据都上传到服务器端之后,再给客户端返回一个上传不合法的提示,同时,这也是一个避免大量战胜客户端内存和服务器端内存的一个方法。
    然而实际情况,并非如此,尽管在服务器端已经关闭了接收客户端上传数据的流,并且已经返回了结果,但这并不能阻止客户端继续上传未上传的数据。究其原因,还是因为在服务器端,处理的输入流并不是直接表示当前请求的输入流,关闭一个请求处理的输入流并不代表关闭此次请求的输入流,所以客户端仍然会继续上传剩下的数据。

    笔者做了一个简单的测试,在本机建立一个使用struts2的应用,设定最大上传数据大小为6*1024*1024(即6M)大小。当上传一个大于6M的数据时,服务器端报如一个数据过大的异常。但这并不能演示客户端的情况。于是,将上传数据扩大为600M(即一个oracle的安装文件)。在这次,服务器快速的返回了,然而客户端浏览器确已不再有反应了,进度条还在继续,还在上传中。过了好一会儿,才最终显示由服务器端返回的数据。

    在帖子中,提到一个处理这种问题的方法,即想办法找到表示当前请求的socket,当数据不合法时,直接关掉当前的socket,来关掉此次请求。由于笔者使用的是tomcat作为一个web应用服务器,所以通过反射的方法 找到了当前请求的socket,然后直接关掉此socket,来演示一下直接关掉请求的结果。处理代码如下所示:在JakataMultiPartRequest的parse(HttpServletRequest servletRequest, String saveDir)方法中,通过在catch异常的处理结果中添加以下处理代码:

try {
                Field field = RequestFacade.class.getDeclaredField("request");
                field.setAccessible(true);
                InputStream inputStream = ((InternalInputBuffer)((Request)field.get(( ((RequestFacade)servletRequest))))
            .getCoyoteRequest().getInputBuffer()).getInputStream();
                Field sf = inputStream.getClass().getDeclaredField("socket");
                sf.setAccessible(true);
                Socket socket = (Socket)sf.get(inputStream);
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write("too long to upload".getBytes());
                outputStream.flush();
            inputStream.close();
            }catch(Exception ex) {
                ex.printStackTrace();
                throw new RuntimeException(ex);
            }

    即直接关掉在tomcat中表示当前请求处理的socket。有了这个之后,界面上的反应确实有变化了。
   
    在以上代码中,本意是想在数据超过大小的时候,往客户端写一条数据信息,并关掉当前socket(关掉inputStream即可关掉当前socket),并期望浏览器上显示这里回显的数据。然而,结果并不是这样,实际的结果如下:

    因为请求socket被关闭了,所以界面上直接显示以上信息,即连接被关闭了,并没有出现想要的结果。

继续阅读“web应用中服务器端不能终止客户端继续上传的验证”