作者: 永恒の_☆ 地址: http://blog.youkuaiyun.com/chenghui0317/article/details/9531879
一、简单介绍
使用Struts2上传附件功能非常容易实现,只需要使用普通的Action类 用来获取上传的附件对象,然后保存到指定的路径下即可。基本原理和Servlet上传附件一样,只不过封装附件的方式实现不一样罢了。servlet上传附件的地址:http://blog.youkuaiyun.com/ch656409110/article/details/9502143
二、准备条件
准备一个struts2环境的项目
common-io.jar ,下载地址:http://commons.apache.org/io/download_io.cgi
common-upload.jar ,下载地址:http://jakarta.apache.org/commons/fileupload/
三、实现思路
准备一个上传附件的页面,代码如下:
<form name="saveForm" action="${ctx}/house/save.action" method="post" οnsubmit="return checkSaveHouseForm();" enctype="multipart/form-data">
<tr>
<td class="field">上传附件:</td>
<td>
<input type="file" name="upload" class="text" οnblur="checkUpload()"/>
<span id="sp_upload" class="message"></span>
<br />
</td>
</tr>
<tr>
<td class="field">附件名称:</td>
<td>
<input type="text" name="fileName" class="text"/>
</td>
</tr>
<div class="buttons"><input type="submit" name="submit" value="发布"/></div>
</form>
页面中定义的附件控件名称是 upload,那么在Action类中也要定义相同的upload属性,如下:
private File upload; //upload file
private String uploadFileName; //附件名称
private String uploadContentType; //附件类型
private String savePath; //附件保存的路径
private List<String> fileName ; //用户输入的名称
//省略get set方法
需要注意的是,如果定义附件对象的附加属性,要以附件对象为前缀,比如upload为名的附件。
并且savePath的get方法要去读取才能获得,代码如下:
public synchronized String getSavePath() {
return ServletActionContext.getServletContext().getRealPath(savePath);
}
封装好对象之后,接下来处理业务逻辑,具体代码如下:
//处理上传的附件
System.err.println(upload.getAbsolutePath());
System.err.println(upload.getName());
System.err.println(uploadFileName);
fileName = fileName + uploadFileName.substring(uploadFileName.lastIndexOf("."));
File target = new File(getSavePath(), fileName);
//直接调用org.apache.commons.io.FileUtils下的copyFile 将上传的附件复制到指定的目录下
//FileUtils.copyFile(upload, target);
//另外还可以通过输入流读取字符之后 用输出流写入到磁盘中也是一样的。
OutputStream outputStream = new FileOutputStream(new File(getSavePath(),fileName));
InputStream inputStream = new FileInputStream(getUpload());
byte[] buffers = new byte[1024];
int length = 0;
while((length = inputStream.read(buffers))!=-1){
outputStream.write(buffers, 0, length);
}
inputStream.close();
outputStream.close();
house.setAttachUrl(savePath + Constant.SEPARATOR + fileName);
houseBiz.saveHouse(house);
ServletActionContext.getRequest().setAttribute("attachurl", house.getAttachUrl());
上面代码中 有两种实现将上传的附件放到指定目录的方法,都可以实现的。另外第一种更加方便一些,直接调用org.apache.commons.io.FileUtils下的copyFile 将上传的附件复制到指定的目录下。
然后struts.xml配置文件中需要配置一下我们action的跳转,以及附件保存路径等等信息,具体如下:
<package name="house" namespace="/house" extends="struts-default">
<action name="*" class="com.struts2.web.action.HouseAction" method="{1}">
<result name="listPage">/WEB-INF/system/page/house_list.jsp</result>
<result name="detail">/WEB-INF/system/page/house_detail.jsp</result>
<result name="detail">/WEB-INF/system/page/house_detail.jsp</result>
<result name="loadSuccess">/WEB-INF/system/page/saveHouse.jsp</result>
<param name="savePath">/attach</param>
<interceptor-ref name="defaultStack">
<!-- 配置允许上传的文件类型,多个用","分隔 -->
<param name="fileUpload.allowedTypes">
image/bmp,image/png,image/gif,image/jpeg,image/jpg ,image/x-png, image/pjpeg
</param>
<!-- 配置允许上传的文件大小,单位字节 -->
<param name="fileUpload.maximumSize">102400</param>
</interceptor-ref>
<result name="saveSuccess">/WEB-INF/system/page/saveHouseSuccess.jsp</result>
<result name="input">/WEB-INF/system/page/saveHouseFail.jsp</result>
</action>
</package>
这里需要指定上传的附件指定的位置,即加一个<param>标签就好了,然后指定 上传的附件的限制类型、以及上传大小。
然后下面这种配置 是网上很多,反正我试了很多次,都不起作用。
<interceptor-ref name="fileUpload">
<param name="allowedTypes">image/jpg,image/png,image/gif,image/jpeg,image/pjpeg</param>
<param name="maximunSize">10000</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
实践证明 <interceptor-ref name="defaultStack"></interceptor-ref> 必须加上,否则接收的所有参数都为null,因为上面有引用fileUpload这个拦截器。
接下来就可以部署项目,启动服务器运行了。
首先选择附件,效果如下:
选择发布按钮,然后服务器就会进行上传操作。效果如下:
然后 在控制台可以在到action中输出的上传附件信息。效果如下:
可见,在请求提交后服务器先把附件放在服务器的一个临时文件夹下,当真正上传成功后就会把该临时文件删除。
这样,使用struts2上传单个文件效果完成了。
四、多附件上传
接下来看看多附件上传,在upload上传页面肯定需要多个file控件,代码如下:
<form name="saveForm" action="${ctx}/house/save.action" method="post" οnsubmit="return checkSaveHouseForm();" enctype="multipart/form-data">
<tr>
<td class="field">上传附件:</td>
<td>
<input type="file" name="upload" class="text" οnblur="checkUpload()"/>
<span id="sp_upload" class="message"></span>
<br />
</td>
</tr>
<tr>
<td class="field">附件名称:</td>
<td>
<input type="text" name="fileName" class="text"/>
</td>
</tr>
<tr>
<td class="field">上传附件:</td>
<td>
<input type="file" name="upload" class="text"/>
<br />
</td>
</tr>
<tr>
<td class="field">附件名称:</td>
<td>
<input type="text" name="fileName" class="text" οnblur="checkFileName()"/>
<span id="sp_fileName" class="message"></span>
</td>
</tr>
</table>
<div class="buttons"><input type="submit" name="submit" value="发布"/></div>
</form>
较上面而言 仅仅多了一个附件控件 和 附件名称控件。
然后后台需要改了,原先单个附件上传可以定义FIle 类型的对象接收参数,现在可是多个。所以需要重新定义属性,代码如下:
private List<File> upload; //upload file
private List<String> uploadFileName; //附件名称
private List<String> uploadContentType; //附件类型
private String savePath; //附件保存的路径
private List<String> fileName ; //用户输入的名称
//省略了get set 方法
实践 发现,这里定义 List 类型或者该属性原有类型的数组 都可以接收成功,可见struts2还是蛮强大的。
比如:
private File[] upload; //upload file
private String[] uploadFileName; //附件名称
private String[] uploadContentType; //附件类型
private String savePath; //附件保存的路径
private String[] fileName ; //用户输入的名称
//省略了get set 方法
然后处理上传的时候需要用循环来依次上传,具体代码如下:
//处理上传的附件
if(upload!=null){
for (int i=0; i<upload.size(); i++) {
fileName.set(i, fileName.get(i) + uploadFileName.get(i).substring(uploadFileName.get(i).lastIndexOf(".")));
File target = new File(getSavePath(), fileName.get(i));
//直接调用org.apache.commons.io.FileUtils下的copyFile 将上传的附件复制到指定的目录下
//FileUtils.copyFile(upload, target);
//另外还可以通过输入流读取字符之后 用输出流写入到磁盘中也是一样的。
OutputStream outputStream = new FileOutputStream(new File(getSavePath(),fileName.get(i)));
InputStream inputStream = new FileInputStream(getUpload().get(i));
byte[] buffers = new byte[1024];
int length = 0;
while((length = inputStream.read(buffers))!=-1){
outputStream.write(buffers, 0, length);
}
inputStream.close();
outputStream.close();
if(house.getAttachUrl()!=null && !house.getAttachUrl().equals("")){
house.setAttachUrl(house.getAttachUrl() + "," + savePath + Constant.SEPARATOR + fileName.get(i));
}else{
house.setAttachUrl(savePath + Constant.SEPARATOR + fileName.get(i));
}
}
}
houseBiz.saveHouse(house);
ServletActionContext.getRequest().setAttribute("attachurl", house.getAttachUrl());
显示上传后的代码具体如下:
<div class="box">
<div class="msg">
<p>恭喜:添加住房成功!</p>
<p><a href="${ctx}">返回首页</a></p>
<%
String attachurl = (String)request.getAttribute("attachurl");
if(attachurl!=null){
String []strs = attachurl.split(",");
for(int i=0; i<strs.length; i++){
out.print("<img src='"+request.getContextPath()+strs[i]+"' width='400' height='270'/><br/><br/>");
}
}
%>
</div>
</div>
ok,再重新部署,启动服务器然后在执行一遍,效果如下图:
点击发布按钮,效果如下:
界面上是成功了,然后在去附件指定目录查看 是否真正成功。
这样,多附件上传就成功完成了。
五、限制上传的附件类型和大小
前面已经配置的很清楚了,当我们想要过滤一些不好的情况,比如上传附件的大小上限,以及类型的限制。可以先在前台判断,当然如果前台被屏蔽了 就只能通过后台再次判断上传的附件是否满足需求,否则服务器会被搞垮的。
当附件的大小超过上限,默认的2M大小,如果给超过上限 就不会传到服务器,服务器不会抛出异常,进入sturts.xml中配置的input对应的页面,然后可以在页面中添加
<s:fielderror></s:fielderror> 标签捕捉具体异常。
具体异常信息显示如下图所示:
当上传的附件不是图片格式的话,也不会上传到服务器,会显示如下结果:
这样子显示好吗?感觉不太友好,全是一片英文字母。所以为了友好显示后台抛出的异常可以这样:
在处理上传附件的Action中重写addActionError(),当异常类型是超出上限的时候我就修改提示,具体代码如下:
@Override
public void addActionError(String anErrorMessage) {
if (anErrorMessage.startsWith("the request was rejected because its size")) {
super.addActionError("抱歉,你上传的文件超过了上限 !!");
} else {
super.addActionError(anErrorMessage);
}
}
说实话,这种方式在我以前的项目中可以实现,但是这里一直不成功,我也不知道为什么。。。。
不过可以用另一种方法,给struts指定一个属性文件,比如struts.properties ,直接放在classpath的根目录下。
例子如下:
struts.messages.error.content.type.not.allowed=\u62B1\u6B49\uFF0C\u60A8\u4E0A\u4F20\u7684\u9644\u4EF6\u4E0D\u662F\u56FE\u7247\u683C\u5F0F\u7684\uFF01
struts.messages.error.file.too.large=\u62B1\u6B49\uFF0C\u60A8\u4E0A\u4F20\u7684\u9644\u4EF6\u592A\u5927\uFF0C\u6700\u591A10M!
struts.messages.error.uploading=\u62B1\u6B49\uFF0C\u4E0A\u4F20\u51FA\u9519\u4E86\uFF01
然后在struts的配置文件中指定一下,就好了,具体如下:
<!-- 指定struts属性文件 -->
<constant name="struts.custom.i18n.resources" value="struts" />
然后在试试看就会发现效果以前好多了,如下图:
这个是附件太大的友好提示,因为传了两个附件,所有显示了两个。
这个是附件类型错误的友好提示。
六、实现下载功能
其实这个下载功能和在Servlet 中处理的实现方式一样,所以不在累赘。servlet上传附件的地址:http://blog.youkuaiyun.com/ch656409110/article/details/9502143