前面我们刚刚学完文件上传,那么我们就接着来看和文件上传相对应的文件下载吧。对于文件上传而言,文件下载实现起来要简单的多。通常我们可以直接将一个超链接的地址指向我们想要给用户下载的资源即可。但是如果这些资源是浏览器能够解析的文件类型,比如html文件等,那么浏览器将不会提示用户下载这些文件了,而是会直接在浏览器中打开。这样做还有一个问题,那就是我们的资源文件的地址就直接暴露给用户了,它可以在其他的页面中被轻易的引用,这往往让我们觉得不太安全。那么就需要通过某种手段来告诉浏览器,让它提示用户下载我们的资源文件,并且将这些资源文件的真实地址隐藏起来。

使用Jsp/Servlet实现文件下载

首先我们使用Jsp和Servlet来完成我们上面手的文件下载功能。

public class FileDownServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {File file = new File(getServletContext().getRealPath("/down")+ "/AdobeReader8.rar");FileInputStream in = new FileInputStream(file);OutputStream out = resp.getOutputStream();resp.setContentType("Application/Octet-stream;charset=utf-8");resp.addHeader("Content-Disposition","attachment; filename="+file.getName());resp.addHeader("Content-Length::", file.length() + "");byte[] b = new byte[1024];int len = 0;while ((len = in.read(b)) != -1) {out.write(b, 0, len);}out.close();in.close();}
}

Input.jsp

<body>

<ahref="FileDownServlet">点击下载</a>

</body>

测试(打开浏览器,点击链接):

可以看到,这样一来,即使是文本文件,也可以提供给用户下载,并且很好的隐藏了文件资源的真实地址。因为文件的真实地址是我们在文件中通过某种方式获得的,并且是以流的形式输出到浏览器,那么浏览器看到的就只有当前jsp文件的地址。我们可以通过在链接上传一些参数(比如文件的id号),然后在Servlet中通过该Id号到数据库中去查询文件的真实的值和名字,也可通过算法直接算出真实地址。

这样虽然可以对文件进行下载了,但是它不支持断点续传功能,即下载的过程不能中断,一旦中断就需要重新下载了。

支持断点续传

实现断点续传的基本原理就是在请求头中加上一个Range参数,这个参数告诉流从什么地方开始下载,如果Rang参数为空或者0那么表示从头开始下载,如果参数有值,表示从流中跳过这么多字节再下载。如果服务端查询到是从头开始下载,那么就应该将响应头状态设置为200,如果是续传,那么应该将响应头设置为206.然后设置头参数Accept-Ranges的值为bytes,说明以字节流的方式传输,是下载文件。接着设置响应字节跳转值,Content-Range属性的值 这个值的格式为 "请求参数Range-流总长度减去1/流总长度"

最后开始向客户端传输响应流,这个流用OutputStream.skip(intp)方法跳过请求参数Range数值进行传输。我们将上面的例子改为支持断点续的服务端:

