Struts1.x系列教程(15):使用DownloadAction类统计文件下载次数

简介: 本文为原创,如需转载,请注明作者和出处,谢谢! 上一篇:Struts1.x系列教程(14):动态Form     除了标准的org.apache.struts.action.Action类外,在Struts中还提供了另外7个Action类来完成特殊的工作。
    除了标准的org.apache.struts.action.Action 类外,在Struts 中还提供了另外7 Action 类来完成特殊的工作。本文及后面的文章中将介绍这些Action 类的用法。

一、 DownloadAction 类简介

   
可能有时需要在Web 程序中加入下载功能。如果要下载的是静态文件,可以直接交给Web 服务器处理,但如果要对下载的文件做额外的功能,如统计文件的下载次数。就需要在下载文件之前先要调用相应的程序进行处理。
    虽然我们可以直接在
Action 子类中来处理下载文件,但是如果这样的程序比较多时,就会写很多重复的代码。为了简化这个工作。Struts 提供了一个新的Action 类:DownloadAction 。所有继承了DownloadAction 类的Struts 动作都可以非常容易地完成下载文件的工作。
    DownloadAction类有一个抽象方法getStreamInfo。这个方法的定义如下:
   protected   abstract  StreamInfo getStreamInfo(ActionMapping mapping,
                ActionForm form, HttpServletRequest request,
            HttpServletResponse response) 
throws  Exception;


    
getStreamInfo方法返回一个StreamInfo对象。StreamInfo接口是DownloadAction类的一个内部接口,这个接口的定义如下:

public   static   interface  StreamInfo 
{
    public   abstract  String getContentType();
   
public   abstract  InputStream getInputStream()  throws  IOException;
}

从上面的代码可以看出,StreamInfo接口有两个方法。其中getInputStream方法返回了服务端要下载的文件的InputStream对象。getContenttType方法返回了HTTP响应消息头字段Content-Type的信息。在getStreamInfo方法中只要返回了实现这两个方法的StreamInfo对象,就可以自动完成下载工作。
    为了方便起见,DownloadAction类中还提供了两个实现StreamInfo的内类:FileStreamInfo和ResourceStreamInfo。这两个类的构造方法的定义如下:

   public  FileStreamInfo(String contentType, File file);
  public  ResourceStreamInfo(String contentType, ServletContext context, String path);

  我们可以使用FileStreamInfo类来下载静态的文件。如果要下载的文件在Web根目录,可以使用ResourceStreamInfo类。其中path参数表示文件相对于Web根目录的路径,必须以“/”开头,表示从Web根目录开始。

二、实例:统计文件的下载次数

    在本节中将使用DownloadAction类实现一个统计文件下载次数的Web程序。这个程序的基本原理是当一个文件下载完成后,加这个文件在数据库中的下载次数加1,如果某个文件是第一次下载,则在数据库中添加一条新记录,下载次数为1

为了实现这个Web程序,需要如下几步:
  【第1步】建立用于保存文件下载次数的数据表

   在本例中我们使用名为struts数据库,并且在struts数据库中建立一个t_dcount表,代码如下:

   CREATE   TABLE  struts.t_dcount(
  id 
INT   NOT   NULL ,
  
count   INT   NOT   NULL ,
  filename 
VARCHAR 256  )  NOT   NULL ,
  
PRIMARY   KEY  (id)
  ) ENGINE 
=  InnoDB   DEFAULT  CHARSET = gbk;

【第2步】编写Struts动作类

这个Struts动作类负责完成文件的下载工作。如果在访问Struts动作类时不加file参数,会将指定目录下的所有文件(不包括隐藏文件)和已经下载的次数发送到客户端浏览器。如果通过file参数指定了下载文件,这个Struts动作就会下载这个文件。
    在/src/action目录中建立一个FileDownloadAction.java文件,代码如下:

   package  chapter6.action;
  
  
import  javax.servlet.http. * ;
  
import  org.apache.struts.action. * ;
  
import  org.apache.struts.actions. * ;
  
import  java.io. * ;
  
import  java.sql. * ;
  
  
public   class  FileDownloadAction  extends  DownloadAction
  {
      
private  Connection conn;
      
private  String path;
      
private  String filename;
  
      
//  获得了Connection对象
       private   void  openConnection()  throws  Exception
      {
          
if  (conn  ==   null )
          {
              Class.forName(
" com.mysql.jdbc.Driver " );
              conn 
=  DriverManager.getConnection(
                      
" jdbc:mysql://localhost/struts?characterEncoding=GBK " ,
                      
" root " " 1234 " );
          }
      }
      
//  获得某个文件的下载次数,其中id是文件名的hashcode
       private   int  getDownloadCount( int  id)  throws  Exception
      {
          openConnection();
          PreparedStatement pstmt 
=  conn
                  .prepareStatement(
" SELECT count FROM t_dcount WHERE id =  "   +  String.valueOf(id));
          ResultSet rs 
=  pstmt.executeQuery();
          
while  (rs.next())
          {
              
return  rs.getInt( 1 );
          }
          
return   0 ;
      }
      
//  在文件完成下载后,将该文件的下载次数加1
       private   void  incDownloadCount()  throws  Exception
      {
          openConnection();
          
int  id  =  filename.hashCode();
          PreparedStatement pstmt 
=  conn
                  .prepareStatement(
" UPDATE t_dcount SET count = count + 1 WHERE id =  "
                          
+  String.valueOf(id));
          
if  (pstmt.executeUpdate()  ==   0 )
          {
              pstmt.executeUpdate(
" INSERT INTO t_dcount(id, count, filename) values( "
                              
+  String.valueOf(id)  +   " ,1,' "   +  filename  +   " ') " );
          }
      }
  
      
//  下载文件时调用getStreamInfo方法
       protected  StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form,
              HttpServletRequest request, HttpServletResponse response)
              
