文件上传一个经常用到的功能,它有许多中实现的方案。
页面表单 + RFC1897规范 + http协议上传
页面控件(flash/html5/activeX/applet) + RFC1897规范 + http协议上传
页面控件(flash/html5/activeX/applet) + 自定义数据规范 + http协议上传
页面控件(flash/html5/activeX/applet) + FTP协议上传
页面控件(flash/html5/activeX/applet) + 自定义协议
用apache common upload组件实际就是采用的“页面表单 + RFC1897规范 + http协议上传”实现方式,需要实现的技术点:
1. 多文件数据的提交
2. 文件数据包接收存储功能
3. 文件数据上传进度
4. WEB页面无刷新异步提交
时序图:
- 文件上传进度获取时序图
实现思路:
1. 多文件数据的提交
在WEB页面采用多个<input type="file">利用form表单进行文件提交
2. 文件数据包接收存储功能
服务端采用servlet,利用apache common upload组件接收解析数据包,接收解析的过程中保存进度到session, 文件接收完毕后保存到指定目录
3. 文件数据上传进度
在WEB页面在界面写一个定时器,定时访问服务器提供上传进度获取功能的servlet,获取文件上传进度信息
4. WEB页面无刷新异步提交
利用iframe来实现WEB页面无刷新异步上传
关键代码:
UploadFileServlet.java
package com.test.servlet;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.FileCleanerCleanup;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileCleaningTracker;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 文件上传数据接收类
*
* @author chengqi
*
*/
public class UploadFileServlet extends HttpServlet {
/** 日志对象*/
private Log logger = LogFactory.getLog(this.getClass());
private static final long serialVersionUID = 1L;
/** 上传目录名*/
private static final String uploadFolderName = "uploadFiles";
/** 上传临时文件存储目录*/
private static final String tempFolderName = "tempFiles";
/** 上传文件最大为30M*/
private static final Long fileMaxSize = 30000000L;
/** 允许上传的扩展名*/
private static final String [] extensionPermit = {"txt", "xls", "zip"};
/** 统一的编码格式*/
private static final String encode = "UTF-8";
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
logger.info("UploadFileServlet#doPost() start");
try {
String curProjectPath = this.getServletContext().getRealPath("/");
String saveDirectoryPath = curProjectPath + "/" + uploadFolderName;
String tempDirectoryPath = curProjectPath + "/" + tempFolderName;
File saveDirectory = new File(saveDirectoryPath);
File tempDirectory = new File(tempDirectoryPath);
logger.debug("Project real path [" + saveDirectory.getAbsolutePath() + "]");
//上传时产生的临时文件的默认保存目录
logger.debug("Temp files default save path [" + System.getProperty("java.io.tmpdir") + "]");
DiskFileItemFactory factory = new DiskFileItemFactory();
//DiskFileItemFactory中DEFAULT_SIZE_THRESHOLD=10240表示如果上传文件大于10K则会产生上传临时文件
//上传临时文件的默认目录为java.io.tmpdir中保存的路径,根据操作系统的不同会有区别
if(!tempDirectory.exists()) {
tempDirectory.mkdir();
}
//重新设置临时文件保存目录
factory.setRepository(tempDirectory);
//设置文件清除追踪器,文件上传过程中产生的临时文件会在
FileCleaningTracker fileCleaningTracker = FileCleanerCleanup.getFileCleaningTracker(this.getServletContext());
factory.setFileCleaningTracker(fileCleaningTracker);
ServletFileUpload upload = new ServletFileUpload(factory);
//设置文件上传进度监听器
FileProcessListener processListener = new FileProcessListener(request.getSession());
upload.setProgressListener(processListener);
// 设置文件上传的大小限制
upload.setFileSizeMax(fileMaxSize);
// 设置文件上传的头编码,如果需要正确接收中文文件路径或者文件名
// 这里需要设置对应的字符编码,为了通用这里设置为UTF-8
upload.setHeaderEncoding(encode);
//解析请求数据包
List<FileItem> fileItems = upload.parseRequest(request);
//遍历解析完成后的Form数据和上传文件数据
for (Iterator<FileItem> iterator = fileItems.iterator(); iterator.hasNext();) {
FileItem fileItem = iterator.next();
String fieldName = fileItem.getFieldName();
String name = fileItem.getName();
//如果为上传文件数据
if(!fileItem.isFormField()) {
logger.debug("fieldName[" + fieldName + "] fileName[" + name + "] ");
if(fileItem.getSize() > 0) {
String fileExtension = FilenameUtils.getExtension(name);
if(!ArrayUtils.contains(extensionPermit, fileExtension)) {
throw new NoSupportExtensionException("No Support extension.");
}
String fileName = FilenameUtils.getName(name);
FileUtils.copyInputStreamToFile(fileItem.getInputStream(),
new File(saveDirectory, fileName));
}
} else { //Form表单数据
String value = fileItem.getString(encode);
logger.debug("fieldName[" + value + "] fieldValue[" + fieldName + "]");
}
}
responseMessage(response, State.OK);
} catch(FileSizeLimitExceededException e) {
logger.error(e.getMessage(), e);
responseMessage(response, State.OVER_FILE_LIMIT);
} catch(NoSupportExtensionException e) {
logger.error(e.getMessage(), e);
responseMessage(response, State.NO_SUPPORT_EXTENSION);
} catch(Exception e) {
logger.error(e.getMessage(), e);
responseMessage(response, State.ERROR);
} finally {
//清除上传进度信息
request.getSession().removeAttribute("fileUploadProcess");
}
logger.info("UploadFileServlet#doPost() end");
}
public enum State {
OK(200, "上传成功"),
ERROR(500, "上传失败"),
OVER_FILE_LIMIT(501, "超过上传大小限制"),
NO_SUPPORT_EXTENSION(502, "不支持的扩展名");
private int code;
private String message;
private State(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
/**
* 返回结果函数
* @param response
* @param state
*/
private void responseMessage(HttpServletResponse response, State state) {
response.setCharacterEncoding(encode);
response.setContentType("text/html; charset=" + encode);
Writer writer = null;
try {
writer = response.getWriter();
writer.write("<script>");
writer.write("window.parent.fileUploadCallBack({\"code\":" + state.getCode() +",\"message\":\"" + state.getMessage()+ "\"});");
writer.write("</script>");
writer.flush();
writer.close();
} catch(Exception e) {
logger.error(e.getMessage(), e);
} finally {
IOUtils.closeQuietly(writer);
}
}
}
GetFileProcessServlet.java
package com.test.servlet;
import java.io.IOException;
import java.io.Writer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 文件上传进度获取Servlet
*
* @author chengqi
*
*/
public class GetFileProcessServlet extends HttpServlet {
/** 日志对象*/
private Log logger = LogFactory.getLog(this.getClass());
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
logger.info("GetFileProcessServlet#doGet start");
String fileUploadPercent = (String)request.getSession().getAttribute("fileUploadProcess");
Writer writer = null;
try {
writer = response.getWriter();
logger.info("percent:" + fileUploadPercent);
IOUtils.write(fileUploadPercent == null ? "0%" : fileUploadPercent, writer);
writer.flush();
writer.close();
} catch(Exception e) {
logger.error(e.getMessage(), e);
} finally {
IOUtils.closeQuietly(writer);
}
logger.info("GetFileProcessServlet#doGet end");
}
}
FileProcessListener.java
package com.test.servlet;
import java.text.NumberFormat;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 文件进度监听器
*
* @author chengqi
*
*/
public class FileProcessListener implements ProgressListener{
/** 日志对象*/
private Log logger = LogFactory.getLog(this.getClass());
private HttpSession session;
public FileProcessListener(HttpSession session) {
this.session = session;
}
public void update(long pBytesRead, long pContentLength, int pItems) {
double readByte = pBytesRead;
double totalSize = pContentLength;
if(pContentLength == -1) {
logger.debug("item index[" + pItems + "] " + pBytesRead + " bytes have been read.");
} else {
logger.debug("item index[" + pItems + "] " + pBytesRead + " of " + pContentLength + " bytes have been read.");
String p = NumberFormat.getPercentInstance().format(readByte / totalSize);
session.setAttribute("fileUploadProcess", p);
}
}
}
apacheUploadDemo.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Apache common实现基本文件上传</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="js/jquery/jquery-1.9.1.js"></script>
<script type="text/javascript" src="js/jquery/jquery.form.js"></script>
<script type="text/javascript">
//定时器对象
var uploadProcessTimer = null;
$(function (){
//绑定定时器开始操作到提交按钮
$('input[type=submit]').click(function () {
//启动上传进度查询定时器
uploadProcessTimer = window.setInterval(getFileUploadProcess, 20);
})
});
//获取文件上传进度
function getFileUploadProcess() {
$.get('/upload/getFileProcessServlet', function(data) {
$('#fileUploadProcess').html(data);
});
}
//上传完成后,由iframe返回脚本自动调用
function fileUploadCallBack(res) {
//清除定时器
if(uploadProcessTimer) {
window.clearInterval(uploadProcessTimer);
}
var message = res['message'];
var code = res['code'];
if(code != 200) {
$('#fileUploadProcess').html('0%');
}
alert(message);
}
</script>
</head>
<body>
<h2>上传文件1</h2>
用户信息: <br/>
<form id="testForm" action="/upload/uploadServlet" method="post" enctype="multipart/form-data" target="iframeUpload">
姓名:<input name="name" type="text"> <br/>
附件1:<input name="file1" type="file" > <br/>
附件2:<input name="file2" type="file" > <br/>
<br><br>
<input type="submit" value="提交" ><br/>
</form>
上传进度:<label id="fileUploadProcess"></label>
<iframe name="iframeUpload" src="" width="350" height="35" frameborder=0 SCROLLING="no" style="display:NONE"></iframe>
</body>
</html>
总结:
虽然使用apache common upload组件实现了文件上传,但是从上传的效果来看,并不是一个很完美的解决方案。
有如下缺点:
1. 当有多个文件上传时,无法知道单个文件的上传进度,因为文件上传消息中根本就没有关于单个文件大小的信息
文件上传消息
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 22 Apr 2014 07:45:45 GMT
POST /upload/uploadServlet HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:8080/upload/apacheUploadDemo.html
Cookie: JSESSIONID=33498CE814284D67F957CA53D45F0174
Connection: keep-alive
Content-Length 2363
Content-Type multipart/form-data; boundary=---------------------------189163093917262
-----------------------------189163093917262
Content-Disposition: form-data; name="name"
-----------------------------189163093917262
Content-Disposition: form-data; name="file1"; filename="New Text Document.txt" Content-Type: text/plain
文件数据
-----------------------------189163093917262
Content-Disposition: form-data; name="file2"; filename="New Text Document (2).txt" Content-Type: text/plain
文件数据
-----------------------------189163093917262--
2. 浏览器必须将所有文件读取完毕才开始上传,并且是一次性提交所有的数据文件,在互联网环境下,会http连接超时,大文件无法上传成功。
3. 服务端判断是否超过大小限制,是通过计算接收数据的累积字节数和限制大小比较,这种情况下,如果限制大小是30M,那么在服务端已经读取了30M完成后才会抛出异常,多余的消耗的服务器的内存和硬盘空间
所以基于这些原因,页面表单 + RFC1897规范 + http协议上传 + 后台apache common upload组件接收的这种解决方案,不适合解决WEB页面一次多文件上传,大文件上传情况,比较适合一次单个小文件附件的情况,如:博客附件,登记照片上传,预览等情况。
Demo源码见附件
- 大小: 11.7 KB
- 大小: 5.9 KB
分享到:
相关推荐
总结来说,"apache-common-uploadfile"是一个解决Java Servlet环境中文件上传问题的库,它提供了高效且灵活的解决方案,使得开发者能够方便地处理用户上传的各种文件。在Web开发中,掌握Apache Commons FileUpload的...
总之,Apache Commons FileUpload 提供了一种方便的方式来处理 HTTP 文件上传,通过其提供的工具类和接口,我们可以轻松地在 Java Web 应用中实现多文件上传的功能。注意,在生产环境中,还需要考虑安全性问题,如...
`common-fileupload`是Apache Commons项目的一部分,提供了一套完善的Java API,用于处理HTTP协议中的文件上传操作。这个库简化了从用户提交的表单中提取并处理文件数据的过程。 1. **文件上传流程** - 用户在Web...
对比之下,Common File Upload是Apache Jakarta项目的一部分,版本1.2.2是其成熟稳定的一个版本。它提供了对Servlet API的全面支持,允许开发者处理来自HTML表单的文件上传请求。Common File Upload具有广泛的社区...
在IT行业中,文件上传是常见的功能之一,尤其是在Web应用程序中。`Common-fileupload`是Apache组织提供的一个Java库,用于处理HTTP协议中的多部分文件上传请求。这个组件封装了复杂的过程,使得开发者能够轻松地在...
在Java Web开发中,文件上传是一项常见的功能,`Commons-FileUpload` 是Apache Commons项目提供的一个用于处理HTTP请求中的多部分数据(通常为文件上传)的库。本篇文章将详细讲解如何使用`Commons-FileUpload`进行...
`common-fileupload`标签可能指的是Apache Commons FileUpload库,这是一个Java库,专门用于处理基于Servlet的文件上传。它能处理多部分/表单数据,使开发人员能够轻松地处理文件上传请求。使用此库,我们可以通过...
在IT行业中,文件上传是Web应用中常见的功能之一,它允许用户将本地文件发送到服务器进行存储、处理或共享。Apache Commons FileUpload库是Java中处理文件上传的一个强大工具,它使得开发者能够轻松地实现这一功能。...
"Common Fileupload上传组件包"是一个专用于处理Web应用程序中文件上传功能的工具包。这个包包含两个核心组件:`commons-io-1.4.jar`和`commons-fileupload-1.2.1.jar`。这两个组件在实现文件上传过程中扮演着至关...
Common-fileupload组件是Apache的一个开源项目,它简化了文件上传的过程,并提供了灵活的配置选项。本篇文章将详细介绍如何利用Common-fileupload组件在JSP环境中实现文件上传功能。 #### 二、Common-fileupload...
上传后文件都保存在upload目录下面(与WEB-INF同级的upload) 新添加单个文件上传用的是apache的commonfileupload有上传进度条 本工程内所有上传复制过去直接可用. 上传后的文件都保存在根目录下面的upload目录文件夹...
Java 文件上传下载是 Web 应用中非常常见的一种功能,特别是在 JSP 环境中实现文件上传功能非常容易。 Apache 的 Commons FileUpload 组件是实现文件上传功能的一种流行的选择。本文将详细介绍如何使用 Commons ...
JavaWeb 文件上传下载是 Web 应用系统开发中非常常用的功能之一。今天,我们来讲一下 JavaWeb 中的文件上传和下载功能的实现。 文件上传实现 文件上传是将文件从客户端上传到服务器端的过程。在 JavaWeb 中,我们...
在这个过程中,我们将使用Apache的两个库:`common-io`和`common-fileupload`,这两个库为文件上传提供了方便且强大的工具。 首先,让我们了解一下`common-io`和`common-fileupload`。Apache Commons IO是Java的一...
在IT行业中,将文件从浏览器上传到Linux服务器是一项常见的任务,尤其在开发Web应用程序时。本文将详细讲解如何通过HTML创建一个文件上传界面,并利用C语言编写CGI(Common Gateway Interface)程序,让Linux服务器...
亚马逊S3(Simple Storage Service)是亚马逊Web Services(AWS)提供的一个高度可扩展、安全可靠的云存储服务。它允许开发者和企业存储、检索和备份大量数据,并提供了丰富的数据管理功能。在本文中,我们将深入...
在Java Web开发中,文件的上传与下载是常见的功能需求,尤其在构建动态网站或Web应用程序时。本教程将深入讲解如何使用Java Servlet、Apache Commons IO库(版本2.4)以及Apache Commons FileUpload库(版本1.3)来...
默认情况下,Struts2使用JakartaMultiPartRequest,但更推荐使用CommonsMultiPartRequest,因为它基于Apache Commons FileUpload库,对文件上传提供了更好的支持。 为了处理文件上传,你需要在Struts配置文件...