`

struts2 文件下载 修正中文问题

阅读更多

在BlogJava上已经有一位作者阐述了文件上传的问题,地址是在Struts 2中实现文件上传 ,因此我就不再讨论那个话题了。我今天简单介绍一下Struts 2的文件下载问题。

我们的项目名为 struts2hello ,所使用的开发环境是MyEclipse 6,当然其实用哪个IDE都是一样的,只要把类库放进去就行了,文件下载不需要再加入任何额外的包。读者可以参考文档:http://beansoft.java-cn.org/myeclipse_doc_cn/struts2_demo.pdf ,来了解怎么下载和配置基本的Struts 2开发环境。

为了便于大家对比,我把完整的struts.xml的配置信息列出来:

Xml代码 复制代码
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE struts PUBLIC   
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"   
  4.     "http://struts.apache.org/dtds/struts-2.0.dtd">  
  5.   
  6. <struts>  
  7.     <package name="default" extends="struts-default"  >  
  8.         <!-- 在这里添加Action定义 -->  
  9.   
  10.         <!-- 简单文件下载 -->  
  11.         <action name="download" class="example.FileDownloadAction">  
  12.             <result name="success" type="stream">  
  13.                 <param name="contentType">text/plain</param>  
  14.                 <param name="inputName">inputStream</param>  
  15.                 <param name="contentDisposition">attachment;filename="struts2中文.txt"</param>  
  16.                 <param name="bufferSize">4096</param>  
  17.             </result>  
  18.         </action>  
  19.            
  20.         <!-- 文件下载,支持中文附件名 -->  
  21.         <action name="download2" class="example.FileDownloadAction2">  
  22.             <!-- 初始文件名 -->  
  23.             <param name="fileName">Struts中文附件.txt</param>  
  24.             <result name="success" type="stream">  
  25.                 <param name="contentType">text/plain</param>  
  26.                 <param name="inputName">inputStream</param>  
  27.                 <!-- 使用经过转码的文件名作为下载文件名,downloadFileName属性   
  28.                 对应action类中的方法 getDownloadFileName() -->  
  29.                 <param name="contentDisposition">attachment;filename="${downloadFileName}"</param>  
  30.                 <param name="bufferSize">4096</param>  
  31.             </result>  
  32.         </action>  
  33.            
  34.         <!-- 下载现有文件 -->  
  35.         <action name="download3" class="example.FileDownloadAction3">  
  36.             <param name="inputPath">/download/系统说明.doc</param>  
  37.             <!-- 初始文件名 -->  
  38.             <param name="fileName">系统说明.doc</param>  
  39.             <result name="success" type="stream">  
  40.                 <param name="contentType">application/octet-stream;charset=ISO8859-1</param>  
  41.                 <param name="inputName">inputStream</param>  
  42.                 <!-- 使用经过转码的文件名作为下载文件名,downloadFileName属性   
  43.                 对应action类中的方法 getDownloadFileName() -->  
  44.                 <param name="contentDisposition">attachment;filename="${downloadFileName}"</param>  
  45.                 <param name="bufferSize">4096</param>  
  46.             </result>  
  47.         </action>  
  48.            
  49.     </package>  
  50.   
  51. </struts>  
<?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="default" extends="struts-default"  >
        <!-- 在这里添加Action定义 -->

        <!-- 简单文件下载 -->
        <action name="download" class="example.FileDownloadAction">
            <result name="success" type="stream">
                <param name="contentType">text/plain</param>
                <param name="inputName">inputStream</param>
                <param name="contentDisposition">attachment;filename="struts2中文.txt"</param>
                <param name="bufferSize">4096</param>
            </result>
        </action>
        
        <!-- 文件下载,支持中文附件名 -->
        <action name="download2" class="example.FileDownloadAction2">
            <!-- 初始文件名 -->
            <param name="fileName">Struts中文附件.txt</param>
            <result name="success" type="stream">
                <param name="contentType">text/plain</param>
                <param name="inputName">inputStream</param>
                <!-- 使用经过转码的文件名作为下载文件名,downloadFileName属性
                对应action类中的方法 getDownloadFileName() -->
                <param name="contentDisposition">attachment;filename="${downloadFileName}"</param>
                <param name="bufferSize">4096</param>
            </result>
        </action>
        
        <!-- 下载现有文件 -->
        <action name="download3" class="example.FileDownloadAction3">
            <param name="inputPath">/download/系统说明.doc</param>
            <!-- 初始文件名 -->
            <param name="fileName">系统说明.doc</param>
            <result name="success" type="stream">
                <param name="contentType">application/octet-stream;charset=ISO8859-1</param>
                <param name="inputName">inputStream</param>
                <!-- 使用经过转码的文件名作为下载文件名,downloadFileName属性
                对应action类中的方法 getDownloadFileName() -->
                <param name="contentDisposition">attachment;filename="${downloadFileName}"</param>
                <param name="bufferSize">4096</param>
            </result>
        </action>
        
    </package>

</struts>

 

Struts 2中对文件下载做了直接的支持,相比起自己辛辛苦苦的设置种种HTTP头来说,现在实现文件下载无疑要简便的多。说起文件下载,最直接的方式恐怕是直接写一个超链接,让地址等于被下载的文件,例如:<a href=”file1.zip”> 下载file1.zip</a> ,之后用户在浏览器里面点击这个链接,就可以进行下载了。但是它有一些缺陷,例如如果地址是一个图片,那么浏览器会直接打开它,而不是显示保存文件的对话框。再比如如果文件名是中文的,它会显示一堆URL编码过的文件名例如%3457...。而假设你企图这样下载文件:http://localhost:8080/struts2hello/download/系统说明.doc ,Tomcat会告诉你一个文件找不到的404错误:HTTP Status 404 - /struts2hello/download/ϵͳ˵Ã÷.doc 。虽然目前还没发现直接配置Struts 2来正确的下载中文名字的附件,不过好在作者对JSP中的文件下载比较了解,因此我们另有办法解决这个问题。另外一个最大的用途,就是动态的生成并下载文件了,例如动态的下载生成的EXCEL,PDF,验证码图片等等。本节内容就依次讨论简单的下载文件代码,下载中文附件,最后介绍如何下载已经存在的文件。

先说文件下载,编写一个普通的Action就可以了,只需要提供一个返回InputStream流的方法,该输入流代表了被下载文件的入口,这个方法用来给被下载的数据提供输入流,意思是从这个流读出来,再写到浏览器那边供下载。这个方法需要由开发人员自己来编写,只需要返回值为InputStream即可。在我们的例子中方法的签名是:public InputStream getInputStream() throws Exception ,当然它也可以是别的名字,例如getDownloadFile() 。好了,现在我们所写的这个进行文件下载的Action类example.FileDownloadAction 的源代码清单如下:

Java代码 复制代码
  1. package example;    
  2.   
  3. import java.io.ByteArrayInputStream;    
  4.   
  5. import java.io.InputStream;    
  6.   
  7. import com.opensymphony.xwork2.Action;    
  8.   
  9. public class FileDownloadAction implements Action {    
  10.   
  11.     public InputStream getInputStream() throws Exception {    
  12.   
  13.         return new ByteArrayInputStream("Struts 2 下载示例".getBytes());    
  14.   
  15.     }    
  16.   
  17.     public String execute() throws Exception {    
  18.   
  19.         return SUCCESS;    
  20.   
  21.     }    
  22.   
  23. }   
package example; 

import java.io.ByteArrayInputStream; 

import java.io.InputStream; 

import com.opensymphony.xwork2.Action; 

public class FileDownloadAction implements Action { 

	public InputStream getInputStream() throws Exception { 

		return new ByteArrayInputStream("Struts 2 下载示例".getBytes()); 

	} 

	public String execute() throws Exception { 

		return SUCCESS; 

	} 

} 

 注意这里唯一特殊的方法就是getInputStream() ,在这个方法里面我们使用了一个数组输入流来从字符串转换成的数组作为数据的来源进行读取。也许方法体中使用这样的实现代码:

Java代码 复制代码
  1. return new java.io.FileInputStream(“c:\\test.txt”);//从系统磁盘文件读取数据   
return new java.io.FileInputStream(“c:\\test.txt”);//从系统磁盘文件读取数据 

 

这样会更直观一些。

文件下载的第二步,乃是在struts.xml 中对action进行配置,其代码清单如下所示:

Xml代码 复制代码
  1. <!-- 简单文件下载 -->    
  2.   
  3. <action name="download" class="example.FileDownloadAction">    
  4.   
  5.     <result name="success" type="stream">    
  6.   
  7.         <param name="contentType">text/plain</param>    
  8.   
  9.         <param name="inputName">inputStream</param>    
  10.   
  11.         <param name="contentDisposition">attachment;filename="struts2.txt"</param>    
  12.   
  13.         <param name="bufferSize">4096</param>    
  14.   
  15.     </result>    
  16.   
  17. </action>   
<!-- 简单文件下载 --> 

<action name="download" class="example.FileDownloadAction"> 

	<result name="success" type="stream"> 

		<param name="contentType">text/plain</param> 

		<param name="inputName">inputStream</param> 

		<param name="contentDisposition">attachment;filename="struts2.txt"</param> 

		<param name="bufferSize">4096</param> 

	</result> 

</action> 

 这个action特殊的地方在于result的类型是一个流(stream ),配置stream类型的结果时,因为无需指定实际的显示的物理资源,所以无需指定location 属性,只需要指定inputName 属性,该属性指向被下载文件的来源,对应着Action类中的某个属性,类型为InputStream。下面则列出了和下载有关的一些参数列表:

参数

说明

contentType

内容类型,和互联网MIME标准中的规定类型一致,例如text/plain代表纯文本,text/xml表示XML,image/gif代表GIF图片,image/jpeg代表JPG图片

inputName

下载文件的来源流,对应着action类中某个类型为Inputstream的属性名,例如取值为inputStream 的属性需要编写getInputStream()方法

contentDisposition

文件下载的处理方式,包括内联(inline)和附件(attachment)两种方式,而附件方式会弹出文件保存对话框,否则浏览器会尝试直接显示文件。取值为:

attachment;filename="struts2.txt" ,表示文件下载的时候保存的名字应为struts2.txt 。如果直接写filename="struts2.txt" ,那么默认情况是代表inline ,浏览器会尝试自动打开它,等价于这样的写法:inline; filename="struts2.txt"

bufferSize

下载缓冲区的大小

在这里面,contentType 属性和contentDisposition 分别对应着HTTP响应中的头Content-TypeContent-disposition 头。好,我们先来看看这个例子,发布运行项目后键入测试地址:http://localhost:8080/struts2hello/download.action ,将会看到浏览器弹出一个文件保存对话框,如图12.12所示。

clip_image002

clip_image004

图12.12 文件下载对话框(IE 7和Firefox 3)

如果此时使用某些工具来探测浏览器返回的HTTP头,将会看到下列内容:

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-disposition: attachment;filename="struts2.txt"

Content-Type: text/plain

Transfer-Encoding: chunked

Date: Sun, 02 Mar 2008 02:58:25 GMT

。所以用来下载的action配置中,只有两个是和浏览器有关的:contentTypecontentDisposition 。关于contentType 的取值,如果是未知的文件类型,或者说出现了浏览器不能打开的文件,例如.bean 文件,或者说这个action是用来做动态文件下载的,事先并不知道未来的文件类型是什么,那么我们可以把它的值设置成为:application/octet-stream;charset=ISO8859-1 ,注意一定要加入charset ,否则某些时候会导致下载的文件出错;有人说这时也可以设置成为application/x-download ,根据笔者的实践,这个头也能正常工作,然而个别时候会出现浏览器无法识别的问题。而contentDisposition ,如果其取值是filename="struts2.txt" ,或者是inline; filename="struts2.txt" ,运行后你可以看到浏览器直接显示了文件的内容:

Struts 2 下载示例 ,而不再弹出对话框提示用户保存文件到硬盘上。所以读者如果想确保文件是被下载而不是被打开,务必使用格式attachment ;filename="struts2.txt" ,不要丢了attachment; 这个类型信息。

至此,关于文件下载的技术内容,已经告一段落。然而做中文系统,不可避免的要解决中文附件的下载问题。关于这个内容,也无权威的资料可查,我们只能用实践中得到的解决方案来处理。也许有读者以为将filename 属性设置为filename=”struts2 中文.txt” 就能解决问题了,好,就来试试,把contentDisposition修改成:

<param name="contentDisposition">attachment;filename="struts2 中文.txt"</param>

。再次键入地址进行测试,看看显示的结果,如图12.13所示。唉,真是完全不给面子!IE压根就不能显示出来文件名,草草敷衍了download_action了事。Firefox稍好点,还出来了一个对话框,但是很显然,那个显示的struts2--txt 绝对不是我们日思夜想的struts2 中文.txt 。怎么办?解决方法是有,那就是用ISO8859-1编码来显示这个中文字符,可以阅读12.8 参考资料 一节中的JSP 文件下载的相对完整代码( 解决中文问题和Weblogic 报错 )这篇文章,可以这样认为,所有的文件下载代码都是基于同样的纯Servlet的方式来进行的。如果是Java代码,我们可以这样做:

clip_image006

clip_image008

图12.13 IE和Firefox下的中文文件下载对话框

String downFileName = new String(“struts2 中文.txt”.getBytes(), "ISO8859-1");

然后把生成的结果字符串放到XML文件中就行了,然而它的输出类似于struts2??.txt ,是无法直接写道我们的XML配置文件中的。所以,我们想到的的办法,就是在Action类中写一个方法来做转码,使它成为某个属性,所以要以get开头。然后,再用12.3.8 给Action 注入参数(param )值 一节的内容,将文件名以正常的方式设置为action类的某个属性,最后呢,再利用一个小小的param参数取值中的伎俩:${ 属性名} ,它可以直接从action类中动态获取某个属性值。好了,现在让我们来看看第二个文件下载类FileDownloadAction2 的代码:

Java代码 复制代码
  1. package example;    
  2.   
  3. import java.io.ByteArrayInputStream;    
  4.   
  5. import java.io.InputStream;    
  6.   
  7. import java.io.UnsupportedEncodingException;    
  8.   
  9. import com.opensymphony.xwork2.Action;    
  10.   
  11. public class FileDownloadAction2 implements Action {    
  12.   
  13.     private String fileName;// 初始的通过param指定的文件名属性    
  14.   
  15.     public InputStream getInputStream() throws Exception {    
  16.   
  17.         return new ByteArrayInputStream("Struts 2 下载示例".getBytes());    
  18.   
  19.     }    
  20.   
  21.     public String execute() throws Exception {    
  22.   
  23.         return SUCCESS;    
  24.   
  25.     }    
  26.   
  27.     public void setFileName(String fileName) {    
  28.   
  29.         this.fileName = fileName;    
  30.   
  31.     }    
  32.   
  33.     /** 提供转换编码后的供下载用的文件名 */    
  34.   
  35.     public String getDownloadFileName() {    
  36.   
  37.         String downFileName = fileName;    
  38.   
  39.         try {    
  40.   
  41.             downFileName = new String(downFileName.getBytes(), "ISO8859-1");    
  42.   
  43.         } catch (UnsupportedEncodingException e) {    
  44.   
  45.             e.printStackTrace();    
  46.   
  47.         }    
  48.   
  49.         return downFileName;    
  50.   
  51.     }    
  52.   
  53. }   
package example; 

import java.io.ByteArrayInputStream; 

import java.io.InputStream; 

import java.io.UnsupportedEncodingException; 

import com.opensymphony.xwork2.Action; 

public class FileDownloadAction2 implements Action { 

	private String fileName;// 初始的通过param指定的文件名属性 

	public InputStream getInputStream() throws Exception { 

		return new ByteArrayInputStream("Struts 2 下载示例".getBytes()); 

	} 

	public String execute() throws Exception { 

		return SUCCESS; 

	} 

	public void setFileName(String fileName) { 

		this.fileName = fileName; 

	} 

	/** 提供转换编码后的供下载用的文件名 */ 

	public String getDownloadFileName() { 

		String downFileName = fileName; 

		try { 

			downFileName = new String(downFileName.getBytes(), "ISO8859-1"); 

		} catch (UnsupportedEncodingException e) { 

			e.printStackTrace(); 

		} 

		return downFileName; 

	} 

} 

 这个类有两个属性,第一个是fileName ,它是需要被指定的下载文件名;第二个则是动态的仅仅由getDownloadFileName() 这个方法定义的属性downloadFileName ,它的值随着fileName而动态变动,仅仅是把它转换成了ISO8859方式的西欧字符集。

接下来就是如何配置这个action了,这是关键的地方所在,现在配置一个新的action,名为download2 ,其源代码如下:

Xml代码 复制代码
  1. <!-- 文件下载,支持中文附件名 -->    
  2.   
  3. <action name="download2" class="example.FileDownloadAction2">    
  4.   
  5.     <!-- 初始文件名 -->    
  6.   
  7.     <param name="fileName">Struts中文附件.txt</param>    
  8.   
  9.     <result name="success" type="stream">    
  10.   
  11.         <param name="contentType">text/plain</param>    
  12.   
  13.         <param name="inputName">inputStream</param>    
  14.   
  15.         <!-- 使用经过转码的文件名作为下载文件名,downloadFileName属性    
  16.   
  17.         对应action类中的方法 getDownloadFileName() -->    
  18.   
  19.         <param name="contentDisposition">attachment;filename="${downloadFileName}"</param>    
  20.   
  21.         <param name="bufferSize">4096</param>    
  22.   
  23.     </result>    
  24.   
  25. </action>   
<!-- 文件下载,支持中文附件名 --> 

<action name="download2" class="example.FileDownloadAction2"> 

	<!-- 初始文件名 --> 

	<param name="fileName">Struts中文附件.txt</param> 

	<result name="success" type="stream"> 

		<param name="contentType">text/plain</param> 

		<param name="inputName">inputStream</param> 

		<!-- 使用经过转码的文件名作为下载文件名,downloadFileName属性 

		对应action类中的方法 getDownloadFileName() --> 

		<param name="contentDisposition">attachment;filename="${downloadFileName}"</param> 

		<param name="bufferSize">4096</param> 

	</result> 

</action> 

 其中特殊的代码就是${downloadFileName} ,它的效果相当于运行的时候将action对象的属性的取值动态的填充在${} 中间的部分,我们可以认为它等价于 action. getDownloadFileName()

好了,现在让我们重新发布然后运行这个项目,键入地址:

http://localhost:8080/struts2hello/download2.action 进行访问,可以看到运行结果完全正确,如图12.14所示。

clip_image010

clip_image012

图 12.14 正确显示了文件下载名的对话框(IE和Firefox)

在本节的最后部分,我们来讨论一下如何下载已经存在于当前Web应用目录下的已经存在的文件。一般的网站可能会把要下载的文件放在某个固定的目录下,例如WebRoot/download ,在这个子目录下,我们放了一个名为系统说明 .doc 的文件,希望最后我们的action能够正确的下载这个文件。要检验下载是否成功非常简单,文件内容仅仅是粗体的系统说明书 这五个字,而word文件坏一个字节的话都是打不开的,所以下载后再用word打开即可检验是否成功。现在我们创建第三个文件下载的Action类,名为example. FileDownloadAction3 ,其源代码清单如下所示:

Java代码 复制代码
  1. package example;    
  2.   
  3. import java.io.InputStream;    
  4.   
  5. import java.io.UnsupportedEncodingException;    
  6.   
  7. import org.apache.struts2.ServletActionContext;    
  8.   
  9. import com.opensymphony.xwork2.Action;    
  10.   
  11. public class FileDownloadAction3 implements Action {    
  12.   
  13.     private String fileName;// 初始的通过param指定的文件名属性    
  14.   
  15.     private String inputPath;// 指定要被下载的文件路径    
  16.   
  17.   
  18.     public InputStream getInputStream() throws Exception {    
  19.   
  20.         // 通过 ServletContext,也就是application 来读取数据    
  21.   
  22.         <span class
    分享到:
    评论

相关推荐

    Struts2中实现文件下载(修正中文问题).pdf

    本篇文章将详细介绍如何在Struts2中实现文件下载,并解决中文文件名的编码问题。 首先,我们需要在`struts.xml`配置文件中定义一个Action,这个Action将会处理文件下载的请求。例如,我们可以创建一个名为`...

    struts2官方中文帮助文档最新修复Bug版(绝版)

    Struts2官方中文帮助文档是开发者了解和学习该框架的重要参考资料,其最新修复Bug版更是对原有文档的错误进行了修正,提升了学习和使用的体验。 Struts2框架的主要特性包括: 1. **Action类**:每个业务逻辑操作...

    Struts in Action 中文修正版

    总之,《Struts in Action 中文修正版》是一本全面的Struts教程,不仅适合初学者了解和掌握Struts的基本用法,也对有经验的开发者在解决实际问题时提供了宝贵参考。通过阅读此书,你可以深入理解Struts框架的工作...

    Struts in Action 中文修正版源代码2

    这个压缩包文件"Struts in Action 中文修正版源代码2"包含的"husted_src"目录,很可能对应于书作者Husted的章节或示例代码。这部分代码可能涉及以下几个关键知识点: 1. **ActionServlet配置**:在`struts-config....

    Struts In Action中文修正版

    **Struts In Action** 是一本专门探讨Apache Struts框架的权威书籍,中文修正版则针对原版英文内容进行了翻译和校对,以适应中文读者的需求。这本书为初学者提供了全面而深入的学习路径,帮助他们理解和掌握如何使用...

    Struts in Action 中文修正版.

    Struts in Action 是一本专注于Apache Struts框架的权威指南,中文修正版则为原版英文书籍的翻译并修正了可能出现的翻译错误,旨在为中国读者提供更准确、更易理解的Struts学习资料。Struts是Java Web开发中的一个...

    Struts in Action 中文修正版.pdf

    ### Struts in Action 中文修正版.pdf 知识点概览 #### 1. Struts 概述 **Struts** 是一个流行的 Java Web 应用框架,它遵循 MVC(Model-View-Controller)设计模式,帮助开发者构建结构清晰、易于维护的 Web 应用...

    Struts in Action 中文修正版(电子书)

    Struts in Action 是一本专注于Java Web开发框架Struts的经典书籍,中文修正版为国内开发者提供了更易理解的阅读体验。这本书全面介绍了如何使用Struts来构建高效、可维护的Web应用程序,是Java开发者深入理解Struts...

    国际化与struts2

    文件`struts2_properties_20150922_guojihua2`很可能包含了Struts2的国际化配置示例,可能是针对2015年9月22日的一个更新或修正。文件名中的“guojihua2”可能是对这个版本的特定描述,可能指的是第二次国际化的改进...

    Struts in Action 中文修正版源代码1

    这本书的中文修正版源代码1提供了与书中的示例和教程对应的代码资源,帮助读者更好地理解和学习Struts框架的工作机制。 Apache Struts是一个基于Model-View-Controller (MVC)设计模式的开源Java Web应用程序框架。...

    解决struts2的s:checkbox显示难看问题

    在Struts2框架中,`s:checkbox`标签用于创建复选框,但在实际使用过程中,用户可能会遇到显示效果不理想的问题。这个问题通常涉及到样式、布局或者与其他UI组件的兼容性。本文将深入探讨如何解决Struts2的`s:...

    Struts in Action 中英文对照

    2. "Struts in Action 中文修正版.pdf":这是中文翻译版,适合那些希望用母语学习Struts的读者。修正版意味着译者已经对翻译进行了校对,提高了翻译质量,确保读者能够准确理解Struts的原理和实践。 通过阅读这两份...

Global site tag (gtag.js) - Google Analytics