struts2 文件上传和下载,以及部分源代码解析
struts2 文件上传 和部分源代码解析,以及一般上传原理
(1) 单文件上传
一.简单介绍
Struts2并未提供自己的请求解析器,也就是就Struts2不会自己去处理multipart/form-data的请求,它须要调用其它请求解析器,将HTTP请求中的表单域解析出来。
但Struts2在原有的上传解析器基础
上做了进一步封装,更进一步简化了文件上传。
Struts2默认使用的是Jakarta的Common-FileUpload框架来上传文件,因此,要在web应用中添加两个Jar文件:commons-fileupload-1.2.jar和commons-io-1.3.1.jar。它在原上传框架上做了进一步封装
,简化了文件上传的代码实现,取消了不同上传框架上的编程差异。
假设要改成其他的文件上传框架,能够改动struts.multipart.parser常量的值为cos/pell,默认值是jakata。并在classpath中添加对应上传组件的类库
比如配置成cos上传
struts.multipart.parser=cos
struts.multipart.maxSize=1024 指定文件的最大字结数
二.原理
无论用common-fileUPload框架,还是用cos,都是通过将HTTP的数据保存到暂时目录。然后Struts使用fileUpload拦截器将文件绑定到Action的实例中。
也就是配置文件的
我们能够通过源码struts2-code-XX.jar的struts-default.xml文件找到
打开这个类的源码能够看见相关例如以下:
/**
*
*
* Interceptor that is based off of {@link 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)
*
*
*
也就是说我们须要三个变量File(表单的name)。其它两个參数通过set个体方法有strtus调用
接着以下是一些国际化提示的东西:
* 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
*
比如struts.messages.error.content.type.not.allowed 表示文件类型错误:也就是说假设我们给拦截器配置了属性allowedTypes 比如:
image/bmp,image/png,image/gif,image/jpeg,image/jpg 可是上传的时候没有上传规定的类型
struts2就会去我们的资源文件去找key为struts.messages.error.content.type.not.allowed的国际化资源给与提示这时候我们能够在我们的资源中配置这个key:
比如:struts.messages.error.content.type.not.allowed=您上传的文件类型仅仅能为...。请又一次选择!
(当然须要)globalMessages为资源前缀,然后通过:来显示提示
*
*
- *
*
- 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.
*
*
上面则是拦截器的相关參数,一目了然:maximumSize 上传文件最大多少 默认:2MB。allowedTypes容许的上传类型。allowedExtensions容许的扩展名
接着是相关action的代码说明:
* 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;
* }
* }
事实上最基本的是set方法的确定:我们跟踪到大约238行:
String contentTypeName = inputName + "ContentType";
String fileNameName = inputName + "FileName";
终于确定我们的private File file;属性名称能够随便,
可是filenam和contenttype的set方法要有规定 比如:
假设private File myFile;
则相应的其它的两个属性set方法例如以下:
public void setMyFileContentType(String contentType) {
this.contentType = contentType;//当然contentType能够随便起名 终于要的是set+MyFile+ContentType方法
}
public void setMyFileFileName(String filename) {
this.filename = filename;/当然filename能够随便起名 终于要的是set+MyFile+FileName方法
}
下面是实例:
三.须要的jar包(默认使用commons-fileupload,假设使用cos。要将jar引进来)
commons-logging-1.1.jar
freemarker-2.3.8.jar
ognl-2.6.11.jar
struts2-core-2.0.6.jar
xwork-2.0.1.jar
commons-io-1.3.1.jar
commons-fileupload-1.2.jar
四.实例
1.首先,创建上传页面
Html代码
1.
2.
3.
4.
5.
6.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
2.action
1.package com;
2.
3.import java.io.BufferedInputStream;
4.import java.io.BufferedOutputStream;
5.import java.io.File;
6.import java.io.FileInputStream;
7.import java.io.FileOutputStream;
8.import java.io.InputStream;
9.import java.io.OutputStream;
10.import java.util.Date;
11.
12.import org.apache.struts2.ServletActionContext;
13.
14.import com.opensymphony.xwork2.ActionSupport;
15.
16.public class FileUploadAction extends ActionSupport {
17.
18. private static final long serialVersionUID = 6452146812454l;
19.
20. private File upload;
21.
22. private String uploadContentType;
23.
24. private String uploadFileName;
25.
26. private String imageFileName;
27.
28. public String getUploadContentType() {
29. return uploadContentType;
30. }
31.
32. public void setUploadContentType(String uploadContentType) {
33. this.uploadContentType = uploadContentType;
34. }
35.
36. public File getUpload() {
37. return upload;
38. }
39.
40. public void setUpload(File upload) {
41. this.upload = upload;
42. }
43.
44. public String getUploadFileName() {
45. return uploadFileName;
46. }
47.
48. public void setUploadFileName(String uploadFileName) {
49. this.uploadFileName = uploadFileName;
50. }
51.
52. public void setImageFileName(String imageFileName) {
53. this.imageFileName = imageFileName;
54. }
55.
56. public String getImageFileName() {
57. return imageFileName;
58. }
59.
60. private static void copy(File src, File dst) {
61. try {
62. InputStream in = null;
63. OutputStream out = null;
64. try {
65. in = new BufferedInputStream(new FileInputStream(src));
66. out = new BufferedOutputStream(new FileOutputStream(dst));
67. byte[] buffer = new byte[1024*10];
68. while (in.read(buffer) > 0) {
69. out.write(buffer);
70. }
71. } finally {
72. if (null != in) {
73. in.close();
74. }
75. if (null != out) {
76. out.close();
77. }
78. }
79. } catch (Exception e) {
80. e.printStackTrace();
81. }
82. }
83.
84. @Override
85. public String execute() {
86. System.out.println(uploadFileName);
87.
88. imageFileName = System.currentTimeMillis() + uploadFileName.substring(uploadFileName.lastIndexOf("."));
89. File imageFile = new File(ServletActionContext.getServletContext()
90. .getRealPath("/uploadImages")
91. + "/" + imageFileName); //我们自己又一次定义的文件名称。也能够直接用 uploadFileName
92. copy(upload, imageFile);
93. return SUCCESS;
94. }
95.
96.}
97
表单的enctype ="multipart/form-data。与一般的上传一样.
会将upload绑定到action的upload,其次他还会将上传记文件的MIME类型绑定到uploadContentType,文件名称绑定到uploadFileName中。他们是通过
setUploadContentType和setUploadFileName进行绑定的,以下进行的多文件上传也是同个道理,只是要用数组或者是list来进行绑定。然后多个文件的MIME类型也会绑定到以数组
名字加ContentType和FileName的字符串数组中。 比方说上传的文件的数组名为:File[] uploads,则它们的MIME类型绑定的相应的数组是uploadsFileName和uploadsContentType.
3.struts.xml的配置
Xml代码
Xml代码
1.
2.
5.
6.
7.
8.
9.
10.
11.
12. image/bmp,image/png,image/gif,image/jpeg,image/jpg
13.
14.
15.
16. /fileUpload.jsp
17. /showUpload.jsp
18.
19.
20.
4.最后是web.xml的配置
Xml代码
1.
2.
7.
8.
9. struts-cleanup
10.
11. org.apache.struts2.dispatcher.ActionContextCleanUp
12.
13.
14.
15.
16. struts2
17.
18. org.apache.struts2.dispatcher.FilterDispatcher
19.
20.
21.
22.
23. struts-cleanup
24. /*
25.
26.
27.
28. struts2
29. /*
30.
31.
32.
33. index.jsp
34.
35.
(2) 多文件上传
多文件上传
与单文件上传相似,实现多文件你能够将多个绑定Action的数组或列表。
例如以下例所看到的。
清单14 多文件上传JSP代码片段
假设你希望绑定到数组,Action的代码应类似:
private File[] uploads;
private String[] uploadSFileName;
private String[] uploadSContentType;
多文件上传数组绑定Action代码片段
假设你想绑定到列表,则应类似:
private List uploads ;
private List uploadSFileName ;
private List uploadSContentType ;
多文件上传列表绑定Action代码片段
另外是一般上传文件的原理:当然详细能够看http协议的rfc文档:
关于multipart/form-data 相关资料能够看;http://www.ietf.org/rfc/rfc1867.txt 大约在[Page 1]的地方有介绍
表单配置multipart/form-data 说明以二进制流的方式传输表单字段的数据:
我们通过下面代码看到request数据流中的内容:
PrintWriter out = response.getWriter();
InputStream is = request.getInputStream();
BufferedReader br = new BufferedReader(
new InputStreamReader(is));
String buffer = null;
while( (buffer = br.readLine()) != null)
{
//在页面中显示读取到的请求參数
out.println(buffer + "
");
}
out.flush();
out.close();
比如:我上传一个文件D:\apache-tomcat-6018\bin\version.sh (tomcat版本号文件)
终于页面显示:
-----------------------------7da1052ec05fe
Content-Disposition: form-data; name="ff"; filename="D:\apache-tomcat-6018\bin\version.sh"
Content-Type: text/plain
#!/bin/sh
# 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.
# resolve links - $0 may be a softlink
PRG="$0"
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`/"$link"
fi
done
PRGDIR=`dirname "$PRG"`
EXECUTABLE=catalina.sh
# Check that target executable exists
if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
echo "Cannot find $PRGDIR/$EXECUTABLE"
echo "This file is needed to run this program"
exit 1
fi
exec "$PRGDIR"/"$EXECUTABLE" version "$@"
-----------------------------7da1052ec05fe--
我们发现我们上传的内容在
-----------------------------7da1052ec05fe
Content-Disposition: form-data; name="ff"; filename="D:\apache-tomcat-6018\bin\version.sh"
Content-Type: text/plain和-----------------------------7da1052ec05fe--中间
因此我们能够通过下面代码来获取上传内容并保存:
//取得HttpServletRequest的InputStream输入流
InputStream is = request.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String buffer = null;
//循环读取请求内容的每一行内容
while( (buffer = br.readLine()) != null)
{
//假设读到的内容以-----------------------------開始。
//且以--结束,表明已到请求内容尾
if(buffer.endsWith("--") && buffer
.startsWith("-----------------------------"))//length为29
{
//跳出循环
break;
}
//假设读到的内容以-----------------------------開始,表明開始了一个表单域
if(buffer.startsWith("-----------------------------"))
{
//假设下一行内容中有filename字符串。表明这是一个文件域
if (br.readLine().indexOf("filename") > 1)
{
//跳过两行,開始处理上传的文件内容
br.readLine();
br.readLine();
//以系统时间为文件名称,创建一个新文件
File file = new File(request.getRealPath("/")
+ System.currentTimeMillis());
//当然我们能够读取filenam来保存这里简化
//创建一个文件输出流
PrintStream ps = new PrintStream(new FileOutputStream(file));
String content = null;
//接着開始读取文件内容
while( (content = br.readLine()) != null)
{
//假设读取的内容以-----------------------------開始。
//表明開始了下一个表单域内容
if(content.startsWith("-----------------------------"))length为29
{
//跳出处理
break;
}
//将读到的内容输出到文件里
ps.println(content);
}
//关闭输出
ps.flush();
ps.close();
}
}
}
br.close();
关于strtus2下载:
下载终于是通过contentType和数据流将数据输出到client来实现。在struts中也是通过InputStream和相关的配置来实现:
相同终于到strtus的下载相关的源码:org.apache.struts2.dispatcher.StreamResult我们看到
public static final String DEFAULT_PARAM = "inputName";
protected String contentType = "text/plain";
protected String contentLength;
protected String contentDisposition = "inline";//在线
protected String contentCharSet ;
protected String inputName = "inputStream";
protected InputStream inputStream;
protected int bufferSize = 1024;
protected boolean allowCaching = true;
当然这些參数都能够在 中配置 比如;
\uploads\document.pdf
Application/pdf
targetFile
attachment;filename="document.pdf"
2048
当中:
contentType:指定被下载文件的文件类型。 application/octet-stream 默认值,能够下载全部类型
inputName:指定被下载文件的入口输入流, 和DownloadAction中的getInputStream()相应,主要是获得实际资源文件
contentDisposition:指定下载的文件名称和显示方式,一般和文件名称一致,可是要注意中文件名称保存时乱码问题,解决的方法就是进行编码处理
targetFile
是下载的入口 我们不须要在我们的action里面配置targetFile变量 但须要getTargetFile方法。默认须要getInputStream()方法 也就是:inputName參数的值就是入口方法去掉get前缀、首字母小写的
字符串
我们的action里面的代码例如以下:
private String inputPath;//通过strtus获取文件地址 也能够直接写比如:String inputPath = ServletActionContext.getRequest().getRealPath("\uploads\document.pdf");
public void setInputPath(String value)
{
inputPath = value;
}
public InputStream getTargetFile() throws Exception
{
return ServletActionContext.getServletContext().getResourceAsStream(inputPath);
}
假设报下面错误:
Can not find a java.io.InputStream with the name [targetFile] in the invocation stack. Check the tag specified for this action.
实际问题是ServletActionContext.getServletContext().getResourceAsStream(inputPath);找不到资源,请检查你的path是否正确。
而关于下载实际struts做了什么呢?我们看一部分源码代码就非常明确了:
HttpServletResponse oResponse = (HttpServletResponse) invocation.getInvocationContext().get(HTTP_RESPONSE);
// Set the content type
...
//Set the content length
...
// Set the content-disposition
...
// Set the cache control headers if neccessary
...
// Get the outputstream
//------------
oOutput = oResponse.getOutputStream();
if (LOG.isDebugEnabled()) {
LOG.debug("Streaming result [" + inputName + "] type=[" + contentType + "] length=[" + contentLength +
"] content-disposition=[" + contentDisposition + "] charset=[" + contentCharSet + "]");
}
// Copy input to output
LOG.debug("Streaming to output buffer +++ START +++");
byte[] oBuff = new byte[bufferSize];
int iSize;
while (-1 != (iSize = inputStream.read(oBuff))) {
oOutput.write(oBuff, 0, iSize);
}
LOG.debug("Streaming to output buffer +++ END +++");
// Flush
oOutput.flush();
}
finally {
if (inputStream != null) inputStream.close();
if (oOutput != null) oOutput.close();
}
//-----------
非常easy。就像曾经在servlet中一样通过getOutputStream 和配置content type ,content-disposition,cache control。content length这些參数的来实现。
这样就非常easy的实现了下载功能。
以上是自己工作之余写的下面总结,不正确的地方希望大家指点,谢谢,转载。请说明地址
很多其它详情
struts2 文件上传和下载,以及部分源代码解析的更多相关文章
- Struts2文件上传和下载(原理)
转自:http://zhou568xiao.iteye.com/blog/220732 1. 文件上传的原理:表单元素的enctype属性指定的是表单数据的编码方式,该属性有3个值:1) ...
- 十六、Struts2文件上传与下载
文件上传与下载 1.文件上传前提:<form action="${pageContext.request.contextPath}/*" method="post& ...
- 【SSH2(实用文章)】--Struts2文件上传和下载的例子
回想一下,再上一篇文章Struts2实现机制,该步骤做一步一步来解决,这种决心不仅要理清再次Struts2用法.映射机制及其在深入分析.最后一个例子来介绍Struts2一种用法,这里将做一个有关文件上 ...
- 学习Struts--Chap07:Struts2文件上传和下载
1.struts2文件上传 1.1.struts2文件上传的基本概述 在开发web应用的时候,我们一般会为用户提供文件上传的功能,比如用户上传一张图像作为头像等.为了能上传文件,我们必须将表单的met ...
- (八)Struts2 文件上传和下载
所有的学习我们必须先搭建好Struts2的环境(1.导入对应的jar包,2.web.xml,3.struts.xml) 第一节:Struts2 文件上传 Struts2 文件上传基于Struts2 拦 ...
- struts2学习(13)struts2文件上传和下载(1)
一.Struts2文件上传: 二.配置文件的大小以及允许上传的文件类型: 三.大文件上传: 如果不配置上传文件的大小,struts2默认允许上传文件最大为2M: 2097152Byte: 例子实现 ...
- Struts2文件上传与下载
一,页面 index.html 在页面中最重要的就是这个文件上传用的 form 表单,注意这里一定要把 form 的encyType属性明确标定为“multipart/form-data”,只有这样. ...
- struts2文件上传和下载
1. struts系统中的拦截器介绍 过滤器:javaweb中的服务器组件,主要针对的请求和响应进行拦截. 拦截器:主要针对方法的调用,进行拦截器,当使用代理对象调用某个方法时候 对方法的调用进行拦截 ...
- 笔记:Struts2 文件上传和下载
为了上传文件必须将表单的method设置为POST,将 enctype 设置为 muiltipart/form-data,只有设置为这种情况下,浏览器才会把用户选择文件的二进制数据发送给服务器. 上传 ...
随机推荐
- FFT与NTT
讲解:http://www.cnblogs.com/poorpool/p/8760748.html 递归版FFT #include <iostream> #include <cstd ...
- Python之Django框架
1.Django简介 Python下有多款不同的 Web 框架,Django是最有代表性的一种.许多成功的网站和APP都基于Django. Django是一个开源的Web应用框架,由Python写成. ...
- EasyUI-Calendar
EasyUI-Calendar 日历篇 Calendar也是页面中经常用到的元素,easyui也为们提供了日历的组件,效果如下图所示: 使用方法如下所示: <div class="ea ...
- js动态添加select菜单 联动菜单
原文发布时间为:2009-11-14 -- 来源于本人的百度文章 [由搬家工具导入] <html> <head> <title>http://hi.baidu.co ...
- Android驱动之 Linux Input子系统之TP——A/B(Slot)协议【转】
转自:http://www.thinksaas.cn/topics/0/646/646797.html 将A/B协议这部分单独拿出来说一方面是因为这部分内容是比较容易忽视的,周围大多数用到input子 ...
- java基于udp实现键盘录入聊天
发送端 package demo02; import java.io.IOException; import java.net.DatagramPacket; import java.net.Data ...
- 【转载】Javascript-XMLHttpRequest对象简介
XMLHttpRequest是Ajax的核心,通过调用XMLHttpRequest对象的属性和方法可以实现在客户端和浏览器之间进行数据的异步传输,从而实现页面的无刷新效果. XMLHttp ...
- 洛谷 P1865 A % B Problem[筛素数/前缀和思想/区间质数个数]
题目描述 区间质数个数 输入输出格式 输入格式: 一行两个整数 询问次数n,范围m 接下来n行,每行两个整数 l,r 表示区间 输出格式: 对于每次询问输出个数 t,如l或r∉[1,m]输出 Cros ...
- Codeforces Round #315 (Div. 2)【贪心/重排去掉大于n的元素和替换重复的元素】
B. Inventory time limit per test 1 second memory limit per test 256 megabytes input standard input o ...
- Akka之BackoffSupervisor
一.背景 最近在开发一个项目,项目的各模块之间是使用akka grpc传输音频帧的,并且各模块中的actor分别都进行了persist.本周在开发过程中遇到了一个bug,就是音频帧在通行一段时间后,整 ...