单文件上传
Struts2 框架为依据“基于表单的HTML文件上传”所进行的文件处理上传提供了内置支持。当文件上传时,它通常会存储在临时目录中,然后Action类应对其进行处理或移动到固定目录中,以确保数据不会丢失。通过一个名为FileUpload的预定义拦截器可以在Struts中上传文件,该拦截器可通过org.apache.struts2.interceptor.FileUploadInterceptor类获得,并作为defaultStack的一部分包含在内。你也将在接下来的内容中看到如何使用它在struts.xml文件中设置各种参数。
上传页面:
<s:form action="/file/fileUploadAction" enctype="multipart/form-data" method="post"><!-- 默认为post -->
<s:file name="file" label="上传文件"/>
<s:submit value="提交"/>
<s:reset value="重置"/>
</s:form>
enctype指定了HTTP请求的Content-Type。
默认情况下,HTML的form表单的enctype=application/x-www-form-urlencoded。
application/x-www-form-urlencoded是指表单的提交,并且将提交的数据进行urlencode。
默认情况下,我们所有的表单提交都是通过这种默认的方式实现的。
上述为文件上传,需将form的enctype参数设置为multipart/form-data。这种方式只支持POST的请求方式。
Contype-Type=multipart/form-data情况的时候,都会通过一个特殊的字符串来将原始POST数据进行分割。
我们可以看到下面的请求中Content-type的类型
总结
- application/x-www-form-urlencoded : 窗体数据被编码为名称/值对。这是标准的编码格式。
- multipart/form-data : 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition(form-data或者file)、Content-Type(默认为text/plain)、name(控件name)等信息,并加上分割符(boundary)。
- text/plain : 窗体数据以纯文本形式进行编码,其中不含任何控件或格式字符。
Action
public class FileUploadAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private File file;
private String fileFileName;
private String fileContentType;
// 省略getter/setter
public String execute() {
ActionContext context = ActionContext.getContext();
String msg = "";
try {
InputStream is = new FileInputStream(getFile());
String uploadPath = ServletActionContext.getServletContext().getRealPath("/file");
System.out.println(uploadPath);
File toFile = new File(uploadPath, getFileFileName());
OutputStream os = new FileOutputStream(toFile);
byte[] buffer = new byte[1024];
int length = 0;
while(-1!=(length=is.read(buffer, 0, buffer.length))) {
os.write(buffer);
}
is.close();
os.close();
}catch(Exception err){
err.printStackTrace();
msg = "Error!";
context.put("message", msg);
return INPUT;
}
msg = "Successfully";
context.put("message", msg);// 默认为request范围
context.put("fileFileName", fileFileName);
context.put("fileContentType", fileContentType);
return SUCCESS;
}
}
由于Struts2内置的文件拦截器FileUploadInterceptor的原因,Action中的属性名fileFileName和fileContentType是固定的,由该拦截器负责填充;
而file属性则与上传页面中的file标签的属性name值相对应。简单建立文件输入输出流,然后构建缓冲区,先读到缓冲区,然后再从缓冲区中刷出到指定目录下的文件中。先通过上下文找到对应的文件存储目录,
根据上传的文件获得文件名,在该目录下建立一个相同的文件,从而构建输出流。
Struts.xml配置
<action name="fileUploadAction" class="com.livejq.action.FileUploadAction">
<result>/done.jsp</result>
<result name="input">/undone.jsp</result><!--没有错误输出页面可能会抛出异常 -->
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">10485760</param><!-- 默认2MB -->
<param name="fileUpload.allowedExtensions">.txt,.doc,.jpg,.png,.mp3</param>
<param name="fileUpload.allowedType">text/plain,application/msword,image/jpeg,audio/mpeg</param>
</interceptor-ref>
</action>
设置文件上传最大值也可通过配置常量(此常量会覆盖上面设置的最大值):
struts.multipart.maxSize=140000
输出结果
F:\eclipseFramework\LoginDemo\WebContent\file
由于环境整体采用了UTF-8编码,结果没有出现中文乱码。
单文件下载
下载页面
<s:a href="/LoginDemo/file/downLoad?filename=%E6%B5%8B%E8%AF%95.txt">测试.txt</s:a>
<s:a href="/LoginDemo/file/downLoad?filename=test.txt">test.txt</s:a>
Action
public class DownLoadAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private String filename;
public String getContentType(){
return ServletActionContext.getServletContext().getMimeType(filename);
}
public InputStream getDownloadFile(){
String filePath = "/file/"+filename;
return ServletActionContext.getServletContext().getResourceAsStream(filePath);
}
public void setFilename(String filename){
System.out.println("setFilename "+filename);
this.filename = filename;
}
public String getFilename() throws IOException {
return encodeDownloadFilename(filename, ServletActionContext.getRequest().getHeader("User-Agent"));
}
private String encodeDownloadFilename(String name, String agent) {
try {
// TODO Auto-generated method stub
if (null != agent && -1 != agent.indexOf("MSIE") || null != agent
&& -1 != agent.indexOf("Trident")) {// ie
name = java.net.URLEncoder.encode(name, "UTF-8");
System.out.println("encodeDownloadFilename "+name);
} else if (null != agent && -1 != agent.indexOf("Mozilla")) {// 火狐,chrome等
name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
System.out.println("after encodeDownloadFilename "+name);
}
} catch (Exception e) {
e.printStackTrace();
}
return name;
}
public String execute() throws Exception{
return SUCCESS;
}
}
由于Action是直接从上文中的上传目录中对文件构建源进行下载的,请确保file目录中存在要下载的文件。
中文乱码问题
编程人员无论如何都会遇到中文乱码这个问题,这个得通过对整个传输过程的细致分析,才能一步一步得出结论来。
由于浏览器的不同,有IE/Firefox/Chrome/Opera等,得首先对他们进行判断。
- IE(带MSIE/Trident字样):
User-Agent: Mozilla/5.0 (MSIE 9.0; Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
- 其它(Chrome为例:只是Mozilla)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36
分析
这里通过查看浏览器发送的请求和给出的响应,还有Action中的局部输出来证实自己的分析:
IE
这里是IE中点击下载的文件时发送的请求:
请求 URL: http://localhost:8080/LoginDemo/file/downLoad?filename=%E6%B5%8B%E8%AF%95.txt
细致的你可能已经发现下载页面中的filename的值为UTF-8编码后的字符。对于IE来说,当判断为已编码,则直接发送请求,
但如果是未编码,在这里为中文,则IE浏览器会采用默认的西文字符ISO8859-1进行编码,你也可以试试中文,只要做相应调整。
而中文编码后发送的请求如下:
请求 URL: http://localhost:8080/LoginDemo/file/downLoad?filename=æµè¯.txt
然后传到Action中时,由于全局过滤器的缘故,
对所有到来的请求进行了UTF-8的转码,控制台输出如下:
setFilename 测试.txt
然后在getFilename方法中:
对IE进行java.net.URLEncoder.encode(name,"UTF-8")编码,
因为从上文可以看出其在通过Get方法传递参数时并未做编码处理。编码后控制台
输出
encodeDownloadFilename %E6%B5%8B%E8%AF%95.txt
IE响应头
Content-Disposition: attachment; filename="%E6%B5%8B%E8%AF%95.txt"
转码统一采用Unicode,然后在浏览器中就可以看到中文了。
其它浏览器
其它浏览器相对友好一点,如Chrome,filename当设置成中文时则采用Unicode进行编码,若已编码则不变,它发送请求时都为:
Request URL: http://localhost:8080/LoginDemo/file/downLoad?filename=%E6%B5%8B%E8%AF%95.txt
Action中与上同:
setFilename 测试.txt
然后在getFilename方法中:
name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
由于Chrome等浏览器在接收数据时默认采用西文字符ISO8859-1进行解码,所以在Action中使用ISO8859-1进行编码,输出为:
after encodeDownloadFilename æµè¯.txt
然后在浏览器中就可以看到中文了,Chrome响应头直接为中文:
Content-Disposition: attachment;filename=测试.txt
评论区