struts2文件上传其实原理和我们前面的文件上传一样,就是对文件上传的操作进行了封装,使得更加方便快捷。
struts2文件上传拦截器API:
org.apache.struts2.interceptor 类 FileUploadInterceptor java.lang.Object com.opensymphony.xwork2.interceptor.AbstractInterceptor org.apache.struts2.interceptor.FileUploadInterceptor 所有已实现的接口: com.opensymphony.xwork2.interceptor.Interceptor, Serializable -------------------------------------------------------------------------------- public class FileUploadInterceptorextends com.opensymphony.xwork2.interceptor.AbstractInterceptor Interceptor that is based off of MultiPartRequestWrapper, which is automatically applied for any request that includes a file. It adds the following parameters, where [File Name] is the name given to the file uploaded by the HTML form: [File Name] : File - the actual File [File Name]ContentType : String - the content type of the file [File Name]FileName : String - the actual name of the file uploaded (not the HTML name) You can get access to these files by merely providing setters in your action that correspond to any of the three patterns above, such as setDocument(File document), setDocumentContentType(String contentType), etc. See the example code section. This interceptor will add several field errors, assuming that the action implements ValidationAware. These error messages are based on several i18n values stored in struts-messages.properties, a default i18n file processed for all i18n requests. You can override the text of these messages by providing text for the following keys: struts.messages.error.uploading - a general error that occurs when the file could not be uploaded struts.messages.error.file.too.large - occurs when the uploaded file is too large struts.messages.error.content.type.not.allowed - occurs when the uploaded file does not match the expected content types specified struts.messages.error.file.extension.not.allowed - occurs when the uploaded file does not match the expected file extensions specified Interceptor parameters: maximumSize (optional) - the maximum size (in bytes) that the interceptor will allow a file reference to be set on the action. Note, this is not related to the various properties found in struts.properties. Default to approximately 2MB. allowedTypes (optional) - a comma separated list of content types (ie: text/html) that the interceptor will allow a file reference to be set on the action. If none is specified allow all types to be uploaded. allowedExtensions (optional) - a comma separated list of file extensions (ie: .html) that the interceptor will allow a file reference to be set on the action. If none is specified allow all extensions to be uploaded. Extending the interceptor: You can extend this interceptor and override the acceptFile method to provide more control over which files are supported and which are not. Example code: <action name="doUpload" class="com.example.UploadAction"> <interceptor-ref name="fileUpload"/> <interceptor-ref name="basicStack"/> <result name="success">good_result.jsp</result> </action> You must set the encoding to multipart/form-data in the form where the user selects the file to upload. <s:form action="doUpload" method="post" enctype="multipart/form-data"> <s:file name="upload" label="File"/> <s:submit/> </s:form> And then in your action code you'll have access to the File object if you provide setters according to the naming convention documented in the start. package com.example; import java.io.File; import com.opensymphony.xwork2.ActionSupport; public UploadAction extends ActionSupport { private File file; private String contentType; private String filename; public void setUpload(File file) { this.file = file; } public void setUploadContentType(String contentType) { this.contentType = contentType; } public void setUploadFileName(String filename) { this.filename = filename; } public String execute() { //... return SUCCESS; } }
文件上传的前台页面:
<body> <form action="${pageContext.request.contextPath }/upload/uploadAction_saveFile.do" name="form1" method="post" enctype="multipart/form-data"> 上传文件名称:<input type="file" name="uploadImage"> <input type="submit" value="上传"> </form> </body>
首先我们书写一个Action,用作文件上传:
根据前面的类型转换的知识,我们知道struts2通过拦截器可以将内存中的对象自动进行类型转换,注入Action中的对应属性,只需要我们提供get,set方法。
根据前面的API我们可以得知,struts2文件上传拦截器可以得到上传文件的File对象,上传文件的类型ContentType,上传文件的真实名,我们接受它们需要使用的命名规范。
[File Name] : File - the actual File [File Name]ContentType : String - the content type of the file [File Name]FileName : String - the actual name of the file uploaded (not the HTML name)
然后我们可以在struts2对应方法中使用这些对象完成文件上传。
例子:
package cn.itcast.upload; import java.io.File; import javax.servlet.ServletContext; import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class UploadAction extends ActionSupport { //上传文件的存储的临时文件 private File uploadImage; //上传文件的类型 private String uploadImageContentType; //上传文件的真实名称 private String uploadImageFileName; public String saveFile() throws Exception{ System.out.println("UploadAction ****** saveFile()"); ServletContext context=ServletActionContext.getServletContext(); String path=context.getRealPath("/fileupload"); File file=new File(path,uploadImageFileName); FileUtils.copyFile(uploadImage, file); uploadImage.delete();//只有struts2.1.8支持自动清空临时文件,其他的都需要手动调用一下 return "success"; } public File getUploadImage() { return uploadImage; } public void setUploadImage(File uploadImage) { this.uploadImage = uploadImage; } public String getUploadImageContentType() { return uploadImageContentType; } public void setUploadImageContentType(String uploadImageContentType) { this.uploadImageContentType = uploadImageContentType; } public String getUploadImageFileName() { return uploadImageFileName; } public void setUploadImageFileName(String uploadImageFileName) { this.uploadImageFileName = uploadImageFileName; } }
注意:
1、action中的几个属性分别是:
File 页面的文件域的name属性值fileName;
String [fileName]+ContentType;
String [fileName]+FileName;
加号表示拼接,中括号内表示可变。
2、提供对应的getter、setter方法
上面这样已经可以完成了基本的文件上传操作,但其实还是有点限制的。比如,上传的文件大小受限制。
我上传一个70M的文件,它就报错了。默认的上传大小限制好像是2M,解决方案:
设置上传文件的总开关
<!--设置comonFileupload上传组件允许的文件大小,默认值是2M--> <constant name="struts.multipart.maxSize" value="2097152"/>
还有一些可以设置的文件上传的限制:
### Parser to handle HTTP POST requests, encoded using the MIME-type multipart/form-data # struts.multipart.parser=cos # struts.multipart.parser=pell # struts.multipart.parser=jakarta-stream struts.multipart.parser=jakarta # uses javax.servlet.context.tempdir by default struts.multipart.saveDir= struts.multipart.maxSize=2097152
注意:
struts2文件上传是使用了文件上传的拦截器,为什么我这里不需要配置拦截器呢?因为我的packege继承了struts-default,其中的默认拦截器栈包含了文件上传拦截器。
单个文件上传的其他限制:
设置单个文件上传的大小,设置允许上传的文件类型,设置允许上传的后缀名
<action name="uploadAction_saveFile" class="cn.itcast.upload.UploadAction" method="saveFile"> <!-- 我们知道文件上传拦截器是在默认拦截器栈被调用,给它赋参数这样做 --> <interceptor-ref name="defaultStack"> <!-- 设置上传单个文件的大小,与struts.xml配置常量的大小的不同之处在于常量配置的是一次上传操作的大小限制, 有可能是我们进行文件上传的时候表单大小的限制,而这里是一个文件项的大小限制 --> <param name="fileUpload.maximumSize">209715200</param> <!-- 设置文件上传所允许的类型,多个使用,隔开 --> <param name="fileUpload.allowedTypes">text/plain,application/octet-stream</param> <!-- 设置文件上传所允许的后缀名 ,多个使用,隔开--> <param name="fileUpload.allowedExtensions">.txt,.jar</param> <!-- 注意:如果设置了允许上传的类型和允许上传的后缀名的话,上传的类型和后缀名要一一对应,否则可能互相影响导致上传失败 --> </interceptor-ref> <result name="success">/upload/success.jsp</result> <result name="input">/upload/error.jsp</result> </action>
上传多个文件:
说明:
与前面上传单个文件基本一致。示例前端页面:
<body> <form action="${pageContext.request.contextPath }/upload/uploadAction02_saveFiles.do" name="form1" method="post" enctype="multipart/form-data"> 上传文件名称:<input type="file" name="uploadImages"><br/> 上传文件名称:<input type="file" name="uploadImages"><br/> 上传文件名称:<input type="file" name="uploadImages"><br/> <input type="submit" value="上传"> </form> </body>
注意:文件域的name需要都一致
对应Action:
package cn.itcast.upload; import java.io.File; import java.io.IOException; import javax.servlet.ServletContext; import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class UploadAction02 extends ActionSupport { //上传文件的存储的临时文件 private File [] uploadImages; //上传文件的类型 private String [] uploadImagesContentType; //上传文件的真实名称 private String [] uploadImagesFileName; public String saveFiles(){ System.out.println("UploadAction ****** saveFile()"); ServletContext context=ServletActionContext.getServletContext(); String path=context.getRealPath("/fileupload"); for(int i=0;i<uploadImages.length;i++){ File file=new File(path,uploadImagesFileName[i]); try { FileUtils.copyFile(uploadImages[i], file); } catch (IOException e) { e.printStackTrace(); } uploadImages[i].delete(); } return "success"; } public File[] getUploadImages() { return uploadImages; } public void setUploadImages(File[] uploadImages) { this.uploadImages = uploadImages; } public String[] getUploadImagesFileName() { return uploadImagesFileName; } public void setUploadImagesFileName(String[] uploadImagesFileName) { this.uploadImagesFileName = uploadImagesFileName; } public void setUploadImagesContentType(String [] uploadImagesContentType) { this.uploadImagesContentType = uploadImagesContentType; } public String [] getUploadImagesContentType() { return uploadImagesContentType; } }
注意:
多文件上传的每个对应的属性都成为了数组,然后使用遍历成单文件上传一一处理。
处理文件上传的错误提示:
我们的错误页面可以使用struts2的<s:filederror>来接受特定信息。
默认的上传错误信息:
struts.messages.error.uploading=Error uploading: {0} struts.messages.error.file.too.large=File {0} is too large to be uploaded. Maximum allowed size is {4} bytes! 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}
如果你希望更改错误信息的显示,做法和前面类型转换一样,在Action的同级目录下新建propertis资源文件,资源文件内容和上面的默认错误信息内容格式一致,然后在struts.xml中加载资源文件即可。
补充代码:
# # $Id$ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # struts.messages.invalid.token=The form has already been processed or no token was supplied, please try again. struts.internal.invalid.token=Form token {0} does not match the session token {1}. struts.messages.bypass.request=Bypassing {0}/{1} struts.messages.current.file=File {0} {1} {2} {3} struts.messages.invalid.file=Could not find a Filename for {0}. Verify that a valid file was submitted. struts.messages.invalid.content.type=Could not find a Content-Type for {0}. Verify that a valid file was submitted. struts.messages.removing.file=Removing file {0} {1} struts.messages.error.uploading=Error uploading: {0} struts.messages.error.file.too.large=File {0} is too large to be uploaded. Maximum allowed size is {4} bytes! 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} # dedicated messages used to handle various problems with file upload - check {@link JakartaMultiPartRequest#parse(HttpServletRequest, String)} struts.messages.upload.error.SizeLimitExceededException=Request exceeded allowed size limit! Max size allowed is: {0} but request was: {1}! struts.messages.upload.error.IOException=Error uploading: {0}! devmode.notification=Developer Notification (set struts.devMode to false to disable this message): {0} struts.exception.missing-package-action.with-context = There is no Action mapped for namespace [{0}] and action name [{1}] associated with context path [{2}].
<?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> <!-- /upload/uploadAction_saveFile.do --> <package name="upload" namespace="/upload" extends="struts-default"> <action name="uploadAction_saveFile" class="cn.itcast.upload.UploadAction" method="saveFile"> <!-- 我们知道文件上传拦截器是在默认拦截器栈被调用,给它赋参数这样做 --> <interceptor-ref name="defaultStack"> <!-- 设置上传单个文件的大小,与struts.xml配置常量的大小的不同之处在于常量配置的是一次上传操作的大小限制, 有可能是我们进行文件上传的时候表单大小的限制,而这里是一个文件项的大小限制 --> <param name="fileUpload.maximumSize">209715200</param> <!-- 设置文件上传所允许的类型,多个使用,隔开 --> <param name="fileUpload.allowedTypes">text/plain,application/octet-stream</param> <!-- 设置文件上传所允许的后缀名 ,多个使用,隔开--> <param name="fileUpload.allowedExtensions">.txt,.jar</param> <!-- 注意:如果设置了允许上传的类型和允许上传的后缀名的话,上传的类型和后缀名要一一对应,否则可能互相影响导致上传失败 --> </interceptor-ref> <result name="success">/upload/success.jsp</result> <result name="input">/upload/error.jsp</result> </action> <action name="uploadAction02_saveFiles" class="cn.itcast.upload.UploadAction02" method="saveFiles"> <result name="success">/upload/success.jsp</result> <result name="input">/upload/error.jsp</result> </action> </package> </struts>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'error.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> error page <br> <s:fielderror></s:fielderror> </body> </html>
这里有个东西要提一下:
我使用的是struts2.3.3 。文件上传遇到了一个bug,当上传的文件过大的时候,程序会卡死在拦截器处理的页面中,而不是抛异常跳错误页面。这个问题我这里忽视了。其他问题都能成功!有知道原因的可以告知下,谢谢!