- 浏览: 10763 次
- 性别:
- 来自: 北京
最新评论
先看效果:
在struts2中上传是很简单的,struts2会先把文件写到临时文件中,以后在提供这个文件的File对象到action中。具体原理看这里:
http://blog.csdn.net/tom_221x/archive/2009/01/12/3761390.aspx。
利用servlet和common-upload.jar很容易实现显示文件上传的进度,在common-upload组件中实现一个ProgressListener接口,组件会把上传的实时进度传给你。但是想在struts2中,实时的显示进度是有些困难的。因为struts2把request对象给封装了,在Action中拿到request对象,如果是上传文件,那么struts2已经把文件写到文件系统里去了。
struts2上传文件的时候封装request对象其实是org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper,也就是说在action中拿到的实际类型是MultiPartRequestWrapper。片段如下:
[java] view plaincopyprint?public class MultiPartRequestWrapper extends StrutsRequestWrapper {
protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
Collection<String> errors;
MultiPartRequest multi;
/**
* Process file downloads and log any errors.
*
* @param request Our HttpServletRequest object
* @param saveDir Target directory for any files that we save
* @param multiPartRequest Our MultiPartRequest object
*/
public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir) {
super(request);
multi = multiPartRequest;
try {
multi.parse(request, saveDir);
for (Object o : multi.getErrors()) {
String error = (String) o;
addError(error);
}
} catch (IOException e) {
addError("Cannot parse request: "+e.toString());
}
}
public class MultiPartRequestWrapper extends StrutsRequestWrapper {
protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
Collection<String> errors;
MultiPartRequest multi;
/**
* Process file downloads and log any errors.
*
* @param request Our HttpServletRequest object
* @param saveDir Target directory for any files that we save
* @param multiPartRequest Our MultiPartRequest object
*/
public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir) {
super(request);
multi = multiPartRequest;
try {
multi.parse(request, saveDir);
for (Object o : multi.getErrors()) {
String error = (String) o;
addError(error);
}
} catch (IOException e) {
addError("Cannot parse request: "+e.toString());
}
}
可以看到在构造的时候,调用multi.parse(request, saveDir)把上传的数据给封装了。这个MultiPartRequest的解析功能是在struts-default.xml中配置的,如下:
[xhtml] view plaincopyprint?<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default"/>
!-- 文件解析器类 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" />
!-- 这就是struts2的文件解析器设置 -->
<constant name="struts.multipart.handler" value="jakarta" />
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default"/>
<!-- 文件解析器类 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" />
<!-- 这就是struts2的文件解析器设置 -->
<constant name="struts.multipart.handler" value="jakarta" />
现在的设想是,strut2不要帮我解析上传的文件,留到action中,我自己设置。所以我们要覆盖这是配置,如下:
[xhtml] view plaincopyprint?<!-- 重写文件上传解析方法 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="myRequestParser"
class="com.*.*.utils.MyRequestParseWrapper" scope="default" optional="true" />
;constant name="struts.multipart.handler" value="myRequestParser" />
<!-- 重写文件上传解析方法 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="myRequestParser"
class="com.*.*.utils.MyRequestParseWrapper" scope="default" optional="true" />
<constant name="struts.multipart.handler" value="myRequestParser" />
这个MyRequestParseWrapper如下:
[java] view plaincopyprint?/**
* 重写struts2的request封装类
*
* @author scott.Cgi
*/
public class MyRequestParseWrapper extends JakartaMultiPartRequest {
/*
* (non-Javadoc)
* @see
* org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest#parse
* (javax.servlet.http.HttpServletRequest, java.lang.String)
*/
@Override
public void parse(HttpServletRequest servletRequest, String saveDir)
throws IOException {
//什么也不做
}
}
/**
* 重写struts2的request封装类
*
* @author scott.Cgi
*/
public class MyRequestParseWrapper extends JakartaMultiPartRequest {
/*
* (non-Javadoc)
* @see
* org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest#parse
* (javax.servlet.http.HttpServletRequest, java.lang.String)
*/
@Override
public void parse(HttpServletRequest servletRequest, String saveDir)
throws IOException {
//什么也不做
}
}
这样一来,在action中拿到的request就是带有上传文件的了。
接下来,所以说实现原理,依然使用common-uplaod.jar组件:
1. 页面有2个iframe,一个上传数据,一个显示进度。
2. 当然有2个action,一个上传数据,一个回写进度。
3. 上传的时候首先请求的是更新进度的iframe, 这个iframe执行客户端js发起上传文件请求,第二个iframe开始上传数据。与此同时,第一个iframe开始回写进度。进度对象保存在session中,通过request的hashcode为key。进度从第一个进度iframe,传递到第二个上传iframe中,实现进度信息的通信。
说明一下,2个iframe是因为,request未结束,不可以向客户端写数据,所以文件超大,就会阻塞回写信息。
具体的上传我封装了一下,具体代码如下:
[java] view plaincopyprint?import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
/**
* upload file
*
* @author scott.Cgi
*/
public class UploadFile {
private static final Logger LOG = Logger.getLogger(UploadFile.class);
/**
* 上传文件
*
* @param request
* http request
* @param response
* htp response
* @throws IOException
* IOException
*/
@SuppressWarnings("unchecked")
public static void upload(HttpServletRequest request,
HttpServletResponse response) throws IOException {
LOG.info("客户端提交类型: " + request.getContentType());
if (request.getContentType() == null) {
throw new IOException(
"the request doesn't contain a multipart/form-data stream");
}
String key = request.getParameter("key");
Progress p = (Progress)request.getSession().getAttribute(key);
// 设置上传文件总大小
p.setLength(request.getContentLength());
LOG.info("上传文件大小为 : " + p.getLength());
// 上传临时路径
String path = request.getSession().getServletContext().getRealPath("/");
LOG.info("上传临时路径 : " + path);
// 设置上传工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(path));
// 阀值,超过这个值才会写到临时目录
factory.setSizeThreshold(1024 * 1024 * 10);
ServletFileUpload upload = new ServletFileUpload(factory);
// 最大上传限制
upload.setSizeMax(1024 * 1024 * 200);
// 设置监听器监听上传进度
upload.setProgressListener(p);
try {
LOG.info("解析上传文件....");
List<FileItem> items = upload.parseRequest(request);
LOG.info("上传数据...");
for (FileItem item : items) {
// 非表单域
if (!item.isFormField()) {
LOG.info("上传路径 : " + path + item.getName());
FileOutputStream fos = new FileOutputStream(path + item.getName());
// 文件全在内存中
if (item.isInMemory()) {
fos.write(item.get());
p.setComplete(true);
} else {
InputStream is = item.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
is.close();
}
fos.close();
LOG.info("完成上传文件!");
item.delete();
LOG.info("删除临时文件!");
p.setComplete(true);
LOG.info("更新progress对象状态为完成状态!");
}
}
} catch (Exception e) {
LOG.error("上传文件出现异常, 错误原因 : " + e.getMessage());
// 发生错误,进度信息对象设置为完成状态
p.setComplete(true);
request.getSession().removeAttribute(key);
}
}
/**
* 执行客户端脚本
*
* @param response
* http response
* @param script
* javscript string
* @throws IOException
* IOException
*/
public static void execClientScript(HttpServletResponse resposne,
String script) throws IOException {
PrintWriter out = resposne.getWriter();
out.println("<mce:script type='text/javascript'><!--
" + script + "
// --></mce:script>");
// fix ie problem
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.flush();
}
/**
* 上传文件进度信息
*
* @author wanglei
* @version 0.1
*/
public static class Progress implements ProgressListener {
// 文件总长度
private long length = 0;
// 已上传的文件长度
private long currentLength = 0;
// 上传是否完成
private boolean isComplete = false;
/*
* (non-Javadoc)
* @see org.apache.commons.fileupload.ProgressListener#update(long,
* long, int)
*/
@Override
public void update(long bytesRead, long contentLength, int items) {
this.currentLength = bytesRead;
}
/**
* the getter method of length
*
* @return the length
*/
public long getLength() {
return length;
}
/**
* the getter method of currentLength
*
* @return the currentLength
*/
public long getCurrentLength() {
return currentLength;
}
/**
* the getter method of isComplete
*
* @return the isComplete
*/
public boolean isComplete() {
return isComplete;
}
/**
* the setter method of the length
*
* @param length
* the length to set
*/
public void setLength(long length) {
this.length = length;
}
/**
* the setter method of the currentLength
*
* @param currentLength
* the currentLength to set
*/
public void setCurrentLength(long currentLength) {
this.currentLength = currentLength;
}
/**
* the setter method of the isComplete
*
* @param isComplete
* the isComplete to set
*/
public void setComplete(boolean isComplete) {
this.isComplete = isComplete;
}
}
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
/**
* upload file
*
* @author scott.Cgi
*/
public class UploadFile {
private static final Logger LOG = Logger.getLogger(UploadFile.class);
/**
* 上传文件
*
* @param request
* http request
* @param response
* htp response
* @throws IOException
* IOException
*/
@SuppressWarnings("unchecked")
public static void upload(HttpServletRequest request,
HttpServletResponse response) throws IOException {
LOG.info("客户端提交类型: " + request.getContentType());
if (request.getContentType() == null) {
throw new IOException(
"the request doesn't contain a multipart/form-data stream");
}
String key = request.getParameter("key");
Progress p = (Progress)request.getSession().getAttribute(key);
// 设置上传文件总大小
p.setLength(request.getContentLength());
LOG.info("上传文件大小为 : " + p.getLength());
// 上传临时路径
String path = request.getSession().getServletContext().getRealPath("/");
LOG.info("上传临时路径 : " + path);
// 设置上传工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(path));
// 阀值,超过这个值才会写到临时目录
factory.setSizeThreshold(1024 * 1024 * 10);
ServletFileUpload upload = new ServletFileUpload(factory);
// 最大上传限制
upload.setSizeMax(1024 * 1024 * 200);
// 设置监听器监听上传进度
upload.setProgressListener(p);
try {
LOG.info("解析上传文件....");
List<FileItem> items = upload.parseRequest(request);
LOG.info("上传数据...");
for (FileItem item : items) {
// 非表单域
if (!item.isFormField()) {
LOG.info("上传路径 : " + path + item.getName());
FileOutputStream fos = new FileOutputStream(path + item.getName());
// 文件全在内存中
if (item.isInMemory()) {
fos.write(item.get());
p.setComplete(true);
} else {
InputStream is = item.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
is.close();
}
fos.close();
LOG.info("完成上传文件!");
item.delete();
LOG.info("删除临时文件!");
p.setComplete(true);
LOG.info("更新progress对象状态为完成状态!");
}
}
} catch (Exception e) {
LOG.error("上传文件出现异常, 错误原因 : " + e.getMessage());
// 发生错误,进度信息对象设置为完成状态
p.setComplete(true);
request.getSession().removeAttribute(key);
}
}
/**
* 执行客户端脚本
*
* @param response
* http response
* @param script
* javscript string
* @throws IOException
* IOException
*/
public static void execClientScript(HttpServletResponse resposne,
String script) throws IOException {
PrintWriter out = resposne.getWriter();
out.println("<mce:script type='text/javascript'><!--
" + script + "
// --></mce:script>");
// fix ie problem
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.flush();
}
/**
* 上传文件进度信息
*
* @author wanglei
* @version 0.1
*/
public static class Progress implements ProgressListener {
// 文件总长度
private long length = 0;
// 已上传的文件长度
private long currentLength = 0;
// 上传是否完成
private boolean isComplete = false;
/*
* (non-Javadoc)
* @see org.apache.commons.fileupload.ProgressListener#update(long,
* long, int)
*/
@Override
public void update(long bytesRead, long contentLength, int items) {
this.currentLength = bytesRead;
}
/**
* the getter method of length
*
* @return the length
*/
public long getLength() {
return length;
}
/**
* the getter method of currentLength
*
* @return the currentLength
*/
public long getCurrentLength() {
return currentLength;
}
/**
* the getter method of isComplete
*
* @return the isComplete
*/
public boolean isComplete() {
return isComplete;
}
/**
* the setter method of the length
*
* @param length
* the length to set
*/
public void setLength(long length) {
this.length = length;
}
/**
* the setter method of the currentLength
*
* @param currentLength
* the currentLength to set
*/
public void setCurrentLength(long currentLength) {
this.currentLength = currentLength;
}
/**
* the setter method of the isComplete
*
* @param isComplete
* the isComplete to set
*/
public void setComplete(boolean isComplete) {
this.isComplete = isComplete;
}
}
}
action代码:
[java] view plaincopyprint?import java.io.IOException;
import com.ufinity.mars.utils.UploadFile;
import com.ufinity.mars.utils.UploadFile.Progress;
import com.ufinity.savor.service.FileService;
/**
* file action
*
* @author scott.Cgi
*/
public class FileAction extends AbstractAction {
/** {field's description} */
private static final long serialVersionUID = 6649027352616232244L;
private FileService fileService;
/**
* 上传文件页面
*
* @return page view
*/
public String preupload() {
return SUCCESS;
}
/**
* 上传文件
*
* @return page view
*/
public String uploadfile() {
try {
UploadFile.upload(this.request, this.response);
} catch (IOException e) {
LOG.error("上传文件发生异常,错误原因 : " + e.getMessage());
}
return null;
}
/**
* 显示上传文件进度进度
*
* @return page view
*/
public String progress() {
String callback1 = this.request.getParameter("callback1");
String callback2 = this.request.getParameter("callback2");
// 缓存progress对象的key值
String key = Integer.toString(request.hashCode());
// 新建当前上传文件的进度信息对象
Progress p = new Progress();
// 缓存progress对象
this.request.getSession().setAttribute(key, p);
response.setContentType("text/html;charset=UTF-8");
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setHeader("expires", "0");
try {
UploadFile.execClientScript(response, callback1 + "(" + key + ")");
long temp = 0l;
while (!p.isComplete()) {
if (temp != p.getCurrentLength()) {
temp = p.getCurrentLength();
// 向客户端显示进度
UploadFile.execClientScript(response, callback2 + "("
+ p.getCurrentLength() + "," + p.getLength() + ")");
} else {
//LOG.info("progress的状态 :" + p.isComplete());
//LOG.info("progress上传的数据量 :+ " + p.getCurrentLength());
//上传进度没有变化时候,不向客户端写数据,写数据过于频繁会让chrome没响应
Thread.sleep(300);
}
}
} catch (Exception e) {
LOG.error("调用客户端脚本错误,原因 :" + e.getMessage());
p.setComplete(true);
}
this.request.getSession().removeAttribute(key);
LOG.info("删除progress对象的session key");
return null;
}
/**
* the getter method of fileService
*
* @return the fileService
*/
public FileService getFileService() {
return fileService;
}
/**
* the setter method of the fileService
*
* @param fileService
* the fileService to set
*/
public void setFileService(FileService fileService) {
this.fileService = fileService;
}
}
import java.io.IOException;
import com.ufinity.mars.utils.UploadFile;
import com.ufinity.mars.utils.UploadFile.Progress;
import com.ufinity.savor.service.FileService;
/**
* file action
*
* @author scott.Cgi
*/
public class FileAction extends AbstractAction {
/** {field's description} */
private static final long serialVersionUID = 6649027352616232244L;
private FileService fileService;
/**
* 上传文件页面
*
* @return page view
*/
public String preupload() {
return SUCCESS;
}
/**
* 上传文件
*
* @return page view
*/
public String uploadfile() {
try {
UploadFile.upload(this.request, this.response);
} catch (IOException e) {
LOG.error("上传文件发生异常,错误原因 : " + e.getMessage());
}
return null;
}
/**
* 显示上传文件进度进度
*
* @return page view
*/
public String progress() {
String callback1 = this.request.getParameter("callback1");
String callback2 = this.request.getParameter("callback2");
// 缓存progress对象的key值
String key = Integer.toString(request.hashCode());
// 新建当前上传文件的进度信息对象
Progress p = new Progress();
// 缓存progress对象
this.request.getSession().setAttribute(key, p);
response.setContentType("text/html;charset=UTF-8");
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setHeader("expires", "0");
try {
UploadFile.execClientScript(response, callback1 + "(" + key + ")");
long temp = 0l;
while (!p.isComplete()) {
if (temp != p.getCurrentLength()) {
temp = p.getCurrentLength();
// 向客户端显示进度
UploadFile.execClientScript(response, callback2 + "("
+ p.getCurrentLength() + "," + p.getLength() + ")");
} else {
//LOG.info("progress的状态 :" + p.isComplete());
//LOG.info("progress上传的数据量 :+ " + p.getCurrentLength());
//上传进度没有变化时候,不向客户端写数据,写数据过于频繁会让chrome没响应
Thread.sleep(300);
}
}
} catch (Exception e) {
LOG.error("调用客户端脚本错误,原因 :" + e.getMessage());
p.setComplete(true);
}
this.request.getSession().removeAttribute(key);
LOG.info("删除progress对象的session key");
return null;
}
/**
* the getter method of fileService
*
* @return the fileService
*/
public FileService getFileService() {
return fileService;
}
/**
* the setter method of the fileService
*
* @param fileService
* the fileService to set
*/
public void setFileService(FileService fileService) {
this.fileService = fileService;
}
}
页面代码:
[xhtml] view plaincopyprint? <mce:style type="text/css"><!--
iframe{
border:none;
width:0;
height:0;
}
#p_out{
width:200px;
height:12px;
margin:10px 0 0 0;
padding:1px;
font-size:10px;
border:solid #6b8e23 1px;
}
#p_in{
width:0%;
height:100%;
background-color:#6b8e23;
margin:0;
padding:0;
}
#dis{
margin:0;
padding:0;
text-align:center;
font-size:12px;
height:12px;
width:200px;
}
--></mce:style><style type="text/css" mce_bogus="1"> iframe{
border:none;
width:0;
height:0;
}
#p_out{
width:200px;
height:12px;
margin:10px 0 0 0;
padding:1px;
font-size:10px;
border:solid #6b8e23 1px;
}
#p_in{
width:0%;
height:100%;
background-color:#6b8e23;
margin:0;
padding:0;
}
#dis{
margin:0;
padding:0;
text-align:center;
font-size:12px;
height:12px;
width:200px;
}
</style>
</head>
<body>
<div class="main">
<div class="top">
<jsp:include page="/top.jsp" />
</div>
<div style="width: 250px; margin: 0 auto;">
<div class="errorbox">
<s:actionerror/>
</div>
<form id="uploadfile_form" name="uploadfile_form" enctype="multipart/form-data"
method="post" target="uploadfile_iframe">
<input type="file" name="file" />
<br><br>
<button onclick="progress()">提交</button>
<div id="p_out"><div id="p_in"></div></div>
<div id="dis"></div>
</form>
<iframe frameborder="0" id="uploadfile_iframe" name="uploadfile_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
<iframe frameborder="0" id="progress_iframe" name="progress_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
</div>
</div>
</body>
<mce:script type="text/javascript"><!--
//上传文件
function uploadFile(key){
document.forms[0].action = 'uploadfile.action?callback=parent.upload&key='+key;
document.forms[0].submit();
document.getElementById('dis').innerHTML = "开始传送数据...";
}
//获取文件上传进度
function progress(){
document.getElementById('progress_iframe').src = 'progress.action?callback1=parent.uploadFile&callback2=parent.upload';
document.getElementById('dis').innerHTML = '初始化数据...';
document.getElementById('p_in').style.width = "0%";
}
//更新进度
function upload(len, total){
document.getElementById('p_in').style.width = (Math.round(len/total*100))+'%';
document.getElementById('dis').innerHTML = len + '/' + total + ' Byte';
if(len === total) {
document.getElementById('dis').innerHTML = "文件上传完成!";
}
}
// --></mce:script>
</html>
<mce:style type="text/css"><!--
iframe{
border:none;
width:0;
height:0;
}
#p_out{
width:200px;
height:12px;
margin:10px 0 0 0;
padding:1px;
font-size:10px;
border:solid #6b8e23 1px;
}
#p_in{
width:0%;
height:100%;
background-color:#6b8e23;
margin:0;
padding:0;
}
#dis{
margin:0;
padding:0;
text-align:center;
font-size:12px;
height:12px;
width:200px;
}
--></mce:style><style type="text/css" mce_bogus="1"> iframe{
border:none;
width:0;
height:0;
}
#p_out{
width:200px;
height:12px;
margin:10px 0 0 0;
padding:1px;
font-size:10px;
border:solid #6b8e23 1px;
}
#p_in{
width:0%;
height:100%;
background-color:#6b8e23;
margin:0;
padding:0;
}
#dis{
margin:0;
padding:0;
text-align:center;
font-size:12px;
height:12px;
width:200px;
}
</style>
</head>
<body>
<div class="main">
<div class="top">
<jsp:include page="/top.jsp" />
</div>
<div style="width: 250px; margin: 0 auto;">
<div class="errorbox">
<s:actionerror/>
</div>
<form id="uploadfile_form" name="uploadfile_form" enctype="multipart/form-data"
method="post" target="uploadfile_iframe">
<input type="file" name="file" />
<br><br>
<button onclick="progress()">提交</button>
<div id="p_out"><div id="p_in"></div></div>
<div id="dis"></div>
</form>
<iframe frameborder="0" id="uploadfile_iframe" name="uploadfile_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
<iframe frameborder="0" id="progress_iframe" name="progress_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
</div>
</div>
</body>
<mce:script type="text/javascript"><!--
//上传文件
function uploadFile(key){
document.forms[0].action = 'uploadfile.action?callback=parent.upload&key='+key;
document.forms[0].submit();
document.getElementById('dis').innerHTML = "开始传送数据...";
}
//获取文件上传进度
function progress(){
document.getElementById('progress_iframe').src = 'progress.action?callback1=parent.uploadFile&callback2=parent.upload';
document.getElementById('dis').innerHTML = '初始化数据...';
document.getElementById('p_in').style.width = "0%";
}
//更新进度
function upload(len, total){
document.getElementById('p_in').style.width = (Math.round(len/total*100))+'%';
document.getElementById('dis').innerHTML = len + '/' + total + ' Byte';
if(len === total) {
document.getElementById('dis').innerHTML = "文件上传完成!";
}
}
// --></mce:script>
</html>
注意: common-upload.jar依赖common-io.jar
在struts2中上传是很简单的,struts2会先把文件写到临时文件中,以后在提供这个文件的File对象到action中。具体原理看这里:
http://blog.csdn.net/tom_221x/archive/2009/01/12/3761390.aspx。
利用servlet和common-upload.jar很容易实现显示文件上传的进度,在common-upload组件中实现一个ProgressListener接口,组件会把上传的实时进度传给你。但是想在struts2中,实时的显示进度是有些困难的。因为struts2把request对象给封装了,在Action中拿到request对象,如果是上传文件,那么struts2已经把文件写到文件系统里去了。
struts2上传文件的时候封装request对象其实是org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper,也就是说在action中拿到的实际类型是MultiPartRequestWrapper。片段如下:
[java] view plaincopyprint?public class MultiPartRequestWrapper extends StrutsRequestWrapper {
protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
Collection<String> errors;
MultiPartRequest multi;
/**
* Process file downloads and log any errors.
*
* @param request Our HttpServletRequest object
* @param saveDir Target directory for any files that we save
* @param multiPartRequest Our MultiPartRequest object
*/
public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir) {
super(request);
multi = multiPartRequest;
try {
multi.parse(request, saveDir);
for (Object o : multi.getErrors()) {
String error = (String) o;
addError(error);
}
} catch (IOException e) {
addError("Cannot parse request: "+e.toString());
}
}
public class MultiPartRequestWrapper extends StrutsRequestWrapper {
protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
Collection<String> errors;
MultiPartRequest multi;
/**
* Process file downloads and log any errors.
*
* @param request Our HttpServletRequest object
* @param saveDir Target directory for any files that we save
* @param multiPartRequest Our MultiPartRequest object
*/
public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir) {
super(request);
multi = multiPartRequest;
try {
multi.parse(request, saveDir);
for (Object o : multi.getErrors()) {
String error = (String) o;
addError(error);
}
} catch (IOException e) {
addError("Cannot parse request: "+e.toString());
}
}
可以看到在构造的时候,调用multi.parse(request, saveDir)把上传的数据给封装了。这个MultiPartRequest的解析功能是在struts-default.xml中配置的,如下:
[xhtml] view plaincopyprint?<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default"/>
!-- 文件解析器类 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" />
!-- 这就是struts2的文件解析器设置 -->
<constant name="struts.multipart.handler" value="jakarta" />
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default"/>
<!-- 文件解析器类 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" />
<!-- 这就是struts2的文件解析器设置 -->
<constant name="struts.multipart.handler" value="jakarta" />
现在的设想是,strut2不要帮我解析上传的文件,留到action中,我自己设置。所以我们要覆盖这是配置,如下:
[xhtml] view plaincopyprint?<!-- 重写文件上传解析方法 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="myRequestParser"
class="com.*.*.utils.MyRequestParseWrapper" scope="default" optional="true" />
;constant name="struts.multipart.handler" value="myRequestParser" />
<!-- 重写文件上传解析方法 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="myRequestParser"
class="com.*.*.utils.MyRequestParseWrapper" scope="default" optional="true" />
<constant name="struts.multipart.handler" value="myRequestParser" />
这个MyRequestParseWrapper如下:
[java] view plaincopyprint?/**
* 重写struts2的request封装类
*
* @author scott.Cgi
*/
public class MyRequestParseWrapper extends JakartaMultiPartRequest {
/*
* (non-Javadoc)
* @see
* org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest#parse
* (javax.servlet.http.HttpServletRequest, java.lang.String)
*/
@Override
public void parse(HttpServletRequest servletRequest, String saveDir)
throws IOException {
//什么也不做
}
}
/**
* 重写struts2的request封装类
*
* @author scott.Cgi
*/
public class MyRequestParseWrapper extends JakartaMultiPartRequest {
/*
* (non-Javadoc)
* @see
* org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest#parse
* (javax.servlet.http.HttpServletRequest, java.lang.String)
*/
@Override
public void parse(HttpServletRequest servletRequest, String saveDir)
throws IOException {
//什么也不做
}
}
这样一来,在action中拿到的request就是带有上传文件的了。
接下来,所以说实现原理,依然使用common-uplaod.jar组件:
1. 页面有2个iframe,一个上传数据,一个显示进度。
2. 当然有2个action,一个上传数据,一个回写进度。
3. 上传的时候首先请求的是更新进度的iframe, 这个iframe执行客户端js发起上传文件请求,第二个iframe开始上传数据。与此同时,第一个iframe开始回写进度。进度对象保存在session中,通过request的hashcode为key。进度从第一个进度iframe,传递到第二个上传iframe中,实现进度信息的通信。
说明一下,2个iframe是因为,request未结束,不可以向客户端写数据,所以文件超大,就会阻塞回写信息。
具体的上传我封装了一下,具体代码如下:
[java] view plaincopyprint?import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
/**
* upload file
*
* @author scott.Cgi
*/
public class UploadFile {
private static final Logger LOG = Logger.getLogger(UploadFile.class);
/**
* 上传文件
*
* @param request
* http request
* @param response
* htp response
* @throws IOException
* IOException
*/
@SuppressWarnings("unchecked")
public static void upload(HttpServletRequest request,
HttpServletResponse response) throws IOException {
LOG.info("客户端提交类型: " + request.getContentType());
if (request.getContentType() == null) {
throw new IOException(
"the request doesn't contain a multipart/form-data stream");
}
String key = request.getParameter("key");
Progress p = (Progress)request.getSession().getAttribute(key);
// 设置上传文件总大小
p.setLength(request.getContentLength());
LOG.info("上传文件大小为 : " + p.getLength());
// 上传临时路径
String path = request.getSession().getServletContext().getRealPath("/");
LOG.info("上传临时路径 : " + path);
// 设置上传工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(path));
// 阀值,超过这个值才会写到临时目录
factory.setSizeThreshold(1024 * 1024 * 10);
ServletFileUpload upload = new ServletFileUpload(factory);
// 最大上传限制
upload.setSizeMax(1024 * 1024 * 200);
// 设置监听器监听上传进度
upload.setProgressListener(p);
try {
LOG.info("解析上传文件....");
List<FileItem> items = upload.parseRequest(request);
LOG.info("上传数据...");
for (FileItem item : items) {
// 非表单域
if (!item.isFormField()) {
LOG.info("上传路径 : " + path + item.getName());
FileOutputStream fos = new FileOutputStream(path + item.getName());
// 文件全在内存中
if (item.isInMemory()) {
fos.write(item.get());
p.setComplete(true);
} else {
InputStream is = item.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
is.close();
}
fos.close();
LOG.info("完成上传文件!");
item.delete();
LOG.info("删除临时文件!");
p.setComplete(true);
LOG.info("更新progress对象状态为完成状态!");
}
}
} catch (Exception e) {
LOG.error("上传文件出现异常, 错误原因 : " + e.getMessage());
// 发生错误,进度信息对象设置为完成状态
p.setComplete(true);
request.getSession().removeAttribute(key);
}
}
/**
* 执行客户端脚本
*
* @param response
* http response
* @param script
* javscript string
* @throws IOException
* IOException
*/
public static void execClientScript(HttpServletResponse resposne,
String script) throws IOException {
PrintWriter out = resposne.getWriter();
out.println("<mce:script type='text/javascript'><!--
" + script + "
// --></mce:script>");
// fix ie problem
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.flush();
}
/**
* 上传文件进度信息
*
* @author wanglei
* @version 0.1
*/
public static class Progress implements ProgressListener {
// 文件总长度
private long length = 0;
// 已上传的文件长度
private long currentLength = 0;
// 上传是否完成
private boolean isComplete = false;
/*
* (non-Javadoc)
* @see org.apache.commons.fileupload.ProgressListener#update(long,
* long, int)
*/
@Override
public void update(long bytesRead, long contentLength, int items) {
this.currentLength = bytesRead;
}
/**
* the getter method of length
*
* @return the length
*/
public long getLength() {
return length;
}
/**
* the getter method of currentLength
*
* @return the currentLength
*/
public long getCurrentLength() {
return currentLength;
}
/**
* the getter method of isComplete
*
* @return the isComplete
*/
public boolean isComplete() {
return isComplete;
}
/**
* the setter method of the length
*
* @param length
* the length to set
*/
public void setLength(long length) {
this.length = length;
}
/**
* the setter method of the currentLength
*
* @param currentLength
* the currentLength to set
*/
public void setCurrentLength(long currentLength) {
this.currentLength = currentLength;
}
/**
* the setter method of the isComplete
*
* @param isComplete
* the isComplete to set
*/
public void setComplete(boolean isComplete) {
this.isComplete = isComplete;
}
}
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
/**
* upload file
*
* @author scott.Cgi
*/
public class UploadFile {
private static final Logger LOG = Logger.getLogger(UploadFile.class);
/**
* 上传文件
*
* @param request
* http request
* @param response
* htp response
* @throws IOException
* IOException
*/
@SuppressWarnings("unchecked")
public static void upload(HttpServletRequest request,
HttpServletResponse response) throws IOException {
LOG.info("客户端提交类型: " + request.getContentType());
if (request.getContentType() == null) {
throw new IOException(
"the request doesn't contain a multipart/form-data stream");
}
String key = request.getParameter("key");
Progress p = (Progress)request.getSession().getAttribute(key);
// 设置上传文件总大小
p.setLength(request.getContentLength());
LOG.info("上传文件大小为 : " + p.getLength());
// 上传临时路径
String path = request.getSession().getServletContext().getRealPath("/");
LOG.info("上传临时路径 : " + path);
// 设置上传工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(path));
// 阀值,超过这个值才会写到临时目录
factory.setSizeThreshold(1024 * 1024 * 10);
ServletFileUpload upload = new ServletFileUpload(factory);
// 最大上传限制
upload.setSizeMax(1024 * 1024 * 200);
// 设置监听器监听上传进度
upload.setProgressListener(p);
try {
LOG.info("解析上传文件....");
List<FileItem> items = upload.parseRequest(request);
LOG.info("上传数据...");
for (FileItem item : items) {
// 非表单域
if (!item.isFormField()) {
LOG.info("上传路径 : " + path + item.getName());
FileOutputStream fos = new FileOutputStream(path + item.getName());
// 文件全在内存中
if (item.isInMemory()) {
fos.write(item.get());
p.setComplete(true);
} else {
InputStream is = item.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
is.close();
}
fos.close();
LOG.info("完成上传文件!");
item.delete();
LOG.info("删除临时文件!");
p.setComplete(true);
LOG.info("更新progress对象状态为完成状态!");
}
}
} catch (Exception e) {
LOG.error("上传文件出现异常, 错误原因 : " + e.getMessage());
// 发生错误,进度信息对象设置为完成状态
p.setComplete(true);
request.getSession().removeAttribute(key);
}
}
/**
* 执行客户端脚本
*
* @param response
* http response
* @param script
* javscript string
* @throws IOException
* IOException
*/
public static void execClientScript(HttpServletResponse resposne,
String script) throws IOException {
PrintWriter out = resposne.getWriter();
out.println("<mce:script type='text/javascript'><!--
" + script + "
// --></mce:script>");
// fix ie problem
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.println("---------------------------------------------------");
out.flush();
}
/**
* 上传文件进度信息
*
* @author wanglei
* @version 0.1
*/
public static class Progress implements ProgressListener {
// 文件总长度
private long length = 0;
// 已上传的文件长度
private long currentLength = 0;
// 上传是否完成
private boolean isComplete = false;
/*
* (non-Javadoc)
* @see org.apache.commons.fileupload.ProgressListener#update(long,
* long, int)
*/
@Override
public void update(long bytesRead, long contentLength, int items) {
this.currentLength = bytesRead;
}
/**
* the getter method of length
*
* @return the length
*/
public long getLength() {
return length;
}
/**
* the getter method of currentLength
*
* @return the currentLength
*/
public long getCurrentLength() {
return currentLength;
}
/**
* the getter method of isComplete
*
* @return the isComplete
*/
public boolean isComplete() {
return isComplete;
}
/**
* the setter method of the length
*
* @param length
* the length to set
*/
public void setLength(long length) {
this.length = length;
}
/**
* the setter method of the currentLength
*
* @param currentLength
* the currentLength to set
*/
public void setCurrentLength(long currentLength) {
this.currentLength = currentLength;
}
/**
* the setter method of the isComplete
*
* @param isComplete
* the isComplete to set
*/
public void setComplete(boolean isComplete) {
this.isComplete = isComplete;
}
}
}
action代码:
[java] view plaincopyprint?import java.io.IOException;
import com.ufinity.mars.utils.UploadFile;
import com.ufinity.mars.utils.UploadFile.Progress;
import com.ufinity.savor.service.FileService;
/**
* file action
*
* @author scott.Cgi
*/
public class FileAction extends AbstractAction {
/** {field's description} */
private static final long serialVersionUID = 6649027352616232244L;
private FileService fileService;
/**
* 上传文件页面
*
* @return page view
*/
public String preupload() {
return SUCCESS;
}
/**
* 上传文件
*
* @return page view
*/
public String uploadfile() {
try {
UploadFile.upload(this.request, this.response);
} catch (IOException e) {
LOG.error("上传文件发生异常,错误原因 : " + e.getMessage());
}
return null;
}
/**
* 显示上传文件进度进度
*
* @return page view
*/
public String progress() {
String callback1 = this.request.getParameter("callback1");
String callback2 = this.request.getParameter("callback2");
// 缓存progress对象的key值
String key = Integer.toString(request.hashCode());
// 新建当前上传文件的进度信息对象
Progress p = new Progress();
// 缓存progress对象
this.request.getSession().setAttribute(key, p);
response.setContentType("text/html;charset=UTF-8");
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setHeader("expires", "0");
try {
UploadFile.execClientScript(response, callback1 + "(" + key + ")");
long temp = 0l;
while (!p.isComplete()) {
if (temp != p.getCurrentLength()) {
temp = p.getCurrentLength();
// 向客户端显示进度
UploadFile.execClientScript(response, callback2 + "("
+ p.getCurrentLength() + "," + p.getLength() + ")");
} else {
//LOG.info("progress的状态 :" + p.isComplete());
//LOG.info("progress上传的数据量 :+ " + p.getCurrentLength());
//上传进度没有变化时候,不向客户端写数据,写数据过于频繁会让chrome没响应
Thread.sleep(300);
}
}
} catch (Exception e) {
LOG.error("调用客户端脚本错误,原因 :" + e.getMessage());
p.setComplete(true);
}
this.request.getSession().removeAttribute(key);
LOG.info("删除progress对象的session key");
return null;
}
/**
* the getter method of fileService
*
* @return the fileService
*/
public FileService getFileService() {
return fileService;
}
/**
* the setter method of the fileService
*
* @param fileService
* the fileService to set
*/
public void setFileService(FileService fileService) {
this.fileService = fileService;
}
}
import java.io.IOException;
import com.ufinity.mars.utils.UploadFile;
import com.ufinity.mars.utils.UploadFile.Progress;
import com.ufinity.savor.service.FileService;
/**
* file action
*
* @author scott.Cgi
*/
public class FileAction extends AbstractAction {
/** {field's description} */
private static final long serialVersionUID = 6649027352616232244L;
private FileService fileService;
/**
* 上传文件页面
*
* @return page view
*/
public String preupload() {
return SUCCESS;
}
/**
* 上传文件
*
* @return page view
*/
public String uploadfile() {
try {
UploadFile.upload(this.request, this.response);
} catch (IOException e) {
LOG.error("上传文件发生异常,错误原因 : " + e.getMessage());
}
return null;
}
/**
* 显示上传文件进度进度
*
* @return page view
*/
public String progress() {
String callback1 = this.request.getParameter("callback1");
String callback2 = this.request.getParameter("callback2");
// 缓存progress对象的key值
String key = Integer.toString(request.hashCode());
// 新建当前上传文件的进度信息对象
Progress p = new Progress();
// 缓存progress对象
this.request.getSession().setAttribute(key, p);
response.setContentType("text/html;charset=UTF-8");
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setHeader("expires", "0");
try {
UploadFile.execClientScript(response, callback1 + "(" + key + ")");
long temp = 0l;
while (!p.isComplete()) {
if (temp != p.getCurrentLength()) {
temp = p.getCurrentLength();
// 向客户端显示进度
UploadFile.execClientScript(response, callback2 + "("
+ p.getCurrentLength() + "," + p.getLength() + ")");
} else {
//LOG.info("progress的状态 :" + p.isComplete());
//LOG.info("progress上传的数据量 :+ " + p.getCurrentLength());
//上传进度没有变化时候,不向客户端写数据,写数据过于频繁会让chrome没响应
Thread.sleep(300);
}
}
} catch (Exception e) {
LOG.error("调用客户端脚本错误,原因 :" + e.getMessage());
p.setComplete(true);
}
this.request.getSession().removeAttribute(key);
LOG.info("删除progress对象的session key");
return null;
}
/**
* the getter method of fileService
*
* @return the fileService
*/
public FileService getFileService() {
return fileService;
}
/**
* the setter method of the fileService
*
* @param fileService
* the fileService to set
*/
public void setFileService(FileService fileService) {
this.fileService = fileService;
}
}
页面代码:
[xhtml] view plaincopyprint? <mce:style type="text/css"><!--
iframe{
border:none;
width:0;
height:0;
}
#p_out{
width:200px;
height:12px;
margin:10px 0 0 0;
padding:1px;
font-size:10px;
border:solid #6b8e23 1px;
}
#p_in{
width:0%;
height:100%;
background-color:#6b8e23;
margin:0;
padding:0;
}
#dis{
margin:0;
padding:0;
text-align:center;
font-size:12px;
height:12px;
width:200px;
}
--></mce:style><style type="text/css" mce_bogus="1"> iframe{
border:none;
width:0;
height:0;
}
#p_out{
width:200px;
height:12px;
margin:10px 0 0 0;
padding:1px;
font-size:10px;
border:solid #6b8e23 1px;
}
#p_in{
width:0%;
height:100%;
background-color:#6b8e23;
margin:0;
padding:0;
}
#dis{
margin:0;
padding:0;
text-align:center;
font-size:12px;
height:12px;
width:200px;
}
</style>
</head>
<body>
<div class="main">
<div class="top">
<jsp:include page="/top.jsp" />
</div>
<div style="width: 250px; margin: 0 auto;">
<div class="errorbox">
<s:actionerror/>
</div>
<form id="uploadfile_form" name="uploadfile_form" enctype="multipart/form-data"
method="post" target="uploadfile_iframe">
<input type="file" name="file" />
<br><br>
<button onclick="progress()">提交</button>
<div id="p_out"><div id="p_in"></div></div>
<div id="dis"></div>
</form>
<iframe frameborder="0" id="uploadfile_iframe" name="uploadfile_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
<iframe frameborder="0" id="progress_iframe" name="progress_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
</div>
</div>
</body>
<mce:script type="text/javascript"><!--
//上传文件
function uploadFile(key){
document.forms[0].action = 'uploadfile.action?callback=parent.upload&key='+key;
document.forms[0].submit();
document.getElementById('dis').innerHTML = "开始传送数据...";
}
//获取文件上传进度
function progress(){
document.getElementById('progress_iframe').src = 'progress.action?callback1=parent.uploadFile&callback2=parent.upload';
document.getElementById('dis').innerHTML = '初始化数据...';
document.getElementById('p_in').style.width = "0%";
}
//更新进度
function upload(len, total){
document.getElementById('p_in').style.width = (Math.round(len/total*100))+'%';
document.getElementById('dis').innerHTML = len + '/' + total + ' Byte';
if(len === total) {
document.getElementById('dis').innerHTML = "文件上传完成!";
}
}
// --></mce:script>
</html>
<mce:style type="text/css"><!--
iframe{
border:none;
width:0;
height:0;
}
#p_out{
width:200px;
height:12px;
margin:10px 0 0 0;
padding:1px;
font-size:10px;
border:solid #6b8e23 1px;
}
#p_in{
width:0%;
height:100%;
background-color:#6b8e23;
margin:0;
padding:0;
}
#dis{
margin:0;
padding:0;
text-align:center;
font-size:12px;
height:12px;
width:200px;
}
--></mce:style><style type="text/css" mce_bogus="1"> iframe{
border:none;
width:0;
height:0;
}
#p_out{
width:200px;
height:12px;
margin:10px 0 0 0;
padding:1px;
font-size:10px;
border:solid #6b8e23 1px;
}
#p_in{
width:0%;
height:100%;
background-color:#6b8e23;
margin:0;
padding:0;
}
#dis{
margin:0;
padding:0;
text-align:center;
font-size:12px;
height:12px;
width:200px;
}
</style>
</head>
<body>
<div class="main">
<div class="top">
<jsp:include page="/top.jsp" />
</div>
<div style="width: 250px; margin: 0 auto;">
<div class="errorbox">
<s:actionerror/>
</div>
<form id="uploadfile_form" name="uploadfile_form" enctype="multipart/form-data"
method="post" target="uploadfile_iframe">
<input type="file" name="file" />
<br><br>
<button onclick="progress()">提交</button>
<div id="p_out"><div id="p_in"></div></div>
<div id="dis"></div>
</form>
<iframe frameborder="0" id="uploadfile_iframe" name="uploadfile_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
<iframe frameborder="0" id="progress_iframe" name="progress_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
</div>
</div>
</body>
<mce:script type="text/javascript"><!--
//上传文件
function uploadFile(key){
document.forms[0].action = 'uploadfile.action?callback=parent.upload&key='+key;
document.forms[0].submit();
document.getElementById('dis').innerHTML = "开始传送数据...";
}
//获取文件上传进度
function progress(){
document.getElementById('progress_iframe').src = 'progress.action?callback1=parent.uploadFile&callback2=parent.upload';
document.getElementById('dis').innerHTML = '初始化数据...';
document.getElementById('p_in').style.width = "0%";
}
//更新进度
function upload(len, total){
document.getElementById('p_in').style.width = (Math.round(len/total*100))+'%';
document.getElementById('dis').innerHTML = len + '/' + total + ' Byte';
if(len === total) {
document.getElementById('dis').innerHTML = "文件上传完成!";
}
}
// --></mce:script>
</html>
注意: common-upload.jar依赖common-io.jar
相关推荐
pandas whl安装包,对应各个python版本和系统(具体看资源名字),找准自己对应的下载即可! 下载后解压出来是已.whl为后缀的安装包,进入终端,直接pip install pandas-xxx.whl即可,非常方便。 再也不用担心pip联网下载网络超时,各种安装不成功的问题。
基于java的大学生兼职信息系统答辩PPT.pptx
基于java的乐校园二手书交易管理系统答辩PPT.pptx
tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl
Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175
有学生和教师两种角色 登录和注册模块 考场信息模块 考试信息模块 点我收藏 功能 监考安排模块 考场类型模块 系统公告模块 个人中心模块: 1、修改个人信息,可以上传图片 2、我的收藏列表 账号管理模块 服务模块 eclipse或者idea 均可以运行 jdk1.8 apache-maven-3.6 mysql5.7及以上 tomcat 8.0及以上版本
tornado-6.1b2-cp38-cp38-macosx_10_9_x86_64.whl
Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175
matlab
基于java的毕业生就业信息管理系统答辩PPT.pptx
随着高等教育的普及和毕业设计的日益重要,为了方便教师、学生和管理员进行毕业设计的选题和管理,我们开发了这款基于Web的毕业设计选题系统。 该系统主要包括教师管理、院系管理、学生管理等多个模块。在教师管理模块中,管理员可以新增、删除教师信息,并查看教师的详细资料,方便进行教师资源的分配和管理。院系管理模块则允许管理员对各个院系的信息进行管理和维护,确保信息的准确性和完整性。 学生管理模块是系统的核心之一,它提供了学生选题、任务书管理、开题报告管理、开题成绩管理等功能。学生可以在此模块中进行毕业设计的选题,并上传任务书和开题报告,管理员和教师则可以对学生的报告进行审阅和评分。 此外,系统还具备课题分类管理和课题信息管理功能,方便对毕业设计课题进行分类和归档,提高管理效率。在线留言功能则为学生、教师和管理员提供了一个交流互动的平台,可以就毕业设计相关问题进行讨论和解答。 整个系统设计简洁明了,操作便捷,大大提高了毕业设计的选题和管理效率,为高等教育的发展做出了积极贡献。
这个数据集来自世界卫生组织(WHO),包含了2000年至2015年期间193个国家的预期寿命和相关健康因素的数据。它提供了一个全面的视角,用于分析影响全球人口预期寿命的多种因素。数据集涵盖了从婴儿死亡率、GDP、BMI到免疫接种覆盖率等多个维度,为研究者提供了丰富的信息来探索和预测预期寿命。 该数据集的特点在于其跨国家的比较性,使得研究者能够识别出不同国家之间预期寿命的差异,并分析这些差异背后的原因。数据集包含22个特征列和2938行数据,涉及的变量被分为几个大类:免疫相关因素、死亡因素、经济因素和社会因素。这些数据不仅有助于了解全球健康趋势,还可以辅助制定公共卫生政策和社会福利计划。 数据集的处理包括对缺失值的处理、数据类型转换以及去重等步骤,以确保数据的准确性和可靠性。研究者可以使用这个数据集来探索如教育、健康习惯、生活方式等因素如何影响人们的寿命,以及不同国家的经济发展水平如何与预期寿命相关联。此外,数据集还可以用于预测模型的构建,通过回归分析等统计方法来预测预期寿命。 总的来说,这个数据集是研究全球健康和预期寿命变化的宝贵资源,它不仅提供了历史数据,还为未来的研究和政策制
基于微信小程序的高校毕业论文管理系统小程序答辩PPT.pptx
基于java的超市 Pos 收银管理系统答辩PPT.pptx
基于java的网上报名系统答辩PPT.pptx
基于java的网上书城答辩PPT.pptx
婚恋网站 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B
基于java的戒烟网站答辩PPT.pptx
基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx
Capital Bikeshare 数据集是一个包含从2020年5月到2024年8月的自行车共享使用情况的数据集。这个数据集记录了华盛顿特区Capital Bikeshare项目中自行车的租赁模式,包括了骑行的持续时间、开始和结束日期时间、起始和结束站点、使用的自行车编号、用户类型(注册会员或临时用户)等信息。这些数据可以帮助分析和预测自行车共享系统的需求模式,以及了解用户行为和偏好。 数据集的特点包括: 时间范围:覆盖了四年多的时间,提供了长期的数据观察。 细节丰富:包含了每次骑行的详细信息,如日期、时间、天气条件、季节等,有助于深入分析。 用户分类:数据中区分了注册用户和临时用户,可以分析不同用户群体的使用习惯。 天气和季节因素:包含了天气情况和季节信息,可以研究这些因素对骑行需求的影响。 通过分析这个数据集,可以得出关于自行车共享使用模式的多种见解,比如一天中不同时间段的使用高峰、不同天气条件下的使用差异、季节性变化对骑行需求的影响等。这些信息对于城市规划者、交通管理者以及自行车共享服务提供商来说都是非常宝贵的,可以帮助他们优化服务、提高效率和满足用户需求。同时,这个数据集也