读书人

怎么跟踪 客户端上载是否完成

发布时间: 2012-11-14 10:12:19 作者: rapoo

如何跟踪 客户端下载是否完成
这是一个技术难题。
需求是:
有一个收费的下载链接,但是是基于用户成功下载才能进行收费。

可是如何判断用户是否成功下载呢?

找了很多的技术文档,没有结果。其中常见的一种方案是:

  String file=request.getParameter("file");//物理文件路径    String filename=file;     if(file.startsWith("/"))        file=session.getServletContext().getRealPath(file);    if(file.indexOf("\\")>-1)        filename=file.substring(filename.lastIndexOf("\\")+1);    String err=null; String txt=null;    System.out.println("filename: "+filename);    byte data[]=null;        try{            InputStream inputStream = new FileInputStream(file); // 以byte流的方式打开文件 d:\1.gif            int i=inputStream.available(); //得到文件大小            data=new byte[i];            inputStream.read(data); //读数据            inputStream.close();            inputStream=null;        }catch(FileNotFoundException e){            err="无法在服务器上获取相关文件!文件不存在.";        }catch(Exception e){            err=e.getMessage();        }     System.out.println(filename);    if(data==null||err!=null){        if(txt!=null)           txt="请求的数据不是合法的二进制格式!";        else{            if(err==null)txt="无法从服务器获取相关文件,文件数据不存在或已经损坏.";            else txt=err;        }        String msg="无法下载文件:"+filename+"\\n"+txt;        return;    }    response.setContentType("application/octet-stream");    response.addHeader("Content-disposition" , "attachment;filename="+filename+"\"");    response.getOutputStream().write(data);    response.getOutputStream().close();    out.clear();    out = pageContext.pushBody();    data=null;                                                                     MD5 md=new MD5();    String ip=request.getRemoteHost();    System.out.println("下载成功!");// 进行下载扣费


经过测试,发现这种方法不管客户端是否下载成功都会进行扣费,和


和下面的代码的执行效果是一样的

response.sendRedirect("dowloadfiel_url");//进行扣费



但是我相信,WEB容器是肯定知道下载是否完成的,而且能够触发下载完成事件,否则,容器怎么知道什么时候停止向客户端发送数据流。


欢迎大家提供思路。

2.getfile.jsp先找到真正的thedoc.doc,并随机生成一个密码"asdfgh"(规则自定义)
3.getfile.jsp用命令行带参数(包括密码"asdfgh")调用rar.exe, 生成临时文件 user01_20071101100505_thedoc.doc.rar(规则自定义)
4.将文件名"user01_20071101100505_thedoc.doc.rar" 和 密码 "asdfgh"存入数据库
5.将文件 user01_20071101100505_thedoc.doc.rar 输出到用户

...

6.用户下载完成,用 winrar 打开(利用 winrar 验证文件是否完整),发现要密码才能解压,于是必须点“扣费”按钮(同时输入文件名"user01_20071101100505_thedoc.doc.rar"), 后台从数据库根据文件名,取出密码 "asdfgh"显示给用户。 response.setContentType("application/octet-stream"); response.addHeader("Content-disposition" , "attachment;filename="+filename+"\""); while(发送文件大小完成) { response.getOutputStream().write(data1); response.getOutputStream().write(data2); ....... } 扣费操作..... response.getOutputStream().close();


以上的代码思路是不正确的,response.getOutputStream().write();写的数据其实还在服务器端,请求下载文件的触发请求 与 相应文件输出的下载流的操作 应该不是同一个线程,所以,以上的思路在下载过程中就会进行扣费。


/* * Java Network Programming, Second Edition * Merlin Hughes, Michael Shoffner, Derek Hamner * Manning Publications Company; ISBN 188477749X * * http://nitric.com/jnp/ * * Copyright (c) 1997-1999 Merlin Hughes, Michael Shoffner, Derek Hamner; * all rights reserved; see license.txt for details. */import java.io.*;public class HttpFile implements HttpProcessor { protected File file; public HttpFile (HttpInputStream in) throws IOException { if (in.getMethod () == HTTP.METHOD_POST) throw new HttpException (HTTP.STATUS_NOT_ALLOWED, "<TT>" + in.getMethod () + " " + in.getPath () + "</TT>"); file = new File (HTTP.HTML_ROOT, HTTP.translateFilename (in.getPath ())); if (in.getPath ().endsWith ("/")) file = new File (file, HTTP.DEFAULT_INDEX); if (!file.exists ()) throw new HttpException (HTTP.STATUS_NOT_FOUND, "File <TT>" + in.getPath () + "</TT> not found."); if (file.isDirectory ()) throw new RedirectException (HTTP.STATUS_MOVED_PERMANENTLY, in.getPath () + "/"); if (!file.isFile () || !file.canRead ()) throw new HttpException (HTTP.STATUS_FORBIDDEN, in.getPath ()); } public void processRequest (HttpOutputStream out) throws IOException { out.setHeader ("Content-type", HTTP.guessMimeType (file.getName ())); out.setHeader ("Content-length", String.valueOf (file.length ())); if (out.sendHeaders ()) { FileInputStream in = new FileInputStream (file); out.write (in); in.close (); } }}

