Struts 2 学习笔记(一)——文件上传与下载

来源:互联网 发布:淘宝网购物14男童装 编辑:程序博客网 时间:2024/06/05 07:43
采用MySQL数据库存放所有文件的文件名和文件类型,将文件存放在磁盘中。
下面将所有的文件信息贴出来。

首先是文件目录结构:


然后是所有代码:


web.xml

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"><!-- 定义Struts2的核心控制器StrutsPrepareAndExecuteFilter --><filter><filter-name>struts2</filter-name><filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter><!-- 使用Struts2核心控制器处理所有的请求 --><filter-mapping><filter-name>struts2</filter-name>  <url-pattern>/*</url-pattern></filter-mapping></web-app>

struts.xml

<?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.multipart.maxSize" value="205358227"></constant><package name="fileuploadanddownload" extends="struts-default"><!-- 重定向到某个页面 --><action name="redirect" class="action.RedirectAction"><result name="upload">/WEB-INF/content/upload.jsp</result><!-- 重定向到另一个Action --><result name="download" type="redirectAction"><param name="actionName">showFiles</param></result></action><!-- 处理文件上传 --><action name="upload" class="action.UploadAction"><!-- 传入文件存放的相对路径 --><param name="savePath">/files</param><result name="success" type="redirectAction">showFiles</result></action><!-- 定向到文件下载页面,同时将数据库中的所有文件信息展现在下载页面 --><action name="showFiles" class="action.ShowFilesAction"><result>/WEB-INF/content/download.jsp</result></action><!-- 处理文件下载 --><action name="download" class="action.DownloadAction"><!-- 传入文件存放的相对路径 --><param name="savePath">/files</param><result name="success" type="stream"><!-- 处理所有类型的文件 --><param name="contentType">application/octet-stream</param><!-- 指定下载文件的文件名 --><param name="contentDisposition">attachment;filename="${filename}"</param><!-- 获得DownloadAction中getTargetFile()方法返回的InputStream --><param name="inputName">targetFile</param><!-- 指定下载文件的缓冲大小 --><param name="bufferSize">40000</param></result></action><action name="*"><result>/WEB-INF/content/index.jsp</result></action></package></struts>

操作数据库的类:DBOperation.java

