基于Struts2的单文件下载

        几乎所有人的人都下载过文件,如音频、视频、软件等等,在各类论坛、网站也会有相应信息导出的功能,那么这种下载的功能是如何分类的呢?又是如何实现的呢?

        对于第一个问题,我认为文件的下载分为两种:其一,信息动态生成,可能通过如时间节点、操作类型,而后导出相应的报表如txt、excel等;第二种,是下载现有文件(音视频、软件),这些文件通常会存储在相应的存储上,通过地址获得其字节流,而后下载。

        不管是第一种,还是第二种,都有一个特征,就是需要将信息转化为字节流,而后输出,这里阐述使用浏览器下载,即通过HTTP的相应报文进行下载。


一、下载的基本流程


        Request->File->Stream->Response

        下载的流程如上所述,用户发出一个下载的请求,而后,后台接收这个请求,对请求的文件或数据进行包装,在通过response返回给用户。那么按照此流程,来看个真实的例子:


1.1 Baidu云盘


         下面的截图,是在百度云空间下载文件时的request和response,可以看下其具体都有哪些信息:

        Content-Disposition:这是最重要的一个字段,意思是,当客户需要请求的信息以文件的形式返回时,设置此字                                            段,在rfc2616以及rfc1806中有详细的论述,其中一段e文原文为:
                                            The Content-Disposition response-header field has been proposed as a
                                            means for the origin server to suggest a default filename if the user
                                            requests that the content is saved to a file. This usage is derived
                                            from the definition of Content-Disposition in RFC 1806 [35].
                                            An example is:
                                           Content-Disposition:attachment; filename="fname.txt"
        Content-Length:此字段,表示返回内容的长度
        Content-Type:   此字段,表示返回的是何种类型的内容即MIME(媒体类型)

        这样,根据这三个字段,我们知道了文件的大小、文件的类型、以及何种操作,就可以将这个文件保存下来了。

1.2 HTTP方式文件下载的根基


        HTTP是应用层协议,并且HTTP协议选择了TCP协议作为其传输层协议,这样,也就保证了数据的可靠性。同时,这也是HTTP方式下载的根基,客户端与Server端建立了一条endpoint to endpoint的通道,用于传输信息,这条通道也是双向的。

二、Struts下载的实现


        注:本文不叙述Struts的搭建过程,文章中,使用的版本为,2.3.9;
        1th.查看Struts的帮助文档查看如下信息
        
         在Struts中每个action会对应一个result,上图是result type的类型,可以看出,对应file dowload的设置是Stream,看下Stream类型的详细配置为:

        可以看出,contentType、contentLength赫然在列,下面看下详细的操作
        注:以上文档信息,在struts2的官方doc中均可找到,有需要可在附件中下载

2.1 Struts文件下载的相关配置


       struts.xml
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC 
	"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
	"http://struts.apache.org/dtds/struts-2.0.dtd">


<struts>
	<package name="a" extends="struts-default">		
		<action name="download" class="com.hzy.struts2.download.FileDownLoad">
			<result name="success" type="stream">
				<param name="contentLength">${fileLength}</param>
				<param name="contentType">text/plain;charset=UTF-8</param>
				<param name="contentDisposition">attachment;filename="${fileName}"</param>
				<param name="inputName">inputStream</param>			
			</result>
		</action>
		
		<action name="downloadFile" class="com.hzy.struts2.download.FileDownLoadFile">
			<result name="success" type="stream">
				<param name="contentType">application/octet-stream</param>
				<param name="contentDisposition">attachment;filename="${fileName}"</param>
				<param name="inputName">inputStream</param>	
				<param name="buffserSize">4096</param>		
			</result>
		</action>
	</package>
</struts>
        downloadFile.action:
package com.hzy.struts2.download;


import java.io.InputStream;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class FileDownLoadFile extends ActionSupport
{
	
	
	private String fileName;
	private InputStream inputStream;
	
	public void setFileName( String fileName )
	{
		this.fileName = fileName + ".exe";
	}

	
	public String getFileName( )
	{
		return this.fileName;
	}

	
	
	public InputStream getInputStream()
	{
		inputStream = ServletActionContext.getServletContext().getResourceAsStream( "/" + fileName );
		
		if( inputStream == null )
		{
			System.out.println( "getResource error!" );
		}
		System.out.println( "FileName : " + fileName );
		return inputStream;
	}
	
	@Override
	public String execute() throws Exception
	{
		return SUCCESS;
	}
}
        download.action:
package com.hzy.struts2.download;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

import com.opensymphony.xwork2.ActionSupport;

public class FileDownLoad extends ActionSupport
{
	
	
	private String fileName;
	private int    fileLength;
	
	public void setFileName( String start )
	{
		this.fileName = start;
	}
	public void setFileLength( int fileLength )
	{
		this.fileLength = fileLength;
	}
	
	public String getFileName( )
	{
		return this.fileName;
	}
	public int getFileLength()
	{
		return this.fileLength;
	}

	
	public InputStream getInputStream()
	{

		String content = "Hahahahaha : " + fileName;
		
		this.setFileLength( content.getBytes().length );
		System.out.println( "The File Size : " + content.getBytes().length );
		return new ByteArrayInputStream( content.getBytes() );
	}
	
	@Override
	public String execute() throws Exception
	{
		return SUCCESS;
	}
}

              以上是,structs单文件下载的一个demo,download.acion模拟的是动态生成一些信息,然后通过stream流输出到前台,并以文本形式报文;downloadFile.action则是,下载服务器端的一个文件,这里下载的是Firefox.exe(20MB)的一个安装包。

       无论是哪种形式,都需要指定类型、长度大小等信息;在structs中这些关键的配置信息已经被固化到了struts.xml中,并且配置比较灵活。
       
       注:这里当下载Firxfox.exe时,并为指定content-Length,这里涉及到另一个协议,HTTP TRUNKED协议,该协议当content-Length未指定的时候,会对数据进行分片传输,并以0结尾,标示文件传输结束。对于该协议的具体细节将会单独开一篇讨论!

       同时,当文件大小不可知或未能明确给出时,会在http的响应报文中,给出Transfer-Encoding chunked字段;同时content-Length与Transfer-Encoding:chunked只能存在其一。

       下图是按上述demo做的截图,两个文件下载的访问方式如下(根据实际情况,酌情修改):
txt:

exe:


        下载文本:http://localhost:8080/Struts_DownFile_/download?fileName=abc
        下载文件:http://localhost:8080/Struts_DownFile_/downloadFile?fileName=Firefox
  

三、小结


        本文初次形成,比较粗糙,如struts缓冲区大小配置等并没有详细论述,而且文件下载一些拓展功能也并没叙述:批量文件下载、上传、断点下载、上传等;这些在后续的文章中会逐步补充,同时,每篇文章都会维护更新的,欢迎大家提出意见。
        诚挚感谢温大的文章,解决了我的困惑:http://my.oschina.net/u/866190/blog/170243

四、附件下载

Struts2(源码、框架、doc):http://pan.baidu.com/s/1gP1ct


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值