byte[] buff = new byte[2048];while (true){ int read = data.read( buff, 0, buff.length ); if (read <= 0) break; out.write( buff, 0, read );}out.flush();out.close();// 此时 理解为 客户端成功下载完.
下载过程中,如果客户端中断,会抛出异常.你只要拦截处理一下就可以.

而如果顺利的执行完了 while循环,并且close了你就可以认为客户端下载完成了.
实际上,这时候一点误差都不会有, 因为你时在flush和close之后判断的.
如果你服务端不想主动flush 和 close,那么有可能存在2K的误差.
因为你最后一次执行out.write之后,有可能数据没有送到客户端.

只要稍微了解一下 HTTP SOCKET TCP/IP 的基础知识
你就应该能够理解我说的意思了 .

还有一点很重要:
我说的这个方案 是我自己亲自动手实践过的 绝对可行的.

最后再提醒一下楼主:
一个对下载 上传 要求严格的网站 ,必须要有一个很好的 http file server.
httpfile server 一个server级的东西,
而不是一个运行在某j2ee server下的servlet 或web listener.




你又说:
"然后扣费处理,如果下载成功也就完成任务了..."
楼主的问题就是, 你如何判断客户端"下载成功"????

29 楼 rehte 2007-11-05 fins:
我觉得你使用socket直接进行编程和使用标准httpserver的结果是一样的。因为httpserver最终最底层也是使用socket实现的。退一步讲,使用socket能准确判知客户端浏览器是否下载成功,但是如果用户发现是误点链接造成的下载,他在浏览器提示保存时,取消了保存怎么办?因此还是不准确。只有在索取密码那种办法才能严格断定用户的行为:
1.他的确下载成功了,解压正常,但缺少密码。
2.他的确想使用该软件,如果是误点而实际并不想使用,他就不会索要密码了。要密码就证明了他想用。这儿密码的含义同购买软件serial number一样,具有确保软件真正是到达用户的作用。它间接的起到了证明用户成功获得软件的事实,证明了他主观上的确要想要下载该软件的,而不是误点造成的。
按照你们的做法,如果用户因为误点而被扣费,他们肯定会很生气,甚至会产生诉讼。 30 楼 fins 2007-11-05 rehte 写道fins:
我觉得你使用socket直接进行编程和使用标准httpserver的结果是一样的。因为httpserver最终最底层也是使用socket实现的。
这点我当然清楚
但是显然楼主不清楚 ,所以我要解释给他听, 客户端断开连接 取消下载后, 服务端是可以知道的.

另外 ,我觉得你说这个和 楼主提的问题 没有什么关系
楼主的问题是
服务器端 如何 判断 客户端成功下载.

这里的问题 是 服务器端 与 客户端 ,是两台机器的事情.
如果非要把人的因素考虑进来 "他在浏览器提示保存时,取消了保存怎么办"
那我问你, 如果他保存了, 可是保存之后,才发现自己下载错了 ,又把文件删除了, 那服务端怎么知道?

楼主的问题是一个技术问题, 而楼上回答的 是一个 收费下载机制的实现方式问题.

我不知道楼主更需要的是哪个答案.

但是,我的观点是:
文件一旦下载到客户端, 那么就应该认为他下载成功了. 而他是否保存,是否删除,以及对他来说是否有用,我们无需关心 也无法正确判断.
毕竟互联网服务不是去超市买东西, 不包退不包换.

你打错电话了, 然后跟移动说, 我打错了, 刚才那电话的花费你别给我扣, 他们会答应吗????
31 楼 realorg 2007-11-05 我想如果我们是技术研讨的话,大家应该把问题放在“如何判断客户端已经下载成功”;
如果我们仅仅是为了解决“如何对已下载用户扣费”的问题,则 使用加密,再由扣费后给用户密码的方式应该是最合适的解决办法。

作为技术讨论,我想我们还是 来继续研究一下“如何判断客户端已经下载成功”。
等待高手ing....

读书人网 >软件架构设计

热点推荐