Struts2与Servlet的文件上传下载详解

servlet普通方法实现文件上传

具体不详细介绍,有兴趣的可以研究一下,以下文章的第一种方法就是普通的方法实现的。
Servlet实现文件上传的几种方法

servlet引入jar包方式实现文件上传

当使用了enctype=”multipart/form-data”之后,普通输入框的内容,将无法使用getParameter获取
commons-fileupload-1.2.2.jar和commons-io-2.0.1.jar
由于项目环境是struts2,在使用servlet上传时会被struts2拦截,所以修改web.xml中的配置/.action,不然获取的文件列表为空,并且加上jsp的过滤,不然jsp页面的struts2标签可能报错

<filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>*.action</url-pattern>
    </filter-mapping>
<filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>*.jsp</url-pattern>
</filter-mapping>

servlet3 API 实现文件上传

注意:使用request.getParts()方法必须给servlet添加注解@MultipartConfig,否则虽然不会抛出异常,但是获取不到数据。

<web-app version="2.5"   
xmlns="http://java.sun.com/xml/ns/javaee"   
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 

此时,文件上传不报错,但是request.getParts()无法获取数据,修改为

<web-app version="3.1"   
xmlns="http://java.sun.com/xml/ns/javaee"   
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"> 

即可

servlet实现的文件上传下载的代码

file.jsp代码

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文件上传下载</title>
</head>
<body>
<form action="UploadServlet" method="post" enctype="multipart/form-data">
    <input type="file" name="filename">
    <input type="submit" value="上传">
</form>
<br>
上传文件.txt<a href="DownServlet?fileName=上传文件.txt">下载 </a><br>
</body>
</html> 

UploadFile.java代码:

/**
 * 
 */
package servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.List;

import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;

/**
 * @author Administrator
 *
 */
