网页显示文件上传进度问题的分析:
显示上传进度的前提是获得上传的进度信息。我们可以想到的就是监听器,同时这有可以分两种情况:监听器可以放服务端,也可以放客户端。
下面的方法,其实更多的关键是前端,后端的话,可以用其他语言处理。
下面是对几种方法的总结:
##demo1
这种方式的服务端 使用commons FileUpload库的 ProgressListener 来监控进度,也就说进度情况是保存在服务端的,如果客户端想要知道进度,需要再次发起请求。
如果客户端想在一个页面完成,可以调用js到服务端获得 上传进度信息。
###缺点:
客户端除了要发送post请求来上传文件外,还要多次发送get请求来获取进度信息。
##demo2
demo2其实是demo1的改进版。
和demo1其实差不多,只不过demo2使用了两个servlet来处理,而demo4只是用一个servlet分别调用post和get。
这个实现客户端更新进度的方法的关键是:
这种方式的服务端 使用commons FileUpload库的 ProgressListener 来监控进度,也就说进度情况是保存在服务端的,如果客户端想要知道进度,需要再次发起请求。
将post请求中的上传进度 封装到 get请求中,然后由js发起get请求,来获得xml,解析后显示到html页面
###缺点:
客户端除了要发送post请求来上传文件外,还要多次发送get请求来获取进度信息。
使用Jquery的ajax来上传文件,关键是使用FormData对象用来上传文件
优点:方法简单
缺点: 无
##demo4
使用 ajax来上传文件,关键是使用FormData对象用来上传文件
优点:方法简单
缺点: 无
##demo5
demo5是demo4的一种特殊的情况,就是页面上没有Form元素。同样需要使用FormData对象用来上传文件
使用 ajax来上传文件,关键是使用FormData对象用来上传文件
优点:方法简单
缺点: 无
下面说的几种上传方法的上传页面可以用jsp或html,或者其他语言的。反正最后都是html,这里全用html作为上传页面。
方法一:(见demo1)
(有问题,因为只能后台看到进度,前台只有刷新才能看到进度)
通过使用commons FileUpload 和Commons IO 库实现上传和获取进度。
关键点是调用org.apache.commons.fileupload.ProgressListener来实现监听上传进度。
demo1.html:上传的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload Page</title>
</head>
<body>
<form action="/demo2test" id="form1" method="post" enctype="multipart/form-data">
<input type="file" name="file1">
<br>
<input type="file" name="file2" >
<br>
<input type="file" name="file3">
<br>
<input type="submit" value="submit">
</form>
</body>
</html>
TestServlet.java:
这里是处理主要的文件上传,已经通过注册的ProgressListener来监听进度。
package demo1;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
/**
* Created by AlexY on 2016/6/27.
*/
public class TestServlet extends HttpServlet {
// 保存上传文件的路径
private final String UPLOAD_DIRECTORY = "D:\\files";
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("Hello<br>");
boolean isMultipartContent = ServletFileUpload.isMultipartContent(req);
if (!isMultipartContent){
out.println("You are not trying to upload<br>");
}
out.println("You are trying to upload <br>");
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// upload.setSizeMax(MAX_UPLOAD_IN_MEGS * 1024 * 1024);
// 创建进程监听器
TestProgressListener testProgressListener = new TestProgressListener();
// 关键设置进程监听器
upload.setProgressListener(testProgressListener);
// 将进程监听器保存到session中,方便后面取出来
HttpSession session = req.getSession();
session.setAttribute("testProgressListener",testProgressListener);
//// 如果存放的目录不存在,就创建
// File dir = new File(UPLOAD_DIRECTORY);
// if ( !dir.exists()){
//
// dir.mkdirs();
// }
try {
List<FileItem> items = upload.parseRequest(req);
Iterator<FileItem> iterator = items.iterator();
if (!iterator.hasNext()){
out.println("No fields found");
}
out.print("<table border=\"1\">");
while (iterator.hasNext()){
out.print("<tr>");
FileItem fileItem = iterator.next();
// 判断当前的fileItem是表单还是文件
boolean isFormField = fileItem.isFormField();
if (isFormField){
// fileItem.getFieldName(),打印jsp中form字段,即
// fileItem.getString(),打印的字段的值
out.println("<td>regular form field</td><td>FIELD NAME: " + fileItem.getFieldName() +
"<br/>STRING: " + fileItem.getString()
);
out.println("</td>");
}else {
// 如果html页面上可以上传三个文件,而我只填写了一个文件,这样就会导致获取不到文件名,从而导致new File的时候报错
String fileName = fileItem.getName();
if ( !"".equals(fileName) ){
// 将文件保存到指定的目录
File saveFile = new File(UPLOAD_DIRECTORY,fileName);
fileItem.write(saveFile);
//fileItem.getFieldName() , 获取jsp中表单上传文件时候的字段名。这里就是 “file”。
// fileItem.getString() 打印字节流,只是为了测试。
//fileItem.getName(),获取文件名
//fileItem.getContentType() 获取文件的类型
//fileItem.getSize() ,获取文件的大小
//fileItem.toString() ,将fileItem的所有信息打印出来
out.println("<td>file form field</td><td>FIELD NAME: " + fileItem.getFieldName() +
// "<br/>STRING: " + fileItem.getString() +
"<br/>NAME: " + fileItem.getName() +
"<br/>CONTENT TYPE: " + fileItem.getContentType() +
"<br/>SIZE (BYTES): " + fileItem.getSize() +
"<br/>TO STRING: " + fileItem.toString()
);
out.println("</td>");
}else {
continue;
}
}
out.println("</tr>");
}
} catch (FileUploadException e) {
out.println("Error: " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
TestProgressListener.java:
该类实现了ProgressListener接口的update()方法。
但是为了效率,我们设置为每次读取大于100KB的字节数的时候,才执行一次getMessage()方法。
package jspUpload;
import org.apache.commons.fileupload.ProgressListener;
/**
* Created by AlexY on 2016/6/27.
*/
public class TestProgressListener implements ProgressListener{
// 每次读取100KB的时候,就将读取过的字节数/100 赋值给num100ks
private long num100ks = 0;
// 已经读取的字节
private long theBytesRead = 0;
// 文件大小
private long theContentLength = -1;
private int whichItem = 0;
// 读取文件的百分比
private int percentDone = 0;
public void update(long bytesRead, long contentLength, int items) {
// 判断文件大小是否可知
if ( contentLength > -1){
contentLengthKnown = true;
}
// 已经读取的字节
theBytesRead = bytesRead;
// 文件的大小
theContentLength = contentLength;
// 当前的FileItem对象
whichItem = items;
long nowNum100Ks = bytesRead / 100000;
// 每次读取100k才执行一次getMessage方法
if ( nowNum100Ks > num100ks){
num100ks = nowNum100Ks;
if ( contentLengthKnown){
percentDone = (int)Math.round(100.00 * bytesRead / contentLength);
}
System.out.println(getMessage());
}
}
public String getMessage(){
if (theContentLength == -1){
return ""+ theBytesRead + " of Unknown-Total bytes have been read.";
}else {
return "" + theBytesRead + " of " + theContentLength + " bytes have been read ("+ percentDone + "% done).";
}
}
// 文件大小是否已知
private boolean contentLengthKnown = false;
public boolean isContentLengthKnown() {
return contentLengthKnown;
}
public void setContentLengthKnown(boolean contentLengthKnown) {
this.contentLengthKnown = contentLengthKnown;
}
public long getNum100ks() {
return num100ks;
}
public void setNum100ks(long num100ks) {
this.num100ks = num100ks;
}
public long getTheBytesRead() {
return theBytesRead;
}
public void setTheBytesRead(long theBytesRead) {
this.theBytesRead = theBytesRead;
}
public long getTheContentLength() {
return theContentLength;
}
public void setTheContentLength(long theContentLength) {
this.theContentLength = theContentLength;
}
public int getWhichItem() {
return whichItem;
}
public void setWhichItem(int whichItem) {
this.whichItem = whichItem;
}
public int getPercentDone() {
return percentDone;
}
public void setPercentDone(int percentDone) {
this.percentDone = percentDone;
}
}
ProgressServlet.java:
用于显示上传的进度。
这里就是这种方法不实用的地方,因为只有刷新该ProgressServlet后,才能看到新的进度。这也是我们后面要改进的地方。
package jspUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Created by AlexY on 2016/6/27.
*/
public class ProgressServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
HttpSession session = req.getSession();
if ( null == session ){
// 为了安全
out.print("Sorry, session is null");
return;
}
TestProgressListener listener = (TestProgressListener) session.getAttribute("testProgressListener");
if ( null == listener){
out.print("Progress listener is null");
return;
}
// 输出上传进度百分比
out.print(listener.getMessage());
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--demo1-->
<servlet>
<description>Servlet for file uploads</description>
<servlet-name>TestServlet</servlet-name>
<servlet-class>demo1.TestServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>ProgressServlet</servlet-name>
<servlet-class>demo1.ProgressServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/demo1test</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ProgressServlet</servlet-name>
<url-pattern>/progress</url-pattern>
</servlet-mapping>
<!--demo1 over-->
</web-app>
测试:
在demo1.html页面上传文件,前台页面:
后台就能看到进度:
访问:ProgressServlet.java 就可以看到进度,但是必须手动刷新页面才能看到更新后的进度,这不是我们想要的。
方法二:(见demo2)
客户端上传使用servlet的doPost方法,然后为了从服务器获取上传的进度,使用js来调用Servlet的doGet方法。我们在服务端将监听器存放到session中,这样保证同一个会话都能访问到进度信息。就有了方法二。
截图:
demo2.html:
该页面增加了js,通过js从服务端获取进度,并显示在页面上。
<html>
<head>
<title>Ajax File Upload</title>
<script
src="https://code.jquery.com/jquery-3.0.0.min.js"
integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0="
crossorigin="anonymous">
</script>
<script >
$(document).ready(function () {
var requestXML;
function ajaxFunction() {
var url = "/servlet/FileUploadServlet";
// 判断是否是IE 还是其他浏览器
if (window.XMLHttpRequest) // Non-IE browsers
{
requestXML = new XMLHttpRequest();
// 设置状态函数
requestXML.onreadystatechange = processStateChange;
try {
requestXML.open("GET", url, true);
}
catch (e) {
alert(e);
}
requestXML.send(null);
}
else if (window.ActiveXObject) // IE Browsers
{
requestXML = new ActiveXObject("Microsoft.XMLHTTP");
if (requestXML) {
requestXML.onreadystatechange = processStateChange;
requestXML.open("GET", url, true);
requestXML.send();
}
}
console.log("ajaxfun");
}
function processStateChange( ) {
/**
* State Description
* 0 The request is not initialized
* 1 The request has been set up
* 2 The request has been sent
* 3 The request is in process
* 4 The request is complete 响应已经完成
*/
if (requestXML.readyState == 4) {
if (requestXML.status == 200) // OK response
{
var xml = requestXML.responseXML;
console.log("xml"+xml );
//不需要遍历,因为只会得到含有一个元素的集合。
var isFinished = xml.getElementsByTagName("finished")[0];
var myBytesRead = xml.getElementsByTagName("bytes_read")[0];
var myContentLength = xml.getElementsByTagName("content_length")[0];
var percentCompplete = xml.getElementsByTagName("percent_complete")[0];
var bar = document.getElementById("myBar");
var label = document.getElementById("label");
// 显示pregress
document.getElementById("progress").style.visibility = "visible";
// 判断上传是否完整,如果完整,则将进度条设置为100%
if ((isFinished != null || isFinished != undefined) && (percentCompplete == null || percentCompplete == undefined )) {
console.log("finish:");
bar.style.width = "100%";
label.innerHTML = "100%";
}
else { //上传未完整,则将已经上传的进度显示出来
if (percentCompplete != null)
{
var myPercent = percentCompplete.textContent;
console.log("percent:"+myPercent );
bar.style.width = myPercent + "%";
label.innerHTML = myPercent+ "%";
//这是最关键的地方
//100ms后再次调用ajaxFunction(),这样Form的内容就可以给action中后台页面处理了。
setTimeout( function () {
ajaxFunction();
}, 100);
}
else {
// document.getElementById("bytesRead").style.visibility = "hidden";
// document.getElementById("progressBar").style.width = "100%";
// document.getElementById("percentComplete").innerHTML = "Done!";
}
}
}
else {
alert(requestXML.statusText);
}
}
}
// 为Form设置onsubmit事件
$("#myForm").submit( ajaxFunction);
});
</script>
<style>
#progress p {
display: block;
width: 240px;
padding: 2px 5px;
margin: 2px 0;
border: 1px inset #446;
border-radius: 5px;
}
#progress p.success {
background: #0c0 none 0 0 no-repeat;
}
#progress p.failed {
background: #c00 none 0 0 no-repeat;
}
#progress {
/*打开页面的时候设置progress不可见*/
visibility: collapse;
position: relative;
width: 100%;
height: 30px;
background-color: grey;
}
#myBar {
position: absolute;
width: 1%;
height: 100%;
background-color: green;
}
#label {
text-align: center;
line-height: 30px;
color: white;
}
</style>
</head>
<body>
<iframe id="uploadFrameID" name="uploadFrame" height="0" width="0" frameborder="0" scrolling="yes"></iframe>
<form id="myForm" enctype="multipart/form-data" method="post" target="uploadFrame"
action="/servlet/FileUploadServlet" >
<input type="file" name="txtFile" id="txtFile"/><br/>
<input type="submit" id="submitID" name="submit" value="Upload"/>
</form>
<div id="progress" >
<div id="myBar">
<div id="label">0%</div>
</div>
</div>
FileUploadServlet :
js调用该servlet的doGet方法获取进度,调用doPost方法上传文件。
package demo2;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
/**
* 这个实现客户端更新进度的方法的关键是:
* 将post请求中的上传进度 封装到 get请求中,然后由js发起get请求,来获得xml,解析后显示到html页面
*
*
*/
public class FileUploadServlet extends HttpServlet implements Servlet {
private static final long serialVersionUID = 2740693677625051632L;
public FileUploadServlet() {
super();
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
FileUploadListener listener = null;
StringBuffer buffy = new StringBuffer();
long bytesRead = 0, contentLength = 0;
// Make sure the session has started
if (session == null)
{
System.out.println("session null");
return;
}
else {
System.out.println("session not null");
// Check to see if we've created the listener object yet
listener = (FileUploadListener)session.getAttribute("LISTENER");
// listener = (FileUploadListener) context.getAttribute("LISTENER");
if (listener == null)
{
System.out.println("listener: null");
return;
}
else
{
// Get the meta information
bytesRead = listener.getBytesRead();
contentLength = listener.getContentLength();
}
}
/*
* XML Response Code servlet返回的是xml
*/
response.setContentType("text/xml");
buffy.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
buffy.append("<response>\n");
buffy.append("\t<bytes_read>" + bytesRead + "</bytes_read>\n");
buffy.append("\t<content_length>" + contentLength + "</content_length>\n");
// Check to see if we're done
if (bytesRead == contentLength)
{
buffy.append("\t<finished />\n");
// No reason to keep listener in session since we're done
session.setAttribute("LISTENER", null);
}
else
{
// Calculate the percent complete
long percentComplete = ((100 * bytesRead) / contentLength);
buffy.append("\t<percent_complete>" + percentComplete + "</percent_complete>\n");
}
buffy.append("</response>\n");
System.out.println("xml:"+ buffy.toString());
out.println(buffy.toString());
out.flush();
out.close();
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// create file upload factory and upload servlet
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// set file upload progress listener
FileUploadListener listener = new FileUploadListener();
HttpSession session = request.getSession();
ServletContext context = request.getServletContext();
context.setAttribute("LISTENER", listener);
session.setAttribute("LISTENER", listener);
// upload servlet allows to set upload listener
upload.setProgressListener(listener);
List uploadedItems = null;
FileItem fileItem = null;
// Path to store file on local system
String filePath = "D:\\files";
try {
// iterate over all uploaded files
uploadedItems = upload.parseRequest(request);
Iterator i = uploadedItems.iterator();
while (i.hasNext()) {
fileItem = (FileItem) i.next();
if (fileItem.isFormField() == false) {
if (fileItem.getSize() > 0) {
File uploadedFile = null;
String myFullFileName = fileItem.getName(),
myFileName = "",
// 有的浏览器,如opera会把文件在客户端上的路径一起作为文件名上传
// 所以需要特别处理,分理处真正的文件名,这里要区分下是从unix/linux 还是windows上传的
slashType = (myFullFileName.lastIndexOf("\\") > 0) ? "\\" : "/"; // Windows or UNIX
int startIndex = myFullFileName.lastIndexOf(slashType);
// Ignore the path and get the filename
myFileName = myFullFileName.substring
(startIndex + 1, myFullFileName.length());
// Create new File object
uploadedFile = new File(filePath, myFileName);
// Write the uploaded file to the system
fileItem.write(uploadedFile);
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
FileUploadListener:
进度监听器
package demo2;
import org.apache.commons.fileupload.ProgressListener;
/**
* This is a File Upload Listener that is used by Apache
* Commons File Upload to monitor the progress of the
* uploaded file.
*/
public class FileUploadListener
implements ProgressListener {
private volatile long
bytesRead = 0L,
contentLength = 0L,
item = 0L;
public FileUploadListener() {
super();
}
public void update(long aBytesRead, long aContentLength,
int anItem) {
bytesRead = aBytesRead;
contentLength = aContentLength;
item = anItem;
}
public long getBytesRead() {
return bytesRead;
}
public long getContentLength() {
return contentLength;
}
public long getItem() {
return item;
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--demo2-->
<servlet>
<description>Servlet for file uploads</description>
<display-name>File Upload Servlet</display-name>
<servlet-name>>FileUploadServlet</servlet-name>
<servlet-class>demo2.FileUploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>>FileUploadServlet</servlet-name>
<url-pattern>/servlet/FileUploadServlet</url-pattern>
</servlet-mapping>
<!--demo2 Over-->
</web-app>
方法三:(见demo3)
方法一和方法二中,因为进度监听器都是放在服务端的,所以只能从从服务端获取进度信息,这样客户端要发起多次http请求,显然不是理想方案。那么,我们可以试着将监听器放到本地,即XMLHttpRequest对象可以设置两个监听器:
设置上传监听器: xhr.upload.addEventListener("progress", function (evt) {});
设置下载监听器: xhr.addEventListener("progress", function (evt) {});
这样,我们就可以直接在客户端获取到上传进度信息,http请求只需一次即可。
效果:
demo3.html:
使用Jquery Ajax上传文件和显示进度
这个已经能获取到进度,至于进度条的css之类的,可以自己定制了。
<!DOCTYPE html>
<html>
<head>
<script
src="https://code.jquery.com/jquery-3.0.0.min.js"
integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0="
crossorigin="anonymous">
</script>
<script>
$(document).ready(
function () {
$("#btn").click(function startUpload() {
// 判断是否选择了上传文件
if ($('#fileInput')[0].files.length == 0) {
alert("Please choose a file");
return;
}
var progressBar = document.getElementById("progressBar");
progressBar.value = 0;
var formData = new FormData();
formData.append('file', $('#fileInput')[0].files[0]);
$.ajax({
url: '/demo4Servlet',
type: 'POST',
cache: false,
data: formData,
processData: false,
contentType: false,
xhr: function () {
var xhr = new window.XMLHttpRequest();
// 设置上传的progress监听器
xhr.upload.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
//Do something with upload progress
console.log("evt.loaded:" + evt.loaded);
var percentComplete = (evt.loaded / evt.total) * 100;
progressBar.value = percentComplete;
}
}, false);
// 设置下载时候的progress监听器
xhr.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
console.log(percentComplete);
// 这里可以根据下载到进度,然后做自己向左的操作,比如修改页面的进度条
}
}, false);
return xhr;
},
success: function (result) {
console.log("result" + result.toString());
alert("Sucess! Upload completed");
},
complete: function (xhr, result) {
// 果http code不是200,则弹窗
console.log("status:" + xhr.status);
if (200 != xhr.status) {
alert("Error! Upload failed Http code:" + xhr.status);
}
},
error: function (xhr,status,error) {
alert("Error! Upload failed. Can not connect to server.");
}
})
});
}
);
</script>
</head>
<body>
<input type="file" id="fileInput" name="file1"/>
<button id="btn">Start Uploading</button>
<br/>
<progress id="progressBar" max="100" value="0"/>
</body>
</html>
AjaxUploadServlet :
服务端肯定还是需要一个Servlet来处理上传的,这个不能少。
package demo3;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
/**
* Created by AlexY on 2016/6/27.
*/
public class AjaxUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// create file upload factory and upload servlet
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> uploadedItems = null;
FileItem fileItem = null;
// Path to store file on local system
// 设置保存的目录,目录必须存在,如果不想手动创建,就自己加代码来自动创建,这里省略
String filePath = "D:\\files";
try {
// iterate over all uploaded files
uploadedItems = upload.parseRequest(req);
Iterator i = uploadedItems.iterator();
while (i.hasNext()) {
fileItem = (FileItem) i.next();
if (fileItem.isFormField() == false) {
if (fileItem.getSize() > 0) {
File uploadedFile = null;
String myFullFileName = fileItem.getName(),
myFileName = "",
// 有的浏览器,如opera会把文件在客户端上的路径一起作为文件名上传
// 所以需要特别处理,分理处真正的文件名,这里要区分下是从unix/linux 还是windows上传的
slashType = (myFullFileName.lastIndexOf("\\") > 0) ? "\\" : "/"; // Windows or UNIX
int startIndex = myFullFileName.lastIndexOf(slashType);
// Ignore the path and get the filename
myFileName = myFullFileName.substring
(startIndex + 1, myFullFileName.length());
// Create new File object
uploadedFile = new File(filePath, myFileName);
// Write the uploaded file to the system
fileItem.write(uploadedFile);
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("success");
out.close();
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--demo3-->
<servlet>
<servlet-name>>demo3Servlet</servlet-name>
<servlet-class>demo3.AjaxUploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>>demo3Servlet</servlet-name>
<url-pattern>/demo3Servlet</url-pattern>
</servlet-mapping>
<!--demo3 over-->
</web-app>
方法4和方法5(demo4和demo5)
demo4和demo5其实和demo3没太大区别。
demo4.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo 4</title>
<script
src="https://code.jquery.com/jquery-3.0.0.min.js"
integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0="
crossorigin="anonymous">
</script>
<script>
$(document).ready(function () {
var form = document.getElementById('the-form');
form.onsubmit = function(evt){
var xhr = new XMLHttpRequest();
//XMLHttpRequest是否支持upload
if (xhr.upload){
var file = $("#file")[0].files[0];
console.log("file:"+ file);
var progress = document.getElementById("progress");
// 点击长传后设置prgress为可见
progress.style.visibility = "visible";
var bar = document.getElementById("myBar");
var message = document.getElementById("messages");
message.innerHTML = "upload " + file.name;
var label = document.getElementById("label");
// 创建FormData对象用来上传文件
var formData = new FormData(form);
formData.append('file', file);
// 设置上传监听器
xhr.upload.addEventListener("progress",function (evt) {
var percentCompplete = parseInt( (evt.loaded / evt.total) *100);
console.log("percent:"+ percentCompplete);
bar.style.width = percentCompplete + "%";
label.innerHTML = percentCompplete+ "%"
},false);
xhr.onreadystatechange = function (evt) {
if (xhr.readyState == 4) {
progress.className = (xhr.status == 200 ? "success" : "failure");
}
}
// Add any event handlers here...
xhr.open('POST', "/demo4Servlet", true);
xhr.send(formData);
}
console.log("evt:"+ evt);
return false; // 避免form去提交
};
});
</script>
<style>
#progress p {
display: block;
width: 240px;
padding: 2px 5px;
margin: 2px 0;
border: 1px inset #446;
border-radius: 5px;
}
#progress p.success {
background: #0c0 none 0 0 no-repeat;
}
#progress p.failed {
background: #c00 none 0 0 no-repeat;
}
#progress {
/*打开页面的时候设置progress不可见*/
visibility: collapse;
position: relative;
width: 100%;
height: 30px;
background-color: grey;
}
#myBar {
position: absolute;
width: 1%;
height: 100%;
background-color: green;
}
#label {
text-align: center;
line-height: 30px;
color: white;
}
</style>
</head>
<body>
<!-- The HTML -->
<form id="the-form" action="/demo4Servlet" enctype="multipart/form-data">
<input name="file" id="file" type="file">
<input type="submit" value="Upload" />
</form>
<div id="progress" >
<div id="myBar">
<div id="label">0%</div>
</div>
</div>
<div id="messages">
<p>Status Messages</p>
</div>
</body>
</html>
demo5.html:
demo5是demo4的一种特殊的情况,就是页面上没有Form元素。同样需要使用FormData对象用来上传文件
使用 ajax来上传文件,关键是使用FormData对象用来上传文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo 5</title>
<script
src="https://code.jquery.com/jquery-3.0.0.min.js"
integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0="
crossorigin="anonymous">
</script>
<script>
$(document).ready(function () {
var button = document.getElementById('btn');
button.onclick = function(){
var xhr = new XMLHttpRequest();
//XMLHttpRequest是否支持upload
if (xhr.upload){
var file = $("#file")[0].files[0];
var file = document.getElementById("file").files[0];
console.log("file:"+ file);
// create progress bar
var progress = document.getElementById("progress");
// 点击长传后设置prgress为可见
progress.style.visibility = "visible";
var bar = document.getElementById("myBar");
var message = document.getElementById("messages");
message.innerHTML = "upload " + file.name;
var label = document.getElementById("label");
// 创建FormData对象用来上传文件,这里与demo4稍微有点不同
var formData = new FormData( );
formData.append('file', file);
// 设置上传监听器
xhr.upload.addEventListener("progress",function (evt) {
var percentCompplete = parseInt( (evt.loaded / evt.total) *100);
console.log("percent:"+ percentCompplete);
bar.style.width = percentCompplete + "%";
label.innerHTML = percentCompplete+ "%"
},false);
xhr.onreadystatechange = function (evt) {
if (xhr.readyState == 4) {
progress.className = (xhr.status == 200 ? "success" : "failure");
}
}
// Add any event handlers here...
xhr.open('POST', "/demo4Servlet", true);
xhr.send(formData);
}
return false; // To avoid actual submission of the form
};
});
</script>
<style>
#progress p {
display: block;
width: 240px;
padding: 2px 5px;
margin: 2px 0;
border: 1px inset #446;
border-radius: 5px;
}
#progress p.success {
background: #0c0 none 0 0 no-repeat;
}
#progress p.failed {
background: #c00 none 0 0 no-repeat;
}
#progress {
/*打开页面的时候设置progress不可见*/
visibility: collapse;
position: relative;
width: 100%;
height: 30px;
background-color: grey;
}
#myBar {
position: absolute;
width: 1%;
height: 100%;
background-color: green;
}
#label {
text-align: center;
line-height: 30px;
color: white;
}
</style>
</head>
<body>
<input name="file" id="file" type="file">
<button id="btn" >start Upload</button>
<div id="progress" >
<div id="myBar">
<div id="label">0%</div>
</div>
</div>
<div id="messages">
<p>Status Messages</p>
</div>
</body>
</html>