throws  Exception
      {
          
final  FileInputStream fis  =   new  FileInputStream(path  +  filename);
          
final  String contentType  =   " application/file " ;
          
//  建议设置content-disposition响应信息头,否则Web浏览器在下载文件时
          
//  无法在保存文件对话框中显示正确的文件名
          response.setHeader( " content-disposition " " attachment; filename= "
                  
+  filename);
          incDownloadCount();
          
return   new  DownloadAction.StreamInfo()   //  使用隐式的方法实现了StreamInfo接口
          {
              
public  String getContentType()
              {
  
                  
return  contentType;
              }
              
public  InputStream getInputStream()  throws  IOException
              {
                  
return  fis;
              }
          };
      }
      
//  如果Struts动作不加file请求参数,则通过execute方法将指定目录中文件列表输出到客户端
       public  ActionForward execute(ActionMapping mapping, ActionForm form,
              HttpServletRequest request, HttpServletResponse response)
              
throws  Exception
      {
          path 
=   this .getServlet().getInitParameter( " downloadPath " );
          filename 
=  request.getParameter( " file " );
          
if  (filename  ==   null )
          {
              File file 
=   new  File(path);
              File[] files 
=  file.listFiles();
              response.setCharacterEncoding(
" GBK " );
              PrintWriter out 
=  response.getWriter();
              
              out.println(
"
  • ");
                  
    for (File f : files) // 开始向客户端浏览器输出文件列表 
                  {
      
                      
    if (f.isFile() && !f.isHidden())
                      {
                          out.println(
    "
  • " + request.getContextPath() + mapping.getPath()
    + ".do?file=" + f.getName() + "'>" + f.getName()
                                   
    + "  下载次数:"
                                  
    + String.valueOf(getDownloadCount(f.getName().hashCode())) 
    + "
  • ");
                      }
                  }
                  out.println(
    "
" );
              
return   null ;
          }
          
else
          {
              
//  当file参数存在时,则调用DownloadAction中的execute方法
              
//  实际上,在DownloadAction类中的execute方法调用了getStreamInfo方法
              
//  这条语句就相当于调用了getStreamInfo方法
               return   super .execute(mapping, form, request, response);
          }
      }
  }

【第3步】配置Struts动作类

    在struts-config.xml文件中的标签中加入了如下内容:

< action  path ="/download"  scope ="request"  type ="action.FileDownloadAction"   />

【第4步】配置下载路径   

在web.xml中找到一个叫action的Servlet,并在标签中添加如下内容:

< init-param >
    < param-name > downloadPath </ param-name >
    < param-value > D:/download/ </ param-value >
</ init-param >

    读取可以设置自已的下载目录,但下载目录必须以“"”结尾。
 

目录
相关文章
|
存储 算法 搜索推荐
数据结构--堆的深度解析
数据结构--堆的深度解析
|
关系型数据库 MySQL 数据处理
针对MySQL亿级数据的高效插入策略与性能优化技巧
在处理MySQL亿级数据的高效插入和性能优化时,以上提到的策略和技巧可以显著提升数据处理速度,减少系统负担,并保持数据的稳定性和一致性。正确实施这些策略需要深入理解MySQL的工作原理和业务需求,以便做出最适合的配置调整。
1502 6
|
算法 Python
NumPy 高级教程——性能优化
NumPy 高级教程——性能优化 【1月更文挑战第2篇】
691 0
|
存储 机器学习/深度学习 编解码
CVPR录用+NTIRE冠军!清华提出首个高光谱图像重建Transformer
CVPR录用+NTIRE冠军!清华提出首个高光谱图像重建Transformer
736 0
|
机器学习/深度学习 存储 SQL
图解大数据 | 基于RDD大数据处理分析@Spark操作
RDD(弹性分布式数据集合)是Spark的基本数据结构,Spark中的所有数据都是通过RDD的形式进行组织。本文讲解RDD的属性、创建方式、广播与累加器等重要知识点,并图解RDD高频算子。
687 0
图解大数据 | 基于RDD大数据处理分析@Spark操作
|
弹性计算 Cloud Native 双11
突破边界局限,阿里云神龙负责人张献涛分享15年虚拟化之路
面对面见未来,金融大佬手动点赞阿里云神龙服务器。
2949 0
突破边界局限,阿里云神龙负责人张献涛分享15年虚拟化之路
|
开发工具 C语言 Linux
OSS 实践篇-C SDK 安装
背景: 由于很多人对 SDK 的安装和系统依赖的环境变量不是很熟悉,导致很熟悉,浪费不必要的时间,而且导致环境变量引入也出现异常。特此写了一篇从安装到遇坑的过程给大家。 操作环境: Linux Centos 6.9 64 位系统 预先安装好的库: 1、glibc-2.14 (mxml 库需要依赖这个库): 先看下 strings /lib64/libc.so.6 | grep GLIBC 是否有 GLBC-2.14 或者以上。
1525 0
OSS 实践篇-C SDK 安装