Struts2实现单文件上传,多文件上传与下载(十)
勿以恶小而为之,勿以善小而不为--------------------------刘备
劝诸君,多行善事积福报,莫作恶
上一章简单介绍了Struts2实现国际化操作及中英文切换(九)如果没有看过,请观看上一章
上传文件和下载文件的重要性就不说了,常用于上传文档,更换头像。
Struts2框架本身提供了一个文件上传的拦截器,在默认的拦截器里面。
一.文件上传前的准备
一.一 添加jar包
Struts2上传时,还需要依赖apache的两个jar包
一.二 注意点
- Struts2上传文件时,用<s:file name="upload"> 或者<input type="file" name="upload" />均可。 建议使用 s:file 的元素。
- form表单提交时,提交方式为post提交。(避免文件过大,get方式不支持)
- form表单提交时,enctype类型需要改变,应该变成:enctype="multipart/form-data"
- 文件上传对应的Action进行接收文件时, 对象属性 File Xxx需要与<s:file >中的name相同,
如<s:file> name为upload,则Action中File相应的为upload.
<s:file>name为uploadImage,则Action中File为uploadeImage. - Action中取得的File upload.getName() 取得的并不是上传文件的真实名称,只是一个缓存的名称,没有任何的意义。
- Action中想取得上传文件的名称和上传类型,需要定义两个String 类型,格式必须是XxxFileName,XxxContentType。其中Xxx为File的值
XxxFileName 取得文件的名称,XxxContentType 取得文件的类型
二 实现单文件上传
二.一 编写 上传文件表单 /content/single.jsp
<s:form action="File_single.action" namespace="/" method="post" enctype="multipart/form-data">
用户名: <s:textfield name="name"/> <br/>
密码: <s:password name="password"/><br/>
上传文件:<s:file name="upload"/><br/>
<s:submit value="提交"/>
<s:reset value="重置"/>
</s:form>
需要保证 类型为post 提交,enctype 类型为 multipart/form-data 。
注意,此时 file 元素的名称为upload。
二.二 编写FileAction 进行处理文件接收
FileAction.java
package com.yjl.web.action;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.yjl.pojo.User;
/**
* @author 两个蝴蝶飞
* @version 创建时间:Aug 28, 2018 8:35:56 AM
* 关于文件上传和下载的Action实现
*/
public class FileAction extends ActionSupport implements ModelDriven<User>{
private static final long serialVersionUID = 1L;
private Logger logger=Logger.getLogger(FileAction.class);
//在文件上传时,也同样可以获得其他标签的值.
private User user=new User();
@Override
public User getModel() {
return user;
}
/**
* 关于文件上传的具体的操作
* @param uploade 与前端表单中file标签的name值相同.
* @param XxxFileName 上传文件的名称
* @param XxxContentType 上传文件的类型
*/
private File upload;
private String uploadFileName;
private String uploadContentType;
//实现它们三个的setter和getter方法
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public String getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
/**
*单文件上传操作
*/
public String single(){
//在实际开发中,需要判断一下是否上传了文件,即upload是否为空,不为空,才进行上传的操作。
logger.info("可以取出user的值:"+user.toString());
if(upload!=null){
//1.输出上传的类型
logger.info("上传文件的名称用File取得:"+this.getUpload().getName());
logger.info("直接用属性取得名称:"+this.getUploadFileName());
logger.info("文件上传的类型:"+this.getUploadContentType());
//2.设置上传文件的放置位置,通常放在服务器下面的upload文件夹下.
String path=ServletActionContext.getServletContext().getRealPath("/upload");
//3.这是一个目录,如果这个目录不存在,需要创建这个目录(包括其父目录)
File srcFile=new File(path);
if(!srcFile.exists()){
srcFile.mkdirs();
}
//4.通常还需要对上传的文件名进行UUID的操作,使文件名不能重复。这里不做处理
String fileName=this.getUploadFileName();
//5.调用FileUtils类执行上传的操作.
try {
FileUtils.copyFile(upload,new File(srcFile,fileName));
logger.info("文件上传成功");
} catch (IOException e) {
e.printStackTrace();
}
}
return SUCCESS;
}
}
二.三 配置struts.xml文件
<struts>
<!--修改国际化编码 -->
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!--修改是否为开发者模式 -->
<constant name="struts.devMode" value="true"></constant>
<!--修改ui样式表 -->
<constant name="struts.ui.theme" value="simple"></constant>
<package name="file" extends="struts-default" namespace="/">
<action name="File_*" class="com.yjl.web.action.FileAction" method="{1}">
<result name="success">/WEB-INF/content/success.jsp</result>
</action>
<!-- 配置跳转页面 -->
<action name="*">
<result>/WEB-INF/content/{1}.jsp</result>
</action>
</package>
</struts>
二.四 重启服务器,验证文件上传
输入网址: http://localhost:8080/Struts_File/single,
进行上传文件, 老蝴蝶这儿上传一张照片。
查看logger输出
发现file.getName()并没有任何意义。
查看服务器upload文件夹下是否有这张图片:
存在图片,文件上传操作成功。
二. 五 设置上传时,允许的格式和大小
二.五.一 配置格式 和大小
通过文件上传拦截器,进行配置
在上传的时候,一般需要特别指定文件上传时的格式和大小。 Struts2自带的文件上传拦截器已经帮我们实现了这一点,
我们只需要进行简单的配置即可。
在struts.xml配置文件中添加拦截器 fileUpload
<package name="file" extends="struts-default" namespace="/">
<interceptors>
<interceptor-stack name="defaultStack">
<!-- 引入已经定义好的文件上传拦截器,将参数传入进去,名称为fileUpload -->
<interceptor-ref name="fileUpload">
<!-- 上传文件的大小 -->
<param name="maximumSize">500000000</param>
<!-- 标准MINE名称 -->
<param name="allowedTypes">text/plain,application/vnd.ms-powerpoint</param>
<!-- 后缀名 可以传递.txt和.ppt结尾的,此时图片是不行的-->
<param name="allowedExtensions">.txt,.ppt</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="File_*" class="com.yjl.web.action.FileAction" method="{1}">
<result name="success">/WEB-INF/content/success.jsp</result>
</action>
<!-- 配置跳转页面 -->
<action name="*">
<result>/WEB-INF/content/{1}.jsp</result>
</action>
</package>
二.五.二 验证配置
如果此刻上传图片的话:
会显示异常:
上传.txt文档的话:
是可以正常上传的。
一般设置图片时,常用的格式为:
<interceptors>
<interceptor-stack name="defaultStack">
<!-- 引入已经定义好的文件上传拦截器,将参数传入进去,名称为fileUpload -->
<interceptor-ref name="fileUpload">
<!-- 上传文件的大小 -->
<param name="maximumSize">500000000</param>
<!-- 后缀名 设置常见的图片形式-->
<param name="allowedExtensions">.bmp,.jpg,.png,.gif</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
二.五.三 设置 struts.multipart.maxSize 常量,控制文件大小
有的时候,可能会上传视频或者其他大文件,可以在struts.xml常量中配置相应的上传文件大小。
默认的大小是2M,即使你在设置类型时,用了maximumSize,设置了一个很大的值。
那么当大小超过2M时,也是错误的。
这个时候需要设置另外一个参数。常量:struts.multipart.maxSize
<constant name="struts.multipart.maxSize" value="1000000000"/>
三. 实现多文件上传
在实现多文件上传时,有了单文件上传的基础上,是非常简单的,只需要将原来的对象,改变成数组即可。
将File upload改成File[] upload即可。 uploadFileName和uploadeContentType也同样需要改变成字符串数组的样式。
三.一 编写 /content/login2.jsp 页面
login.jsp页面
file是多选形式,用多个name值相同的样式。
<s:form action="MoreFile_multiple.action" namespace="/" method="post" enctype="multipart/form-data">
用户名: <s:textfield name="name"/> <br/>
密码: <s:password name="password"/><br/>
<!-- 名称相同 -->
上传文件1:<s:file name="upload"/><br/>
上传文件2:<s:file name="upload"/><br/>
<s:submit value="提交"/>
<s:reset value="重置"/>
</s:form>
三.二 创建 MoreFileAction.java
MoreFileAction:
package com.yjl.web.action;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
* @author 两个蝴蝶飞
* @version 创建时间:Aug 28, 2018 8:35:56 AM
* 关于文件上传和下载的Action实现
*/
public class FileAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private Logger logger=Logger.getLogger(FileAction.class);
/**
* 关于文件上传的具体的操作
* @param uploade 与前端表单中file标签的name值相同.
* @param XxxFileName 上传文件的名称
* @param XxxContentType 上传文件的类型
*/
private File []upload;
private String []uploadFileName;
private String []uploadContentType;
//实现它们三个的setter和getter方法
public File[] getUpload() {
return upload;
}
public void setUpload(File []upload) {
this.upload = upload;
}
public String[] getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String []uploadFileName) {
this.uploadFileName = uploadFileName;
}
public String[] getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(String []uploadContentType) {
this.uploadContentType = uploadContentType;
}
/**
*多文件上传操作
*/
public String multiple(){
//在实际开发中,需要判断一下是否上传了文件,即upload是否为空,不为空,才进行上传的操作。
if(upload!=null){
for(int i=0;i<upload.length;i++){
//1.输出上传的类型
logger.info("上传文件的名称用File取得:"+this.getUpload()[i].getName());
logger.info("直接用属性取得名称:"+this.getUploadFileName()[i]);
logger.info("文件上传的类型:"+this.getUploadContentType()[i]);
//2.设置上传文件的放置位置,通常放在服务器下面的upload文件夹下.
String path=ServletActionContext.getServletContext().getRealPath("/upload");
//3.这是一个目录,如果这个目录不存在,需要创建这个目录(包括其父目录)
File srcFile=new File(path);
if(!srcFile.exists()){
srcFile.mkdirs();
}
//4.通常还需要对上传的文件名进行UUID的操作,使文件名不能重复。这里不做处理
String fileName=this.getUploadFileName()[i];
//5.调用FileUtils类执行上传的操作.
try {
FileUtils.copyFile(upload[i],new File(srcFile,fileName));
logger.info("文件上传成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
return SUCCESS;
}
}
三.三 配置struts.xml 文件
struts.xml
<action name="MoreFile_*" class="com.yjl.web.action.MoreFileAction" method="{1}">
<result name="success">/WEB-INF/content/success.jsp</result>
</action>
三.四 重启服务器测试
输入网址: http://localhost:8080/Struts_File/login2
进行相关的测试:
上传文件:
控制台打印输出:
查看Tomcat 目录:
多文件上传操作成功。
四. 文件上传实例应用
在实际项目中,不仅要能正常的上传文件,还应该把上传文件的位置返回。
前端页面和struts.xml的配置都一样,主要是对Action的处理。 我通常的作法是创建一个BaseAction的类,
与第四章项目中的BaseAction相同,只是这里添加了上传的操作。
BaseAction
package com.yjl.web.action;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.text.SimpleDateFormat;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
/**
* @author 两个蝴蝶飞
* @version 创建时间:2018年8月23日 下午4:40:13
* @description BaseAction的工具类
*/
@SuppressWarnings(value= {"rawtypes","unchecked"})
public class BaseAction<T> extends ActionSupport implements ModelDriven<T>{
private static final long serialVersionUID = -7180401147510521582L;
private Logger logger=Logger.getLogger(BaseAction.class);
private T t;
private Class clazz;
public BaseAction() {
//得到当前的类
Class class1=this.getClass();
//得到运行中的父类
ParameterizedType parameterizedType=(ParameterizedType) class1.getGenericSuperclass();
clazz=(Class) parameterizedType.getActualTypeArguments()[0];
try {
t=(T) clazz.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logger.info("当前类的类:"+clazz.getName()+"完成初始化");
}
@Override
public T getModel() {
return t;
}
//添加实现文件上传和下载的操作
/**
* 关于文件上传的具体的操作
* @param uploade 与前端表单中file标签的name值相同.
* @param XxxFileName 上传文件的名称
* @param XxxContentType 上传文件的类型
*/
private File upload;
private String uploadFileName;
private String uploadContentType;
//实现它们三个的setter和getter方法
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public String getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
/**
*得到上传的路径
*/
public String getUploadPath(){
//在实际开发中,需要判断一下是否上传了文件,即upload是否为空,不为空,才进行上传的操作。
String contextPath=null;
if(upload!=null){
//1.输出上传的类型
logger.info("上传文件的名称用File取得:"+this.getUpload().getName());
logger.info("直接用属性取得名称:"+this.getUploadFileName());
logger.info("文件上传的类型:"+this.getUploadContentType());
//2.设置上传文件的放置位置,通常放在服务器下面的upload文件夹下.
String path=ServletActionContext.getServletContext().getRealPath("/upload");
//3.这是一个目录,如果这个目录不存在,需要创建这个目录(包括其父目录)
File srcFile=new File(path);
if(!srcFile.exists()){
srcFile.mkdirs();
}
//4.通常还需要对上传的文件名进行UUID的操作,使文件名不能重复。文件的返回路径是fileName.
String fileName=path+getAnglePath(this.getUploadFileName());
//5.调用FileUtils类执行上传的操作.
try {
//写复制方法
FileUtils.copyFile(upload,new File(fileName));
//将fileName进行相应的处理,去掉前面的一些无用的东西。 得到项目名
String context=ServletActionContext.getRequest().getContextPath().substring(1);
contextPath=getPath(fileName,context);
logger.info("在Tomcat下的目录为:"+contextPath);
logger.info("文件上传成功");
} catch (IOException e) {
e.printStackTrace();
}
}
return contextPath;
}
/**
* 将一个文件名设置成唯一的文件名
* @param fileName 普通的文件名
* @return 返回一个唯一的文件名
*/
public String getAnglePath(String fileName) {
//不采用UUID的形式,可以自定义当前的时间字符串来处理.
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");
String angle=sdf.format(new java.util.Date());
return File.separator+angle+"_"+fileName;
}
/**
* 将磁盘上的文件在Tomcat下的文件,转换成在Tomcat上的文件
* @param realPath 在磁盘上的路径
* @param name 项目名
* @return 将磁盘上的文件在Tomcat下的文件,转换成在Tomcat上的文件
*/
public String getPath(String realPath, String name) {
int index = realPath.indexOf(name);
String[] args = realPath.substring(index).split("\\\\");
StringBuffer sb = new StringBuffer("/");
for (int i = 0; i < args.length; i++) {
if (i != args.length - 1) {
sb.append(args[i]).append("/");
} else {
sb.append(args[i]);
}
}
return sb.toString();
}
}
在我们写的FileAction中,只需要继承BaseAction类,然后调用方法即可。
package com.yjl.web.action;
import org.apache.log4j.Logger;
import com.yjl.pojo.User;
/**
* @author 两个蝴蝶飞
* @version 创建时间:Aug 28, 2018 8:35:56 AM
* 关于文件上传和下载的Action实现
*/
public class FileAction extends BaseAction<User>{
private static final long serialVersionUID = 1L;
private Logger logger=Logger.getLogger(FileAction.class);
public String login(){
String path=getUploadPath();
logger.info("上传的路径是:"+path);
return SUCCESS;
}
}
文件上传后的控制台:
如果没有上传文件的话,
这样就完成了。
如果在上传时,出现中文乱码的问题,那是因为没有在 struts.xml中添加格式处理常量:
<!--修改国际化编码 -->
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
文件上传操作完成。
五. 文件下载
在下载时,只需要指定文件的名称,就会去相应的文件目录中去寻找,去下载。
在该项目下新建一个upload文件夹,里面放置一些文件。
四.一 下载前查询所有的文件
一般在下载之前,会将可以下载的文件以列表的形式进行显示,或者将那个文件进行相应的显示。
五.一.一 创建 ShowFileAction
ShowFileAction中:
package com.yjl.web.action;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.yjl.pojo.User;
import com.yjl.utils.BaseAction;
/**
* @author 两个蝴蝶飞
* @version 创建时间:Aug 28, 2018 8:35:56 AM
* 关于文件上传和下载的Action实现
*/
public class ShowFileAction extends BaseAction<User>{
private static final long serialVersionUID = 1L;
private Logger logger=Logger.getLogger(FileAction.class);
public String login(){
String path=getUploadPath();
logger.info("上传的路径是:"+path);
return SUCCESS;
}
//要下载的文件
private List<File> downFileList=new ArrayList<File>();
public List<File> getDownFileList() {
return downFileList;
}
public void setDownFileList(List<File> downFileList) {
this.downFileList = downFileList;
}
public String downFileList(){
//1.找到path路径
String path=ServletActionContext.getServletContext().getRealPath("/upload");
File pFile=new File(path);
//2.找到pFile文件夹下及下属文件夹下所有的文件。
List<File> temp=new ArrayList<File>();
//3.将所有的文件都放置到列表中
downFileList=ergodic(pFile, temp);
logger.info("长度是:"+downFileList.size());
//4. 将集合转换成数组
return "downFileList";
}
private List<File> ergodic(File file,List<File> resultFileList){
if(file==null||!file.exists()){
return new ArrayList<File>();
}
File[] files = file.listFiles();
if(files==null||files.length<=0){
return new ArrayList<File>();
}
for (File f : files) {
if(f.isDirectory()){// 判断是否文件夹
ergodic(f,resultFileList);// 调用自身,查找子目录
}else{
resultFileList.add(f);
}
}
return resultFileList;
}
}
在Action中看有的人是用的File []数组,这里用集合了,也是可以的。
五.一.二 配置 struts.xml 文件
在struts.xml中是正常的配置。
<!-- 下载文件显示 -->
<action name="ShowFile_*" class="com.yjl.web.action.ShowFileAction" method="{1}">
<result name="success">/WEB-INF/content/success.jsp</result>
<result name="downFileList">/WEB-INF/content/list.jsp</result>
</action>
五.一.三 编写 /content/list.jsp 页面
<body>
这是可以下载文件的列表<br/>
<s:iterator value="downFileList" var="f">
<s:property value="#f.name"/><s:a action="getDownFile?fileName=%{#f.name}" namespace="/">下载</s:a>
<br/>
</s:iterator>
</body>
五.一.四 重启服务器,验证界面
输入网址: http://localhost:8080/Struts_File/ShowFile_downFileList
会显示可以下载的文件:
五.二 下载文件
五.二.一 编写 DownFileAction
主要的Action代码:
public class DownFileAction extends ActionSupport{
private static final long serialVersionUID = 1L;
//@param fileName 下载时传入的文件名
private String fileName;
//下载时,显示的文件名
private String showFileName;
//文件类型
private String contentType;
public String getFileName() {
return this.fileName;
}
public void setFileName(String fileName) {
//如果前台传入的是中文乱码,那么需要进行乱码处理.
//如果前台传入的不是乱码,那么不需要进行处理。
this.fileName=fileName;
/*try {
this.fileName=new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}*/
}
/**
* 获取文件的类型
*/
public String getContentType() {
return ServletActionContext.getServletContext().getMimeType(fileName);
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getShowFileName() {
return showFileName;
}
public void setShowFileName(String showFileName) {
this.showFileName=showFileName;
}
public InputStream getDownloadFile() throws Exception{
//设置一下,展示的名称。
setShowFileName(fileName);
String realPath=File.separator+"upload"+File.separator+fileName;
InputStream is=ServletActionContext.getServletContext().getResourceAsStream(realPath);
return is;
}
public String download(){
return SUCCESS;
}
}
五.二.二 配置struts.xml 文件
<!-- 配置下载页面 -->
<action name="getDownFile" class="com.yjl.web.action.DownFileAction" method="download">
<!--下载时候的配置-->
<result type="stream">
<!--文件类型 -->
<param name="contentType">${contentType}</param>
<!-- 下载的文件数据存放的方法,该方法返回一个InputStream
例如取值为inputStream的属性需要编写getInputStream()方法-->
<param name="inputName">downloadFile</param>
<!--下载时,客户端显示的下载的文件名 -->
<param name="contentDisposition">attachment;filename=${showFileName}</param>
<!-- 数据的缓冲大小 -->
<param name="bufferSize">1024</param>
<!--解决中文文件名乱码-->
<param name="encode">true</param>
</result>
</action>
五.二.三 下载验证
输入网址: http://localhost:8080/Struts_File/ShowFile_downFileList
点击链接,可以进行下载, 但是下载中文时, 火狐浏览器会乱码,而谷歌浏览器正常。
本章节的代码链接为:
链接:https://pan.baidu.com/s/1P9bA-OX__modBaQHg7HO6g
提取码:lm3v
谢谢您的观看!!!