@WebServlet(name = "UploadServlet", urlPatterns = "/UploadServlet")
@MultipartConfig // 标识Servlet支持文件上传
public class UploadFile extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
    // 借助commons-fileupload-1.2.2.jar和commons-io-2.0.1.jar来实现文件上传功能
     //upload(request, response);
    // servlet3的接口实现的
    uploadS3(request, response);
    }

    /**
     * @param request
     * @param response
     */
    private void uploadS3(HttpServletRequest request, HttpServletResponse response) {
    try {
        boolean saveboolean = false;
        // 存储路径
        String savePath = request.getServletContext().getRealPath("/upload");
        // 获取上传的文件集合
        Collection<Part> parts = request.getParts();
        System.out.println(parts);
        // 上传单个文件
        if (parts.size() == 1) {
        // Servlet3.0将multipart/form-data的POST请求封装成Part,通过Part对上传的文件进行操作。
        // Part part = parts[0];//从上传的文件集合中获取Part对象
        Part part = request.getPart("filename");// 通过表单file控件(<input
        // type="file"
        // name="file">)的名字直接获取Part对象
        // Servlet3没有提供直接获取文件名的方法,需要从请求头中解析出来
        // 获取请求头,请求头的格式:form-data; name="file";
        // filename="snmp4j--api.zip"
        String header = part.getHeader("content-disposition");
        // 获取文件名
        String fileName = getFileName(header);
        // 把文件写到指定路径
        part.write(savePath + File.separator + fileName);
        saveboolean = true;
        System.out.println(savePath + File.separator + fileName);
        } else {
        // 一次性上传多个文件
        for (Part part : parts) {// 循环处理上传的文件
            // 获取请求头,请求头的格式:form-data; name="file";
            // filename="snmp4j--api.zip"
            String header = part.getHeader("content-disposition");
            // 获取文件名
            String fileName = getFileName(header);
            // 把文件写到指定路径
            part.write(savePath + File.separator + fileName);
            saveboolean = saveboolean&true;
        }

        }
        PrintWriter out;

        out = response.getWriter();
        if (saveboolean) {
        out.println("上传成功");
        } else {
        out.println("上传失败");
        }
        out.flush();
        out.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    }

    /**
     * 根据请求头解析出文件名 请求头的格式:火狐和google浏览器下:form-data; name="file";
     * filename="snmp4j--api.zip" IE浏览器下:form-data; name="file";
     * filename="E:\snmp4j--api.zip"
     * 
     * @param header
     *            请求头
     * @return 文件名
     */
    public String getFileName(String header) {
    /**
     * String[] tempArr1 =
     * header.split(";");代码执行完之后,在不同的浏览器下,tempArr1数组里面的内容稍有区别
     * 火狐或者google浏览器下:tempArr1={form-data,name="file",filename="snmp4j--api.zip"}
     * IE浏览器下:tempArr1={form-data,name="file",filename="E:\snmp4j--api.zip"}
     */
    String[] tempArr1 = header.split(";");
    /**
     * 火狐或者google浏览器下:tempArr2={filename,"snmp4j--api.zip"}
     * IE浏览器下:tempArr2={filename,"E:\snmp4j--api.zip"}
     */
    String[] tempArr2 = tempArr1[2].split("=");
    // 获取文件名,兼容各种浏览器的写法
    String fileName = tempArr2[1].substring(tempArr2[1].lastIndexOf("\\") + 1).replaceAll("\"", "");
    return fileName;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
    doGet(req, resp);
    }

    /**
     * @param request
     * @param response
     */
    private void upload(HttpServletRequest request, HttpServletResponse response) {
    try {
        request.setCharacterEncoding("utf-8");
    } catch (UnsupportedEncodingException e1) {
        e1.printStackTrace();
    }
    DiskFileItemFactory factory = new DiskFileItemFactory();// 1.创建DiskFileItemFactory工厂类
    ServletFileUpload upload = new ServletFileUpload(factory);// 2.创建解析类,用于解析resquest

    try {
        List<FileItem> list = upload.parseRequest(request);// 3.解析内容,获取一个list,数据都存储在list中
        System.out.println(list);
        for (FileItem item : list) {
        if (item.isFormField()) {// 判断是否是普通的表单内容
            System.out.println(item.getFieldName());// 获取的是表单中name属性的值
            System.out.println(item.getString());// 获取的是对应的表单的值
        } else {// 为假,说明是上传项
            // 获取流,进行处理
            InputStream ism = item.getInputStream();
            // System.out.println(" item.getString():" +
            // item.getString("gbk"));
            String path = getServletContext().getRealPath("\\upload\\");
            File dirfile = new File(path);
            if (!dirfile.exists()) {
            dirfile.mkdirs();
            }
            String filename = item.getName();// 这里getName可以获取文件名
            System.out.println(filename);
            File file = new File(path + "\\" + filename);
            System.out.println(file.getPath());
            file.createNewFile();// 这里不做文件存在性和名字重复判断
            OutputStream fos = new FileOutputStream(file);
            // 这里直接借助commons.io来做io对接,不然需要做流的读取和写入
            IOUtils.copy(ism, fos);// 把输入流的数据拷贝到输出
            IOUtils.closeQuietly(ism);
            IOUtils.closeQuietly(fos);

        }
        }
    } catch (FileUploadException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    }

    // 判断编码,待定的操作
    public String parseCode(String sb) {

    try {
        String iso8859 = new String(sb.toString().getBytes("iso8859-1"));

        String gbk = new String(sb.toString().getBytes("gbk"));
        String utf8 = new String(sb.toString().getBytes("utf-8"));
        if (iso8859.equals(sb.toString())) {
        return "ios8859";
        } else if (gbk.equals(sb.toString())) {
        return "gbk";
        } else if (utf8.equals(sb.toString())) {
        return "utf-8";
        }
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return "utf-8";
    }
}

DownFile.java代码:

/**
 * 
 */
package servlet;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import sun.misc.BASE64Encoder;

/**
 * @author Administrator
 *
 */
@WebServlet(name = "DownServlet", urlPatterns = "/DownServlet")
public class DownFile extends HttpServlet {

    /**
     * 
     */
    private static final long serialVersionUID = 1531004603279372230L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
    downStream(request, response);
    }

    /**
     * 
     */
    private void downStream(HttpServletRequest request, HttpServletResponse response) {
    try {
        // path是指欲下载的文件的路径。
        String fileName = request.getParameter("fileName");
        String path = request.getServletContext().getRealPath("/upload") + File.separator + fileName;
        File file = new File(path);

        // 以流的形式下载文件。
        InputStream fis = new BufferedInputStream(new FileInputStream(path));
        byte[] buffer = new byte[fis.available()];
        fis.read(buffer);
        fis.close();
        // 清空response
        response.reset();
        // 文件名处理
        // 根据不同浏览器进行不同的编码
        // 获得请求头中的User-Agent
        String agent = request.getHeader("User-Agent");
        String filenameEncoder = "";
        if (agent.contains("MSIE")) {
        // IE浏览器
        filenameEncoder = URLEncoder.encode(fileName, "utf-8");
        filenameEncoder = filenameEncoder.replace("+", " ");
        } else if (agent.contains("Firefox")) {
        // 火狐浏览器
        BASE64Encoder base64Encoder = new BASE64Encoder();
        filenameEncoder = "=?utf-8?B?" + base64Encoder.encode(fileName.getBytes("utf-8")) + "?=";
        } else {
        // 其它浏览器
        filenameEncoder = URLEncoder.encode(fileName, "utf-8");
        }

        // 设置response的Header
        response.addHeader("Content-Disposition", "attachment;filename=" + filenameEncoder);
        response.addHeader("Content-Length", "" + file.length());
        OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
        response.setContentType("application/octet-stream");
        toClient.write(buffer);
        toClient.flush();
        toClient.close();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
    doGet(request, response);
    }
}

struts2方式的文件上传下载

struts2底层也使用了commons-fileupload-1.2.2.jar和commons-io-2.0.1.jar,所以struts2的11个jar包中就包含这两个包

文件上传

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 <s:form action="uploads.action" method="post" theme="simple" enctype="multipart/form-data">
        输入帐号:<s:textfield name="uid"/><br>
        选择文件:<s:file name="headFile"/><br>
         选择文件:<s:file name="headFile"/><br>
   <s:submit value="提交"/>
     <s:fielderror/>
    </s:form>
    请下载文件:<a href="downLoad.action?downPath=上传文件.txt">文件</a><br>
    请下载图片:<a href="downLoad.action?downPath=飞机地球.jpg">图片</a><br>
</body>
</html> 

单个文件上传

/**
 * 
 */
package action;

import java.io.File;

import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.InterceptorRefs;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;

import com.opensymphony.xwork2.ActionSupport;

import lombok.Getter;
import lombok.Setter;

/**
 * @author Administrator
 *
 */
@Namespace("/")
@Action(value = "upload")
//当文件过大时会返回input结果,如果没有定义这个结果会报错
@Results({ @Result(name = "input", location = "/fileaction.jsp"),@Result(name = "error", location = "/fileaction.jsp"), @Result(name = "success", location = "/upsuccess.jsp") })

@InterceptorRefs({
    @InterceptorRef(value = "fileUpload", params = { "maximumSize", "65536", "allowedTypes",
    "image/x-png,image/bmp,image/gif,image/jpeg,image/jpg,text/plain" }),
    @InterceptorRef(value = "defaultStack") })

public class FileAction extends ActionSupport {
    @Getter
    @Setter
    private String uid; // 封装帐号(uid)请求参数属性
    @Getter
    @Setter
    private File headFile; // 封装上传文件域属性
    @Getter
    @Setter
    private String headFileContentType; // 封装上传文件类型的属性
    @Getter
    @Setter
    private String headFileFileName; // 封装上传文件名字

    @Override
    public String execute() {
    try {
        System.out.println("上传文件属性为:" + headFileContentType + "\n上传文件名字为:" + headFileFileName);
        // 上传文件的保存位置在“/upload”,该位置在tomcat服务器的“webapps”之中
        String realpath = ServletActionContext.getServletContext().getRealPath("/upload");

        // 声明文件目录image,如果文件名不存在就建一个呗~
        File file = new File(realpath);
        if (!file.exists()) {
        file.mkdirs();
        }
        // 实现文件上传,也就是做了一个方法调用~

        FileUtils.copyFile(headFile, new File(file, headFileFileName));

        return SUCCESS;
    } catch (Exception e) {
        e.printStackTrace();
        return ERROR;
    }
    }

}

多个文件上传


/** * */ package action; import java.io.File; import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext; import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.InterceptorRef; import org.apache.struts2.convention.annotation.InterceptorRefs; import org.apache.struts2.convention.annotation.Namespace; import org.apache.struts2.convention.annotation.Result; import org.apache.struts2.convention.annotation.Results; import com.opensymphony.xwork2.ActionSupport; import lombok.Getter; import lombok.Setter; /** * @author Administrator * */ @Namespace("/") @Action(value = "uploads") //当文件过大时会返回input结果,如果没有定义这个结果会报错 @Results({ @Result(name = "input", location = "/fileaction.jsp"),@Result(name = "error", location = "/fileaction.jsp"), @Result(name = "success", location = "/upsuccess.jsp") }) @InterceptorRefs({ @InterceptorRef(value = "fileUpload", params = { "maximumSize", "655360000", "allowedTypes", "image/x-png,image/bmp,image/gif,image/jpeg,image/jpg,text/plain" }), @InterceptorRef(value = "defaultStack") }) public class FilesAction extends ActionSupport { @Getter @Setter private String uid; // 封装帐号(uid)请求参数属性 @Getter @Setter private File[] headFile; // 封装上传文件域属性 @Getter @Setter private String[] headFileContentType; // 封装上传文件类型的属性 @Getter @Setter private String[] headFileFileName; // 封装上传文件名字 @Override public String execute() { try { System.out.println("上传文件属性为:" + headFileContentType + "\n上传文件名字为:" + headFileFileName); // 上传文件的保存位置在“/upload”,该位置在tomcat服务器的“webapps”之中 String realpath = ServletActionContext.getServletContext().getRealPath("/upload"); // 声明文件目录image,如果文件名不存在就建一个呗~ File file = new File(realpath); if (!file.exists()) { file.mkdirs(); } // 实现多文件下载代码如下: for(int i = 0; i < headFile.length; i++){ File uploadFile = headFile[i]; FileUtils.copyFile(uploadFile,new File(file,headFileFileName[i])); } return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } } }

在文件上传的特性中,一般有三个属性
1、File xxxx; // 封装上传文件域属性
2、String xxxxContentType //封装上传文件的类型
3、String xxxxFileName //封装上传文件的名字

当文件只有一个时是这样的,当多个文件上传时,只需将其都变为数组即可
1、File[] xxxx; // 封装上传文件域属性
2、String[] xxxxContentType //封装上传文件的类型
3、String[] xxxxFileName //封装上传文件的名字

然后一个文件上传改为多个

 FileUtils.copyFile(headFile, new File(file, headFileFileName));
 // 实现多文件下载代码如下:
        for(int i = 0; i < headFile.length; i++){
            File uploadFile = headFile[i];
            FileUtils.copyFile(uploadFile,new File(file,headFileFileName[i]));
         }

文件下载

/**
 * 
 */
package action;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;

import com.opensymphony.xwork2.ActionSupport;

import lombok.Getter;
import lombok.Setter;

/**
 * @author Administrator
 *
 */
/*@Namespace("/")
@Action(value = "downLoad")
//当文件过大时会返回input结果,如果没有定义这个结果会报错
@Result(name = "download", type = "stream", 
params = {
"contentType", "application/octet-stream",
"inputName","inputStream",
"contentDisposition", "attachment;filename=\"${filename}\"",
"bufferSize", "4096"
})*/

public class DownFileAction  extends ActionSupport
{
    @Getter
    @Setter
    private String downPath; // 下载文件的名字
    @Getter
    @Setter
    private String contentType;     // 保存文件类型
    @Getter
    private String filename;        // 保存时的文件名

   /*
    *下载用的Action返回一个InputString实例,该方法对应Action配置
    *里面的result的inputName参数,值为inputString
    *
    */

   public InputStream getInputStream() throws UnsupportedEncodingException{
    //转换格式,否则中文不能显示。
    this.filename=new String(filename.getBytes(),"iso-8859-1");
       InputStream inputStream = ServletActionContext.getServletContext().getResourceAsStream(downPath);

       return inputStream;
   }

   public String execute() throws IOException{
       // 下载保存时的文件名和被下载的文件名一样
       filename = downPath;
       // 下载的文件路径,请在webapps目录下创建images
       downPath = "upload/" + downPath;
       // 保存文件的类型
       System.out.println(downPath);

    // 保存文件的类型

       contentType = "application/octet-stream";
       return SUCCESS;
   }

    /**
     * @param filename the filename to set
     * @throws UnsupportedEncodingException 
     */
    public void setFilename(String filename) throws UnsupportedEncodingException {
        this.filename =  new String(filename.getBytes("iso-8859-1"),"utf-8");
    }
}

struts.xml

<action name="downLoad" class="action.DownFileAction">
            <!--结果类型为String-->
            <result type="stream">
                <param name="contentType">${contentType}</param>
                <!--默认就是inputStream,它将会指示StreamResult通过
                  inputName属性值的getter和setter方法,如这里就是
                  getInputStream()来获取下载文件的内容,意味着Action
                  要有这个方法
                  -->
                <param name="inputName">inputStream</param>
                 <!--默认为inline(在线打开),设置为attachment将会告诉浏览器下载
                     该文件,filename指定下载文件时的文件名,若未指定将会以浏览器
                     页面名作为文件名,如:以download.action作为文件名
                   -->
                <param name="contentDisposition">attachment;filename=${filename}</param>
                <!--指定下载文件的缓冲大小-->
                <param name="bufferSize">4096</param>

            </result>
        </action>

注意在action中有两个与编码相关的代码,我的项目环境是UTF-8编码,必须要这样写,不然要下载的文件中文名是空白

注意到inputName这个参数,获取的是action中getInputStream函数的返回值,也就是文件的流,当我们不存在这个方法时会提示错误

可以看到struts2的文件上传下载比普通的方法要简单

参考

servlet使用common-fileupload实现上传下载

Servlet实现文件上传的3种方法

Servlet3.0学习总结(三)——基于Servlet3.0的文件上传

【文件下载】Java下载文件的几种方式

JavaWeb框架Struts2(七)—–>文件的上传和下载

点赞

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注