Struts2学习—文件上传和下载

Posted mingxin95

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2学习—文件上传和下载相关的知识,希望对你有一定的参考价值。

在做B/S系统时,通常会涉及到上传文件和下载文件,在没接struts2框架之前,我们都是使用apache下面的commons子项目的FileUpload组件来进行文件的上传,但是那样做的话,代码看起来比较繁琐,而且不灵活,在学习了struts2后,struts2为文件上传下载提供了更好的实现机制,在这里我分别就单文件上传和多文件上传的实现进行一下讲解,这里
我们使用的struts2 web项目所导入的jar包中的**commons-fileupload-1.3.1.jar
commons-io-2.2.jar**是struts2实现上传下载所使用的类库

struts2单文件上传

原理

  • 确保 html设置了 enctype和指定了一个活多个 file type input.

jsp页面表单的书写格式

<form name="myForm" enctype="multipart/form-data">
     <input type="file" name="myDoc" value="Browse ..." />
     <input type="submit" />
  </form>
  • 确保 Action提供一个或多个 fileUpload 只读(mutator )方法, 并且方法名t与 file type input名称相关.
public void setMyDoc(File myDoc)
public void setMyDocContentType(String contentType)
public void setMyDocFileName(String filename)
  • Action 可能提供相关联的访问器方法 .
public File getMyDoc()
public String getMyDocContentType()
public String getMyDocFileName()

示例

首先是一个jsp文件上传页面,这个比较简单,就是一个表单,里面有个文件上传框

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!--在进行文件上传时,表单提交方式一定要是post的方式,因为文件上传时二进制文件可能会很大,还有就是enctype属性,这个属性一定要写成multipart/form-data,
  不然就会以二进制文本上传到服务器端--> 
<form action="upload" method="post" enctype="multipart/form-data">
上传文件:<input name="photo" type="file"><br>
<input type="submit">
</form>
</body>
</html>

接下来是FileUploadAction部分代码,因为struts2对上传和下载都提供了很好的实习机制,所以在action这段我们只需要写很少的代码就行:
FileUploadAction.java

package com.lgh.struts2.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

import sun.nio.ch.IOUtil;

public class FileUploadAction extends ActionSupport {

    private static final long serialVersionUID = 1L;

    private File photo;
    private String photoContentType;
    private String photoFileName;

    @Override
    public String execute() throws Exception {
        InputStream is = new FileInputStream(photo);
        String path = ServletActionContext.getServletContext().getRealPath("/upload");
        String ext = photoFileName.substring(photoFileName.lastIndexOf(".")+1);
        System.out.println(ext);
        String filePath = path+"/"+UUID.randomUUID()+"."+ext;
        OutputStream os = new FileOutputStream(filePath);
        System.out.println(path);
        IOUtils.copy(is, os);
        os.close();
        is.close();
        return super.execute();
    }
    public File getPhoto() {
        return photo;
    }
    public void setPhoto(File photo) {
        this.photo = photo;
    }
    public String getPhotoContentType() {
        return photoContentType;
    }
    public void setPhotoContentType(String photoContentType) {
        this.photoContentType = photoContentType;
    }
    public String getPhotoFileName() {
        return photoFileName;
    }
    public void setPhotoFileName(String photoFileName) {
        this.photoFileName = photoFileName;
    }



}

上传成功的页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:debug></s:debug>
</body>
</html>

action配置

  <action name="upload*" class="com.lgh.struts2.action.FileUpload{1}Action"
            method="execute">
            <result name="success">/upload_sucess.jsp</result>
        </action>

测试
这里写图片描述
提交后进入上传文件夹中:
这里写图片描述

处理多个文件

当通过一个表单上传多个文件, 文件用数组来表示.

原理 

给定:

<form name="myForm" enctype="multipart/form-data">
      <input type="file" name="myDoc" value="Browse File A ..." />
      <input type="file" name="myDoc" value="Browse File B ..." />
      <input type="file" name="myDoc" value="Browse File C ..." />
      <input type="submit" />
   </form>

Action 类定义了文件处理方法,这些方法接收数组参数.

public void setMyDoc(File[] myDocs)
public void setMyDocContentType(String[] contentTypes)
public void setMyDocFileName(String[] fileNames)

通过迭代数组来处理多个文件上传.

示例