public class FileDownServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {File file = new File(getServletContext().getRealPath("/down")+ "/Access2003.exe");if (file.exists()) {long p = 0;long fileLength;fileLength = file.length();InputStream in = new FileInputStream(file);response.reset();response.setHeader("Accept-Ranges", "bytes");// 查看是否头信息中包含Range参数if (request.getHeader("Range") != null) {// 设置相应头状态(206)response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);// 通过头信息获取Range参数的(整数)值p = Long.parseLong(request.getHeader("Range").replaceAll("bytes=", "").replaceAll("-", ""));}response.setHeader("Content-Length", new Long(fileLength - p).toString());if (p != 0) {// 断点开始// 响应的格式是:// Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]String contentRange = new StringBuffer("bytes ").append(new Long(p).toString()).append("-").append(new Long(fileLength - 1).toString()).append("/").append(new Long(fileLength).toString()).toString();response.setHeader("Content-Range", contentRange);// 跳过指定数量的字节in.skip(p);}response.addHeader("Content-Disposition", "attachment;filename="+ file.getName());byte[] buf = new byte[1024];int len = 0;// 开始写入数据while ((len = in.read(buf)) != -1) {response.getOutputStream().write(buf, 0, len);response.getOutputStream().flush();}in.close();}}

Input.jsp页面保持不变。

测试(下载工具需要支持断点续传,这里我使用快车):

通过从上面的两张图中可以看出,从头下载和续传下载在请求头上的区别。

使用Struts2实现文件下载

Struts2中提供了一种叫做stream的结果类型,通过它可以直接将一个InputStream传送给它,它就会自动的提供给客户端下载。原来一些我们在程序中使用代码设置的头信息,现在可以在Action的配置文件中配置了(当然也可以通过代码设置)。下面我们就动手做一个实例吧:

public class DownAction extends ActionSupport implements ServletContextAware {private int id;private InputStream inputStream;private ServletContext context;private String fileName;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getFileName() {return fileName;}public void setFileName(String fileName) {this.fileName = fileName;}public InputStream getInputStream() {fileName = id + ".exe";return context.getResourceAsStream("/down/" + id + ".exe");}public void setInputStream(InputStream inputStream) {this.inputStream = inputStream;}public void setServletContext(ServletContext context) {this.context = context;}}

down.jsp

<body>

<ahref="down.action?id=1">下载地址1</a><br/>

<ahref="down.action?id=2">下载地址2</a>

</body>

struts.xml

<actionname="down"class="action.DownAction">

<resultname="success"type="stream">

<paramname="contentType">application/octet-stream</param>

<paramname="contentDisposition">filename=${fileName}</param>

</result>

</action>

测试:

配置文件中这些关于响应参数的设置也可以通过StreamRsult类提供的方法来设置。

void

setAllowCaching(booleanallowCaching)
Set allowCaching to false to indicate that the client should be requested not to cache the data stream.

void

setBufferSize(intbufferSize)

void

setContentCharSet(StringcontentCharSet)

void

setContentDisposition(StringcontentDisposition)

void

setContentLength(StringcontentLength)

void

setContentType(StringcontentType)

void

setInputName(StringinputName)

Struts2文件下载要实现断点续传,思路和我们前面使用jsp/servlet实现断点断点续传的思路相同。主要就是请求头参数和响应头参数的处理。断点续传的时候,下载位置由客户端来提供的,那么我们在服务器端只要具有能够根据客户端请求参数来移动输入流指针位置的功能,就能够实现断点续传。当然,上面只是我自己一些理解和看法,如有错误还希望路过的老鸟指正,感激不尽!

Struts2学习笔记(十七) 文件下载(File Download)相关推荐

  1. windows内核开发学习笔记十七:IRP 和 IO_STACK_LOCATION 的交互

    windows内核开发学习笔记十七:IRP 和 IO_STACK_LOCATION 的交互 前面两篇学习笔记分别介绍了IRP和IO_STACK_LOCATION,整个设备栈来处理这个IRP,但是每个设 ...

  2. Polyworks脚本开发学习笔记(四)-利用FILE IMPORT进行数据导入及对齐

