struts2实现文件上传(多文件上传)及下载 及其乱码

Posted robbinluobo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了struts2实现文件上传(多文件上传)及下载 及其乱码相关的知识,希望对你有一定的参考价值。

 

一、要实现文件上传,需在项目中添加两个jar文件

技术分享图片

二、上传准备的页面

注:必须植入enctype="multipart/form-data"属性,以及提交方式要设置成post

 

技术分享图片
 <h2>单个文件上传</h2>
    <s:form action="upload.action" enctype="multipart/form-data" method="post">
    <s:textfield name="title" label="标题"/>
    <s:file name="upload" label="选择文件"/><br/>
    <s:submit name="submit" value="上传文件"></s:submit>
    
    </s:form>
技术分享图片

实现文件上传的Action类

技术分享图片
public class UploadAction extends ActionSupport{
    //封装上传文件属性
    private File upload;
    //封装上传文件的类型
    private String uploadContentType;
    //封装上传文件名称
    private String uploadFileName;
    //获取文件上传的路径
    private String savePath;
    
    @Override
    public String execute() throws Exception {
        byte[] buffer=new byte[1024];
        
        //读取文件
        FileInputStream fis=new FileInputStream(getUpload());
        //保存文件
        FileOutputStream fos=new FileOutputStream(getSavePath()+"\\\\"+this.getUploadFileName());
        int length=fis.read(buffer);
        while(length>0){
            //每次写入length长度的内容
            fos.write(buffer,0,length);
            length=fis.read(buffer);
        }
        fis.close();
        fos.flush();
        fos.close();
        
        return SUCCESS;
    }

      //获取文件上传的保存路径 通过读取存放目录获得保存路径
       public String getSavePath() {
          return ServletActionContext.getServletContext().getRealPath(savePath);
     }

 
技术分享图片

在Action中使用了三个属性封装文件信息:

File类型的XXX属性,与表单的File控件的name属性一样,用于封装File控件对应的文件内容

String类型的xxxFileName属性,该属性名称由前面的File类型属性和FileName组合,是固定的语法,是封装File控件对应文件的文件名

String类型的XXXContentType属性,同样由xxx属性和ContentType组合而成,是固定语法,封装File控件对应文件的文件类型

配置Action:

技术分享图片
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
   <constant name="struts.devMode" value="true"></constant>
    <package name="default" namespace="/" extends="struts-default">
    <!-- 文件上传 -->
        <action name="upload" class="cn.happy.action.UploadAction">
        <!-- 通过param参数设置保存目录的路径 -->
        <param name="savePath">/upload</param>
           <result name="success">ok.jsp</result> 
        </action>
        
    </package>
</struts>
技术分享图片

上传成功后的结果页面:植入的value是Action类中所对应的实体类属性

 您所上传的文件是:<s:property value="uploadFileName"/><br/>
 文件类型:<s:property value="uploadContentType"/>

实现效果:

技术分享图片

技术分享图片


三、多文件上传

与单文件上传的不同之处在于 将三个属性的类型修改成数组类型,并通过for循环进行遍历

技术分享图片
public class SomeUploadAction extends ActionSupport {

    // 封装上传文件属性
    private File[] upload;

    // 封装上传文件的类型
    private String[] uploadContentType;

    // 封装上传文件名称
    private String[] uploadFileName;

    // 封装文件上传的路径
    private String savePath;

    public String execute() throws Exception {
        byte[] buffer = new byte[1024];
        for (int i = 0; i < upload.length; i++) {
            FileInputStream fis = new FileInputStream(getUpload()[i]);
            FileOutputStream fos = new FileOutputStream(getSavePath() + "\\\\"
                    + this.getUploadFileName()[i]);
            int length = fis.read(buffer);
            while (length > 0) {
                fos.write(buffer, 0, length);
                length = fis.read(buffer);
            }
            fos.flush();
            fos.close();
            fis.close();
        }
        return SUCCESS;
    }