fileuploadMuti.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="uploadMuti" method="post" enctype="multipart/form-data">
上传文件:<input name="photo" type="file"><br>
上传文件:<input name="photo" type="file"><br>
<input type="submit">
</form>
</body>
</html>

FileUploadMutiAction.java

package com.lgh.struts2.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

import sun.nio.ch.IOUtil;

public class FileUploadMutiAction extends ActionSupport {

    private static final long serialVersionUID = 1L;

    private File[] photo;
    private String[] photoContentType;
    private String[] photoFileName;

    @Override
    public String execute() throws Exception {

        for (int i = 0; i < photo.length; i++) {
                InputStream is = new FileInputStream(photo[i]);
        String path = ServletActionContext.getServletContext().getRealPath("/upload");
        String ext = photoFileName[i].substring(photoFileName[i].lastIndexOf(".")+1);
        System.out.println(ext);
        String filePath = path+"/"+UUID.randomUUID()+"."+ext;
        OutputStream os = new FileOutputStream(filePath);
        System.out.println(path);
        IOUtils.copy(is, os);
        os.close();
        is.close();

        }
        return super.execute();
    }

    public File[] getPhoto() {
        return photo;
    }

    public void setPhoto(File[] photo) {
        this.photo = photo;
    }

    public String[] getPhotoContentType() {
        return photoContentType;
    }

    public void setPhotoContentType(String[] photoContentType) {
        this.photoContentType = photoContentType;
    }

    public String[] getPhotoFileName() {
        return photoFileName;
    }

    public void setPhotoFileName(String[] photoFileName) {
        this.photoFileName = photoFileName;
    }




}

action配置:

  <action name="upload*" class="com.lgh.struts2.action.FileUpload{1}Action"
            method="execute">
            <result name="success">/upload_sucess.jsp</result>
        </action>

测试:
这里写图片描述
提交后
这里写图片描述

额外信息

属性 默认值
struts.multipart.parser 使用Commons FileUpload 框架
struts.multipart.saveDir 容器定义的javax.servlet.context.tempdir
设置上传文件的最大值
struts.multipart.maxSize 大约2M

文件下载

参数