    Polyworks脚本开发学习笔记(四)-利用FILE IMPORT进行数据导入及对齐 导入参考及数据 FILE IMPORT_REFERENCE导入参考的CAD文件如igs格式 括号内参数分别为(导 ...

  3. Polyworks脚本开发学习笔记(十七)-制作宏脚本自定义工具条

    Polyworks脚本开发学习笔记(十七)-制作宏脚本自定义工具条 做好的宏脚本程序,每次打开脚本加载程序太多麻烦,为了方便的调用脚本做各种操作,可以使用系统的自定义工具条功能将脚本加载到工具条上. ...

  4. OpenCV学习笔记(十七)——K均值聚类

    当我们要预测的是一个离散值时,做的工作就是"分类".机器学习模型还可以将训练集中的数据划分为若干个组,每个组被称为一个"簇(cluster)".它的重要特点是在 ...

  5. kvm虚拟化学习笔记(十七)之KVM到KVM之v2v迁移

    1.源KVM虚拟主机node1 (1).查看源KVM虚拟主机上的虚拟机列表,本文计划将CentOS6.5-01虚拟机迁移到其它KVM虚拟主机中. [root@node1 ~]# virsh list ...

  6. Struts2学习笔记(一)--入门常见配置

     Struts2框架的学习路线 l 第一天:Struts2的概述.Struts2的入门.Struts2常见的配置.Struts2的Action的编写 l 第二天:Struts2的数据的封装.结果页面配 ...

  7. Struts2学习笔记(六):Struts2的处理流程与Action的管理方式

    StrutsPrepareAndExecuteFilter是Struts 2框架的核心控制器,它负责拦截由<url-pattern>/*</url-pattern>指定的所有用 ...

  8. 【theano-windows】学习笔记十七——梯度中的consider_constant

    前言 主要是在写玻尔兹曼机相关的theano时, 在计算梯度grad的时候发现一个参数名字叫做consider_constant,来看看这个到底做了什么事情 参考博客: using consider_ ...

  9. Mr.J-- jQuery学习笔记(十七)--动画淡入淡出弹窗广告

    之前写过动画的隐藏与显示:Mr.J-- jQuery学习笔记(十四)--动画显示隐藏 动画隐藏与显示的一个小demo--对联广告:Mr.J-- jQuery学习笔记(十五)--实现页面的对联广告 展开 ...

  10. Java学习笔记十七——集合类详细总结各自对比

    Java编程基础倒数第三篇,感谢没有放弃的自己. 学习笔记参考书籍<Java编程基础>主编 张焕生.本书内容比较适合没有什么基础的入门小白,学完一章还有习题,比较适合初学者. 自律.积极. ...

最新文章

  1. R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(改变图例位置、移除图例)实战
  2. 【Python翻屏软件】英文1秒翻,水准堪比专九,这下考级有救了。
  3. Echarts实例中json数据查找
  4. C++ Ouput Exactly 2 Digits After Decimal Point 小数点后保留三位数字
  5. Java 重写 多态性_java多态性重写overriding和重载overloading的区别
  6. bulter机器人_科普!九款使用率最高的物流机器人大盘点!
  7. 一个很好的机器学习普及网站
  8. error40无法打开到sql_SQL-mysql游标与触发器
  9. 利用静态内部类实现单例模式
  10. windows server 2008 r2 x64 enterprise service pack1中aspjpeg.dll安装
  11. mac安装JDK及maven
  12. 全网最全移动端App性能测试方法,值得收藏!
  13. PDF文件转换成excel文件的三种方法
  14. 沟通CTBS远程接入软件研究
  15. 英特尔核显驱动hd630_技术力井喷!深度解析英特尔11代酷睿处理器
  16. java实现计算器算法_怎样用java 实现计算器的 算法
  17. 【直击】腾讯SaaS加速器首期复试现场:122位CEO争夺30席入场券,激烈!
  18. Pycharm远程调试踩坑:[Errno 2] No such file or directory Process finished with exit code 2
  19. 裸奔真的会中木马病毒 挖矿病毒 wup.exe 程序文件占用电脑资源
  20. python判断用户输入的两个单词是否字母相同,且出现的次数也一样

热门文章

  1. ExpressionFuncTObject, bool与FuncTObject, bool的区别
  2. [转]nodejs npm常用命令
  3. Python爬取小说网站下载小说
  4. elasticsearch删除索引后再新建时踩的坑
  5. zabbix错误提示解决
  6. 洛谷P1429 平面最近点对(加强版)
  7. 10-05 Java 内部类概述和讲解
  8. 通过anaconda 调用opencv
  9. 阿里云免费试用之体验——阿里云serverECS试用心得
  10. [转载] 晓说——第23期:大师照亮八十年代