      public String getSavePath() {
         return ServletActionContext.getServletContext().getRealPath(savePath);
       }

 
技术分享图片

实现效果:

技术分享图片

技术分享图片


 

 四、实现文件下载

为了支持文件的下载,Struts2框架提供了stream结果类型,该类型的作用就是专门用于实现文件下载的功能

技术分享图片

 

实现文件下载:

 由于在Struts2中实现文件下载时需要用到InputStream,所以在文件下载Action中要提供一个获得InputStream的方法,通过这个输入流将可以获取希望下载的文件内容

技术分享图片
public class DownAction extends ActionSupport{

    //读取下载文件的目录
    private String inputPath;
    //下载文件的文件名
    private String fileName;
    //读取下载文件的输入流
    private InputStream inputStream;
    //下载文件的类型
    private String conetntType;
    
    @Override
    public String execute() throws Exception {
        
        return SUCCESS;
    }
        //创建InputStream输入流
    public InputStream getInputStream() throws FileNotFoundException {
        String path=ServletActionContext.getServletContext().getRealPath(inputPath);
        return new BufferedInputStream(new FileInputStream(path+"\\\\"+fileName));
    }
技术分享图片

通过Context上下文得到下载文件的实际路径,并构建了一个InputStream输入流实现文件下载读取

Action配置:

技术分享图片
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
   <constant name="struts.devMode" value="true"></constant>
    <package name="default" namespace="/" extends="struts-default">
   
        <!-- 文件下载 -->
        
         <action name="down" class="cn.happy.action.DownAction">
        <param name="inputPath">/image</param>
           <result name="success" type="stream">
           <param name="contentType">application/octet-stream</param>
           <param name="inputName">inputStream</param>
           <param name="contentDisposition">attachment;filename="${fileName}"</param>
           <param name="bufferSize">4096</param>
           </result> 
        </action>
    </package>
</struts>
技术分享图片

在配置文件中,contentType参数决定了下载文件的类型。不同文件类型对应的参数值也是不同的。

通常情况下,contentType参数直接设置为application/octet-stream即可

contentDispoistion参数由两个部分组成,前面的部分表示处理文件的形式,如attachement表示在下载时弹出对话框,提示用户保存或者直接打开文件,而后一部分表示下载文件的名称,两部分之间以“;”进行分隔

技术分享图片

当单击超链接时,即可下载