文件上传后我们还需要将其下载下来,其实struts2的文件下载原理很简单,就是定义一个输入流,然后将文件写到输入流里面就行,关键配置还是在struts.xml这个配置文件里配置以下参数:

  • contentType - 告诉浏览器的流的 mime-type (默认 = text/plain).

  • contentLength - 按字节计算的流长度(浏览器显示进度条).

  • contentDisposition -内容的 disposition 头值来指定文件的名称 (默认 = inline, 值通常是 attachment;filename=”document.pdf”.

  • inputName - the name of the InputStream property from the chained action (默认 = inputStream).

  • bufferSize - 从输入往输出拷贝时缓存的大小 (默认 = 1024).

  • allowCaching -如果设置为 ‘false’ 将设置头 ‘Pragma’ 和 ‘Cache-Control’为’no-cahce’, 并阻止客户端缓存该内容. (默认= true)

  • contentCharSet -如果设置为字符串, ‘;charset=value’ 将添加到 content-type 头. 如果为一个表达式, 将使用计算的结果. 如果没有设置, 将不添加字符编码到头
    这些参数通过提供与参数名字相同的 getter 方法来设置到你的 Action. 例如, 你可以提供一个 getContentType() 方法来覆盖当前action的参数.

示例-固定下载

准备的文件:
这里写图片描述
文件下载不需要jsp页面,我们先配置action,这种方式是把下载文件写死的方式,不可用任意下载:

<action name="download" class="com.lgh.struts2.action.FileDownloadAction"
        method="execute">
        <result name="success" type="stream">
            <param name="contentType">image/jpeg</param>
            <param name="inputName">is</param>
            <param name="contentDisposition">attachment;filename="123.jpg"</param>
            <param name="bufferSize">1024</param>

        </result>
    </action>

action类:
FileDownloadAction.java

package com.lgh.struts2.action;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class FileDownloadAction extends ActionSupport {


    private static final long serialVersionUID = 1L;

    public InputStream getIs() throws FileNotFoundException{
        //文件下载时我们需要一个输入流来读取文件,其他的输出struts2会帮我们创建
        return new FileInputStream(ServletActionContext.
                getServletContext().getRealPath("/download")+"/123.jpg");
    }

}

测试:
这里写图片描述
经测试下载成功

示例-任意下载

有时候我们的下载并不固定格式,比如txt和jpg是两种不同的格式,123.txt和456.txt下载文件的名字也不相同,这时我们的struts2.xml中的action配置就不能写死了,我们需要使它能根据传递过来的文件名不同动态的生成一些配置参数的值:
比如:通过OGNL表达式从文件下载action类中通过get方法得到值

 <action name="downloadX" class="com.lgh.struts2.action.FileDownloadXAction"
            method="execute">
            <result name="success" type="stream">
            <param name="contentType">${contentType}</param>
             <param name="inputName">is</param>
             <param name="contentDisposition">attachment;filename="${f}"</param>
             <param name="bufferSize">1024</param>

            </result>
        </action>

这时我们就要在相应的action类中创建get方法了
FileDownloadXAction.java

package com.lgh.struts2.action;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class FileDownloadXAction extends ActionSupport {


    private static final long serialVersionUID = 1L;
    private String f;
    private String contentType;

    public InputStream getIs() throws FileNotFoundException{
        System.out.println(ServletActionContext.getServletContext().getMimeType(f));
        return new FileInputStream(ServletActionContext.
                getServletContext().getRealPath("/download")+"/"+f);
    }

    public String getF() throws UnsupportedEncodingException {

        return f;
    }

    public void setF(String f) {
        /*
         * 在 tomcat7 中从get请求获取参数是若包含中文,
         * 则会发现中文乱码现象
         * 因为浏览器传送来的数据是以utf-8编码的,在服务器tomcat则以iso-8859-1解码
         * 产生了乱码,下面代码可以解决该问题
         * new String(f.getBytes("ISO-8859-1"),"UTF-8");
         * */

        this.f = f;
    }


    public String getContentType() {
        System.out.println(f);
        System.out.println(ServletActionContext.getServletContext().getMimeType(f));
        return ServletActionContext.getServletContext().getMimeType(f);

    }


}

测试
下载文件名非中文的
这里写图片描述
成功没有问题
下载中文名字文件
火狐浏览器:
这里写图片描述
谷歌浏览器:
这里写图片描述
这里写图片描述
我们看到在下载中文的文件时,文件名都出现了错误
这是因为:浏览器在响应头中不允许中文存在 我们下载时必须content-disposition头中设置一些下载信息这时如果我们要下载的文件包含中文,那么我们就必须进行编码
修改getF()方法:


    public String getF() throws UnsupportedEncodingException {
        /*
         * 浏览器在响应头中不允许中文存在
         * 我们下载时必须在content-disposition头中设置一些下载信息
         * 这时如果我们要下载的文件包含中文,那么我们就必须进行编码
         *设置  URLEncoder.encode(name, "utf-8") 对于ie 谷歌 已经可以了,
         *但是对于火狐仍然有一些问题
         *;filename*=utf-8''"+ URLEncoder.encode(name, "utf-8")
         *用于就解决火狐浏览器的问题
         *我们在struts2.xml通过配置的方式也可以达到以下功能
         *
         */

        /*
         * 这时我们不使用struts2框架下载时所需要设置的响应头
         * response.setHeader("content-disposition", "attachment;filename=\\""+ 
                   URLEncoder.encode(name, "utf-8")+"\\";filename*=utf-8''"+
                   URLEncoder.encode(name, "utf-8"));

         * */
        return URLEncoder.encode(f, "utf-8");
    }

测试:
这里写图片描述
这里写图片描述
火狐浏览器测试:
这里写图片描述
解决
但是对于火狐仍然有一些问题
不使用框架则以下代码 用于就解决火狐浏览器的问题
;filename*=utf-8””+ URLEncoder.encode(name, “utf-8”)
我们在struts2.xml通过配置的方式也可以达到以下功能
这里写图片描述
这时问题解决了:
这里写图片描述

总结

总的来说,struts2提供的文件上传下载机制简化了我们很多代码,我们可以在以后的项目中使用该机制,同样我们也可以使用FileUpload组件来进行文件的上传,这个都是因个人爱好决定!

以上是关于Struts2学习—文件上传和下载的主要内容,如果未能解决你的问题,请参考以下文章

struts2学习(14)struts2文件上传和下载多个文件上传和下载

struts2学习(13)struts2文件上传和下载

Struts2学习———— 文件上传和下载

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

Struts2学习总结——文件上传与下载

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