文件上传
一、上传下载组件介绍
jspSmartUpload(model1的年代);
apache-commons-fileupload,Struts2默认上传组件;
Servlet3.0使用的Part,但Servlet3.0还没有普及;
COS,Struts2支持,不过已经停止更新很久了;
pell,Struts2支持。
二、fileUpload的拦截器
Struts2默认使用的是commons-fileUpload组件完成上传的,使用Struts2会大量简化上传文件的开发。
这一工作由fileUpload拦截器来完成。
它会查看当前请求的enctype是否为multipart/form-data,如果不是就会直接“放行”;
如果是,那么它会去解析表单,然后把解析的结果传递给Action的属性!
fileUpload拦截器对会对Action提供很大的“帮助”,同时它也会对Action提出一些“小小的要求”。
Action需要提供3个属性:
private File myUpload
private String myUploadContentType
private String myUploadFileName
三、演示上传
上传文件首先我们需要给出一个页面,页面中要有一个表单:
upload.jsp
<form action="<c:url value='/UploadAction.action'/>" method="post" enctype="multipart/form-data">
用户名: <input type="text" name="username"/><br/>
文 件:<input type="file" name="myUpload"/><br/>
<input type="submit" value="Submit"/>
</form>
UploadAction
public class UploadAction extends ActionSupport {
private String username;
public void setUsername(String username) {
this.username = username;
}
private File myUpload;
private String myUploadContentType;
private String myUploadFileName;
private String savepath = "/WEB-INF/uploads";
public void setMyUpload(File myUpload) {
this.myUpload = myUpload;
}
public void setMyUploadContentType(String myUploadContentType) {
this.myUploadContentType = myUploadContentType;
}
public void setMyUploadFileName(String myUploadFileName) {
this.myUploadFileName = myUploadFileName;
}
public String execute() throws Exception {
System.out.println(username);
System.out.println(this.myUploadContentType);
System.out.println(this.myUploadFileName);
System.out.println(this.myUpload.getAbsolutePath());
this.savepath = ServletActionContext.getServletContext().getRealPath(savepath);
File destFile = new File(savepath, myUploadFileName);
FileUtils.copyFile(myUpload, destFile);
return NONE;
}
}
struts.xml
<package name="s8" namespace="/" extends="struts-default">
<action name="UploadAction" class="cn.hcx.upload.action.UploadAction">
</action>
</package>
四、上传配置
可以通过Struts2的常量来完成对上传的配置,下面是与上传相关的常量:
struts.multipart.parser:指定使用的上传组件,默认值为jakarta,表示使用commons-fileupload组件,Struts2还支持cos和pell;
struts.multipart.saveDir:临时目录,如果没有指定临时目录,那么临时文件会在Tomcat的work目录中;
struts.multipart.maxSize:整个大小限制,默认值为2097152,即2M。注意,这个限制是整个请求的大小,而不是单一文件的大小。
当上传的表单超出了限制时,拦截器会向fieldError中添加错误信息!
当执行wokflow拦截器时,会发现fieldError中存在错误,这时就会跳转到input视图,所以我们需要为Action指定input视图。
case:
<struts>
<!-- 指定文件上传时临时文件所在目录 -->
<constant name="struts.multipart.saveDir" value="d:/temp"></constant>
<!-- 文件上传大小限制(总闸) -->
<constant name="struts.multipart.maxSize" value="12097152"></constant>
<package name="upload" namespace="/upload" extends="struts-default" >
<interceptors>
<!-- 自己配置一个拦截器栈 -->
<interceptor-stack name="defaultStack2">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload">
<param name="maximumSize">12097152</param>
<param name="allowedExtensions">jpg,jpeg,bmp</param>
<param name="allowedTypes">image/pjpeg</param>
</interceptor-ref>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="defaultStack2"></default-interceptor-ref>
<action name="Demo1Action" class="cn.hcx.e_fileupload.Demo1Action">
<result>/upload/demo1.jsp</result>
<result name="input">/upload/demo1.jsp</result>
</action>
<action name="Demo2Action" class="cn.hcx.e_fileupload.Demo2Action">
<result>/upload/demo2.jsp</result>
<result name="input">/upload/demo2.jsp</result>
</action>
</package>
</struts>
修改提示信息:(重写配置文件中的信息)
fileUpload拦截器也有3个参数,我们可以给fileUpload拦截器配置这3个参数:
- maximumSize:上传的单个文件的大小限制;
- allowedTypes:允许上传文件的类型,多个类型以逗号隔开;
- allowedExtensions:允许上传文件的扩展名,多个扩展名以逗号隔开;
<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.multipart.maxSize" value="1048576" />
<package name="s8" namespace="/" extends="struts-default">
<action name="UploadAction" class="cn.hcx.upload.action.UploadAction">
<result name="input">/demo1/upload.jsp</result>
<param name="savepath">/WEB-INF/uploads</param>
<interceptor-ref name="defaultStack">
<!-- 限制单个文件大小上限为512K -->
<param name="fileUpload.maximumSize">524288</param>
<param name="fileUpload.allowedExtensions">jpg,png,bmp</param>
</interceptor-ref>
</action>
</package>
</struts>
五、上传相关的错误信息国际化
在上传文件时如果出现错误,那么在input视图显示的错误信息都是英文的。
如果想替换这些信息,需要知道这些错误信息的资源key,然后在我们自己的国际化资源文件中指定这些key的新值即可。
与上传相关的错误信息都在org.apache.struts2包下的struts-message.properties文件中。
struts.messages.error.uploading=Error uploading: {0}
struts.messages.error.file.too.large=The file is to large to be uploaded: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}
struts.messages.upload.error.SizeLimitExceededException=Request exceeded allowed size limit! Max size allowed is: {0} but request was: {1}!
我们可以在src下res.properties文件,在这个文件中对象以上资源key进行替换。
然后在struts.xml文件中给出<constant name="struts.custom.i18n.resources"value="res/">即可。
<body>
<s:form action="Demo1Action" method="post" namespace="/upload" enctype="multipart/form-data" >
<s:file name="file" label="文件上传" ></s:file>
<s:submit value="上传" ></s:submit>
</s:form>
<s:actionerror/>
<s:debug></s:debug>
</body>
package cn.hcx.e_fileupload;
import java.io.File;
import java.util.UUID;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class Demo1Action extends ActionSupport {
private File file; //跟表单提交的键一致
private String fileContentType;
private String fileFileName;
public String getFileContentType() {
return fileContentType;
}
public void setFileContentType(String fileContentType) {
this.fileContentType = fileContentType;
}
public String getFileFileName() {
return fileFileName;
}
public void setFileFileName(String fileFileName) {
this.fileFileName = fileFileName;
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
@Override
//将文件放入 res文件夹下
public String execute() throws Exception {
System.out.println(file);
//----------------------------
//1 获得res文件夹的路径
String dirPath = ServletActionContext.getServletContext().getRealPath("/res");
//2 创建目标文件
String fileName = UUID.randomUUID().toString();
File target = new File(dirPath+"/"+fileName);
//3 将file拷贝到目标文件中
file.renameTo(target);
//----------------------------------
System.out.println("上传文件的Mime类型:"+fileContentType);
System.out.println("上传文件的文件名称:"+fileFileName);
return SUCCESS;
}
}
<action name="Demo1Action" class="cn.hcx.e_fileupload.Demo1Action">
<result>/upload/demo1.jsp</result>
<result name="input">/upload/demo1.jsp</result>
</action>
六、多文件上传
当需要上传多个文件时,如果每个<input type=”file”>对应Action的3个属性,那么这会使Action的属性过多的现象。
处理这一问题的方法是在表单中设置所有<input type=”file”>的名称为相同名称,然后在Action中给出数组属性即可。
uploads.jsp
<s:fielderror />
<s:actionerror/>
<form action="<c:url value='/UploadsAction.action'/>" method="post" enctype="multipart/form-data">
用户名: <input type="text" name="username"/><br/>
文 件1:<input type="file" name="myUpload"/><br/>
文 件2:<input type="file" name="myUpload"/><br/>
文 件3:<input type="file" name="myUpload"/><br/>
<input type="submit" value="Submit"/>
</form>
UploadsAction
public class UploadsAction extends ActionSupport {
private File[] myUpload;
private String[] myUploadContentType;
private String[] myUploadFileName;
private String savepath;
public void setSavepath(String savepath) {
this.savepath = savepath;
}
public void setMyUpload(File[] myUpload) {
this.myUpload = myUpload;
}
public void setMyUploadContentType(String[] myUploadContentType) {
this.myUploadContentType = myUploadContentType;
}
public void setMyUploadFileName(String[] myUploadFileName) {
this.myUploadFileName = myUploadFileName;
}
public String execute() throws Exception {
savepath = ServletActionContext.getServletContext().getRealPath(savepath);
for(int i = 0; i < myUpload.length; i++) {
System.out.println("文件名:" + myUploadFileName[i]);
System.out.println("文件类型:" + myUploadContentType[i]);
FileUtils.copyFile(myUpload[i], new File(savepath, myUploadFileName[i]));
}
return NONE;
}
}
其中Struts2也允许我们使用List类型来代替数组属性。
UploadsAction.java
public class UploadsAction extends ActionSupport {
private List<File> myUpload;
private List<String> myUploadContentType;
private List<String> myUploadFileName;
…
}
jsp:
<body>
<s:form action="Demo2Action" method="post" namespace="/upload" enctype="multipart/form-data" >
<s:file name="file" label="文件上传" ></s:file>
<s:file name="file" label="文件上传" ></s:file>
<s:file name="file" label="文件上传" ></s:file>
<s:submit value="上传" ></s:submit>
</s:form>
<s:actionerror/>
<s:debug></s:debug>
</body>
package cn.hcx.e_fileupload;
import java.io.File;
import java.util.Arrays;
import java.util.UUID;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class Demo2Action extends ActionSupport {
private File[] file;
private String[] fileContentType;
private String[] fileFileName;
public File[] getFile() {
return file;
}
public void setFile(File[] file) {
this.file = file;
}
public String[] getFileContentType() {
return fileContentType;
}
public void setFileContentType(String[] fileContentType) {
this.fileContentType = fileContentType;
}
public String[] getFileFileName() {
return fileFileName;
}
public void setFileFileName(String[] fileFileName) {
this.fileFileName = fileFileName;
}
@Override
//将文件放入 res文件夹下
public String execute() throws Exception {
System.out.println("上传文件:"+Arrays.toString(file));
System.out.println("上传文件的Mime类型:"+Arrays.toString(fileContentType));
System.out.println("上传文件的文件名称:"+Arrays.toString(fileFileName));
return SUCCESS;
}
}