import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.Statement;/* * 数据库操作类 * 提供连接数据库、插入数据、查询数据功能 */public class DBOperation {//建立数据库连接public Connection getConnection() throws Exception {Class.forName("com.mysql.jdbc.Driver");Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/filesload","root","1234");return conn;}//将上传文件文件名和文件类型插入数据库中public void insert(String filename, String type) throws Exception {String sql = "insert into filesload.file (filename, type) values (?, ?);";PreparedStatement pstmt = getConnection().prepareStatement(sql);pstmt.setString(1, filename);pstmt.setString(2, type);pstmt.execute();}//获得数据库中所有的文件信息public ResultSet select() throws Exception {String sql = "select * from filesload.file;";Statement stmt = getConnection().createStatement();ResultSet rs = stmt.executeQuery(sql);return rs;}}

主页index.jsp,选择上传文件还是下载文件

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!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>功能选择页面</title></head><body><!-- 选择功能——上传文件还是下载文件 --><s:form action="redirect" method="get"><s:select name="function" list="{'upload','download'}"/><s:submit value="确认"/></s:form></body></html>

上传文件页面:upload.jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!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>文件上传页面</title></head><body><!-- 上传文件 --><!-- 表单必须具有enctype="multipart/form-data",否则只能将文件名发送给服务器 --><s:form action="upload" method="post" enctype="multipart/form-data"><s:file name="upload" label="Picture"/><s:file name="upload" label="PDF"/><s:file name="upload" label="ZIP"/><s:file name="upload" label="PPT"/><s:submit value="上传"/></s:form></body></html>

下载文件页面:download.jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!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>文件下载页面</title></head><body><h2>当前数据库中存储的文件:</h2><!-- 遍历filename,获得所有的文件名,同时提供下载入口 --><s:iterator value="#session.filename" id="number"><s:property value="number"/><a href="download?filename=<s:property value='number'/>">下载</a><br/></s:iterator></body></html>


重定向Action:RedirectAction.java,对应于主页index.jsp

/* * 提供重定向功能,根据主页选择功能,重定向到指定功能页面 */public class RedirectAction {private String function;public String getFunction() {return function;}public void setFunction(String function) {this.function = function;}public String execute() throws Exception {return getFunction();}}

文件上传Action:UploadAction.java

import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.util.UUID;import org.apache.struts2.ServletActionContext;import bean.DBOperation;import com.opensymphony.xwork2.ActionSupport;public class UploadAction extends ActionSupport {private static final long serialVersionUID = 1L;//封装上传文件的文件内容、文件名、文件类型和文件存储目录(相对路径)private File[] upload;private String[] uploadFileName;private String[] uploadContentType;private String savePath;//文件内容的getter方法和setter方法public File[] getUpload() {return upload;}public void setUpload(File[] upload) {this.upload = upload;}//文件名的getter方法和setter方法public String[] getUploadFileName() {return uploadFileName;}public void setUploadFileName(String[] uploadFileName) {this.uploadFileName = uploadFileName;}//文件类型的getter方法和setter方法public String[] getUploadContentType() {return uploadContentType;}public void setUploadContentType(String[] uploadContentType) {this.uploadContentType = uploadContentType;}//根据相对路径获得绝对路径public String getSavePath() {return ServletActionContext.getServletContext().getRealPath(savePath);}public void setSavePath(String savePath) {this.savePath = savePath;}public String execute() throws Exception {String newName;String uri;DBOperation dbo = new DBOperation();for(int i = 0, length = upload.length ;i < length; i++) {//获得上传文件的新名字与资源路径newName = UUID.randomUUID().toString() +uploadFileName[i].substring(uploadFileName[i].lastIndexOf("."));uri = getSavePath() + "\\" + newName;//将上传文件写入磁盘File saveFile = new File(uri);FileInputStream fis = new FileInputStream(getUpload()[i]);FileOutputStream fos = new FileOutputStream(saveFile);byte[] b = new byte[1024];int len = 0;while((len = fis.read(b)) > 0) {fos.write(b, 0, len);}fis.close();fos.close();getUploadFileName()[i] = newName;//将文件名与文件类型存储到数据库中dbo.insert(newName, getUploadContentType()[i]);}return SUCCESS;}}

展示所有文件的Action:ShowFilesAction.java

import java.sql.ResultSet;import java.util.ArrayList;import java.util.List;import bean.DBOperation;import com.opensymphony.xwork2.ActionContext;import com.opensymphony.xwork2.ActionSupport;public class ShowFilesAction extends ActionSupport {private static final long serialVersionUID = 1L;//从数据库中获得所有的文件名及文件类型,分别存储到两个List中,并添加到Session中去public String execute() throws Exception {DBOperation dbo = new DBOperation();ResultSet allFiles = dbo.select();List<String> filename = new ArrayList<String>();List<String> type = new ArrayList<String>();while(allFiles.next()) {filename.add(allFiles.getString(1));type.add(allFiles.getString(2));}ActionContext.getContext().getSession().put("filename", filename);ActionContext.getContext().getSession().put("type", type);return SUCCESS;}}

文件下载Action:DownloadAction.java

import java.io.InputStream;import org.apache.struts2.ServletActionContext;import com.opensymphony.xwork2.ActionSupport;public class DownloadAction extends ActionSupport {private static final long serialVersionUID = 1L;//封装文件名和文件存放的相对路径private String filename;private String savePath;//文件名的getter方法和setter方法public String getFilename() {return filename;}public void setFilename(String filename) {this.filename = filename;}//文件相对路径的getter方法和setter方法public String getSavePath() {return savePath;}public void setSavePath(String savePath) {this.savePath = savePath;}/* * 定义一个返回InputStream的方法 * InputStream作为文件下载的入口 */public InputStream getTargetFile() throws Exception {//获得文件的相对路径String uri = getSavePath() + "\\" + filename;return ServletActionContext.getServletContext().getResourceAsStream(uri);}public String execute() throws Exception {return SUCCESS;}}

所有的代码如上,遇到的问题如下:

1.上传文件必须封装四个属性——文件名xxxFileName、文件类型xxxContentType、文件内容xxx、存储路径savePath,需要注意的是三个xxx必须完全一致。

private File[] upload;private String[] uploadFileName;private String[] uploadContentType;private String savePath;

2.在这个应用中,为了保护JSP页面(如果不保护,同时我知道文件的目录结构,我就可以绕过核心控制器直接访问JSP页面),我将所有的JSP页面放在/WEB-INF/content下。由于WEB-INF目录只有服务器端才能够访问,客户端无法直接访问,所以在struts.xml中增加一个由通配符标识的<action>来对index.jsp页面进行访问。

<action name="*"><result>/WEB-INF/content/index.jsp</result></action>

在这里我遇到了一个问题,为了对index.jsp页面进行访问,我觉得不用<action>,直接在web.xml中的<welcome-file-list>中设置也是可以的,所以我就尝试了一下,结果index.jsp页面能够访问,但是提交请求后却报404 Not Found,除此之外没有任何信息了。struts.xml配置没有错的,我就纳闷是怎么回事了,发现在地址栏中页面发送的请求URL为http://localhost:8080/FileUploadAndDownload/WEB-INF/content/redirect.action?function=upload,我就郁闷啊,理论上请求URL应该是http://localhost:8080/FileUploadAndDownload/redirect.action?function=upload才对啊,这里为什么多了/WEB-INF/content呢?现在还是很不解,待测试。


3.在upload.jsp中,上传表单属性必须具有:enctype="multipart/form-data"。表单enctype="multipart/form-data"的意思,是设置表单的MIME编码。默认情况,这个编码格式是application/x-www-form-urlencoded,不能用于文件上传;只有使用了multipart/form-data,才能完整的传递文件数据。刚开始的时候忘记添加这个属性,结果报下面的错误:

java.lang.NoSuchMethodException: UploadAction.setUpload([Ljava.lang.String;)

意思是UploadAction.java中没有setUpload(String str)方法。的确,因为UploadAction.java中只有setUpload(File upload)方法,两个参数类型不一致。这也就说明了,没有上面的属性,请求中表单数据是上传文件的文件名——字符串类型,添加了上面的属性,请求中表单数据才是上传文件的内容——File类型。


4.在UploadAction.java中相对路径转换成绝对路径使用下面的方法:

public String getSavePath() {return ServletActionContext.getServletContext().getRealPath(savePath);}
大多数书或网络资料中使用的是下面的方法:

public String getSavePath() {return ServletActionContext.getRequest().getRealPath(savePath);}
但是现在这个已经过时了,你非要用这个也会看见Eclipse提示:The method getRealPath(String) from the type ServletRequest is deprecated,并且用删除线标记getRealPath(realSavePath),所以我们不用这个方法。我们参考Servlet API文档我们可以看到,getRealPath(String path)是ServletContext的方法,所以我们在struts2中直接使用ServletActionContext.getServletContext()获得ServletContext对象即可。


5.成功上传了文件,但是在相应的文件夹下却找不到上传文件问题。
首先先重新回去看一下我的文件目录结构。
从上面我们可以看到,我是通过files文件夹来存放上传文件的,但是实际情况却是:全过程没有报错,在success.jsp页面也能够看到自己上传的文件,但是真正去这个files文件夹下一找,发现文件夹是空的,这是怎么回事? 
当时遇到这个问题的时候,百度了很久,网上讲的都是设置一个struts.multipart.saveDir常量,但是可以跟你说这是没有用的,因为这个常量配置的是临时文件夹,数据会丢失的。 
参考UploadAction.java文件中execute方法的最后,我添加了一句代码:

System.out.println(saveUploadFile);
添加这句代码主要为了调试,程序执行时会在控制台输出你上传的文件的实际路径,你参照这条路径到硬盘的具体目录去看看,你会发现你上次的文件居然放在这个文件夹里,具体路径可参考:

E:\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\FileUploadAndDownload\files

从.metadata我们知道这个文件目录是Eclipse创建的。你创建web项目的时候,可能将项目放在其他地方,但是你运行应用时,Eclipse实际是将你的应用放在这个目录下运行的,相当于放在tomcat中的webapps目录下,所以上传文件自然就放在这个地方。 
刚刚遇到这个问题的时候,我想了很久,怎样才能将上传文件弄出来,放在我的项目文件目录下的files文件里呢?网上也有很多人提出这个问题。我的解决办法是——不用管它! 
不是不能管,而是没有必要。你可以试试空手搭建自己的应用,不借助IDE,然后将应用放在tomcat下的webapps下运行,这时候你会发现上传的文件切切实实地存放到你想要的目录下了,而且控制台输出文件的路径也会发生改变,变成:

E:\apache-tomcat-7.0.42\webapps\FileUploadAndDownload\files
参考上面.metadata的说明,应该能够想到为什么了。