 技术分享图片

技术分享图片技术分享图片

 

 
 -------------------------==========================-----------------------------------------
 
不过该方法只适用于IE10+和ff chrome等高版本的浏览器。在这里主要讨论文件下载,下面开始进入主题。

首先我们看看StreamResult这个类的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class StreamResult extends StrutsResultSupport {
 
    private static final long serialVersionUID = -1468409635999059850L;
 
    protected static final Logger LOG = LoggerFactory.getLogger(StreamResult.class);
 
    public static final String DEFAULT_PARAM = "inputName";
 
    protected String contentType = "text/plain";
    protected String contentLength;
    protected String contentDisposition = "inline";
    protected String contentCharSet ;
    protected String inputName = "inputStream";
    protected InputStream inputStream;
    protected int bufferSize = 1024;
    protected boolean allowCaching = true;
从该类的字段来看基本是围绕一个http响应头的设置,包括mineType,缓冲区大小,字符编码,下载的文件名等的设置。我们可以在Action中注入这些要用的字段值就可以了,当然需要在strut.xml中通过el动态配置。下面给出一个实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class FileDownloadAction extends ActionSupport implements
        ServletRequestAware {
    private HttpServletRequest request;
    private String inputPath;
    private String fileName;
    private String target;
    private String mimeType;
    private String rootPath;
    private static Logger logger = Logger.getLogger(FileDownloadAction.class
            .getName());
 
    /**
     * 注入输入流
     *
     * @return
     * @throws Exception
     */
    public InputStream getTargetFile() throws Exception {
        // 获取目标文件类型
        mimeType = ServletActionContext.getServletContext().getMimeType(
                inputPath);
        String browserType = ServletActionContext.getRequest().getHeader(
                "User-Agent");
        // IE浏览器编码方式是URLEncoder编码
        boolean isIE11 = browserType.contains("rv") && browserType.contains("Trident");
        if (browserType.contains("MSIE") || isIE11) {
            fileName = URLEncoder.encode(fileName, "ISO8859-1");
        }
        return new FileInputStream(inputPath);
    }
 
    @Override
    public String execute() throws Exception {
        rootPath = request.getSession().getServletContext().getRealPath("/");
        try {      
                // 获取关键路径
                String[] sep = inputPath.split(":");
                String userId = (String) ActionContext.getContext()
                        .getSession().get("userId");
                if (userId == null) {// 登陆控制
                    return "mainPage";
                }
                inputPath = rootPath + "//WEB-INF//uploadify-files//document//"
                        + sep[0] + "//" + sep[1] + "//" + sep[2] + "//"
                        + sep[3];
                fileName = new String(sep[3].getBytes("UTF-8"), "ISO8859-1");
            return "success";
        } catch (Exception e) {
            e.printStackTrace();
            return "errorPage";
        }
    }
然后在xml中进行配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 文件下载 -->
        <action name="fileDownload"
            class="com.bjhit.eranges.actions.download.FileDownloadAction">
            <!-- 指定被下载资源的位置 -->
            <param name="inputPath">${inputPath}</param>
            <!-- 配置结果类型为stream的结果 -->
            <result name="success" type="stream">
                <!-- 指定下载文件的文件类型 -->
                <param name="contentType">${mimeType}</param>
                <!-- 指定由getTargetFile()方法返回被下载文件的inputStream -->
                <param name="inputName">targetFile</param>
                <param name="contentDisposition">attachment;filename="${fileName}"</param>
                <!-- 指定下载文件的缓冲大小 -->
                <param name="bufferSize">1024</param>
            </result>
        </action>
这里的注入方式是get,所以响应的字段要有get方法。在处理下载时中文文件名乱码的时候要主要IE的编码方式是URL编码的,要单独转换一下。然后get请求乱码的处理这里采用的是配置tomcat的URI编码来做的,否则需要自己进行编码解码。tomcat配置如下:
1
<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" useBodyEncodingForURI="true"/>
总结:文件下载主要是考虑好存放路劲和访问控制,最好不要将敏感类型文件暴露在公众可以访问的目录,最好放web-inf或者本地文件系统。中文乱码主要是get请求乱码造成找不到文件,或者因为不同浏览器编码方式不一样导致中文文件名下载是乱码。解决办法是设置好tomcat服务器的URI编码方式为UTF-8,项目统一使用该编码方式,然后针对不同的浏览器采用不同的编码方式实现中文文件名的下载。

简单记录下,以便将来查阅。

文件的上传在此不多做解说,大家可以参考我写的这篇html5无插件的方式实现:http://my.oschina.net/gongxufan/blog/189824。不过该方法只适用于IE10+和ff chrome等高版本的浏览器。在这里主要讨论文件下载,下面开始进入主题。

首先我们看看StreamResult这个类的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class StreamResult extends StrutsResultSupport {
 
    private static final long serialVersionUID = -1468409635999059850L;
 
    protected static final Logger LOG = LoggerFactory.getLogger(StreamResult.class);
 
    public static final String DEFAULT_PARAM = "inputName";
 
    protected String contentType = "text/plain";
    protected String contentLength;
    protected String contentDisposition = "inline";
    protected String contentCharSet ;
    protected String inputName = "inputStream";
    protected InputStream inputStream;
    protected int bufferSize = 1024;
    protected boolean allowCaching = true;

从该类的字段来看基本是围绕一个http响应头的设置,包括mineType,缓冲区大小,字符编码,下载的文件名等的设置。我们可以在Action中注入这些要用的字段值就可以了,当然需要在strut.xml中通过el动态配置。下面给出一个实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class FileDownloadAction extends ActionSupport implements
        ServletRequestAware {
    private HttpServletRequest request;
    private String inputPath;
    private String fileName;
    private String target;
    private String mimeType;
    private String rootPath;
    private static Logger logger = Logger.getLogger(FileDownloadAction.class
            .getName());
 
    /**
     * 注入输入流
     *
     * @return
     * @throws Exception
     */
    public InputStream getTargetFile() throws Exception {
        // 获取目标文件类型
        mimeType = ServletActionContext.getServletContext().getMimeType(
                inputPath);
        String browserType = ServletActionContext.getRequest().getHeader(
                "User-Agent");
        // IE浏览器编码方式是URLEncoder编码
        boolean isIE11 = browserType.contains("rv") && browserType.contains("Trident");
        if (browserType.contains("MSIE") || isIE11) {
            fileName = URLEncoder.encode(fileName, "ISO8859-1");
        }
        return new FileInputStream(inputPath);
    }
 
    @Override
    public String execute() throws Exception {
        rootPath = request.getSession().getServletContext().getRealPath("/");
        try {      
                // 获取关键路径
                String[] sep = inputPath.split(":");
                String userId = (String) ActionContext.getContext()
                        .getSession().get("userId");
                if (userId == null) {// 登陆控制
                    return "mainPage";
                }
                inputPath = rootPath + "//WEB-INF//uploadify-files//document//"
                        + sep[0] + "//" + sep[1] + "//" + sep[2] + "//"
                        + sep[3];
                fileName = new String(sep[3].getBytes("UTF-8"), "ISO8859-1");
            return "success";
        } catch (Exception e) {
            e.printStackTrace();
            return "errorPage";
        }
    }

然后在xml中进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 文件下载 -->
        <action name="fileDownload"
            class="com.bjhit.eranges.actions.download.FileDownloadAction">
            <!-- 指定被下载资源的位置 -->
            <param name="inputPath">${inputPath}</param>
            <!-- 配置结果类型为stream的结果 -->
            <result name="success" type="stream">
                <!-- 指定下载文件的文件类型 -->
                <param name="contentType">${mimeType}</param>
                <!-- 指定由getTargetFile()方法返回被下载文件的inputStream -->
                <param name="inputName">targetFile</param>
                <param name="contentDisposition">attachment;filename="${fileName}"</param>
                <!-- 指定下载文件的缓冲大小 -->
                <param name="bufferSize">1024</param>
            </result>
        </action>

这里的注入方式是get,所以响应的字段要有get方法。在处理下载时中文文件名乱码的时候要主要IE的编码方式是URL编码的,要单独转换一下。然后get请求乱码的处理这里采用的是配置tomcat的URI编码来做的,否则需要自己进行编码解码。tomcat配置如下:

1
<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" useBodyEncodingForURI="true"/>

总结:文件下载主要是考虑好存放路劲和访问控制,最好不要将敏感类型文件暴露在公众可以访问的目录,最好放web-inf或者本地文件系统。中文乱码主要是get请求乱码造成找不到文件,或者因为不同浏览器编码方式不一样导致中文文件名下载是乱码。解决办法是设置好tomcat服务器的URI编码方式为UTF-8,项目统一使用该编码方式,然后针对不同的浏览器采用不同的编码方式实现中文文件名的下载。

简单记录下,以便将来查阅。






以上是关于struts2实现文件上传(多文件上传)及下载 及其乱码的主要内容,如果未能解决你的问题,请参考以下文章

[原创]java WEB学习笔记72:Struts2 学习之路-- 文件的上传下载,及上传下载相关问题

文件上传(多文件上传)/下载

struts2实现文件上传和下载

Web上传文件的原理及实现

Struts2学习—文件上传和下载

Struts2文件上传