");
while (iterator.hasNext()){
out.print("
");
FileItem fileItem = iterator.next();
// 判断当前的fileItem是表单还是文件
boolean isFormField = fileItem.isFormField();
if (isFormField){
// fileItem.getFieldName(),打印jsp中form字段,即
// fileItem.getString(),打印的字段的值
out.println("regular form fieldFIELD NAME: " + fileItem.getFieldName() +
"
STRING: " + fileItem.getString()
);
out.println("");
}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("file form fieldFIELD NAME: " + fileItem.getFieldName() +
// "
STRING: " + fileItem.getString() +
"
NAME: " + fileItem.getName() +
"
CONTENT TYPE: " + fileItem.getContentType() +
"
SIZE (BYTES): " + fileItem.getSize() +
"
TO STRING: " + fileItem.toString()
);
out.println("");
}else {
continue;
}
}
out.println("");
}
} 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:
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">
Servlet for file uploads
TestServlet
demo1.TestServlet
ProgressServlet
demo1.ProgressServlet
TestServlet
/demo1test
ProgressServlet
/progress
测试:
在demo1.html页面上传文件,前台页面:
后台就能看到进度:
访问:ProgressServlet.java 就可以看到进度,但是必须手动刷新页面才能看到更新后的进度,这不是我们想要的。
方法二:(见demo2)
客户端上传使用servlet的doPost方法,然后为了从服务器获取上传的进度,使用js来调用Servlet的doGet方法。我们在服务端将监听器存放到session中,这样保证同一个会话都能访问到进度信息。就有了方法二。
截图:
demo2.html:
该页面增加了js,通过js从服务端获取进度,并显示在页面上。
Ajax File Upload
integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0="
crossorigin="anonymous">
$(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);
});
action="/servlet/FileUploadServlet" >
0%
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("\n");
buffy.append("\n");
buffy.append("\t" + bytesRead + "\n");
buffy.append("\t" + contentLength + "\n");
// Check to see if we're done
if (bytesRead == contentLength)
{
buffy.append("\t\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" + percentComplete + "\n");
}
buffy.append("\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:
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">
Servlet for file uploads
File Upload Servlet
>FileUploadServlet
demo2.FileUploadServlet
>FileUploadServlet
/servlet/FileUploadServlet
方法三:(见demo3)
方法一和方法二中,因为进度监听器都是放在服务端的,所以只能从从服务端获取进度信息,这样客户端要发起多次http请求,显然不是理想方案。那么,我们可以试着将监听器放到本地,即XMLHttpRequest对象可以设置两个监听器:
设置上传监听器: xhr.upload.addEventListener("progress", function (evt) {});
设置下载监听器: xhr.addEventListener("progress", function (evt) {});
这样,我们就可以直接在客户端获取到上传进度信息,http请求只需一次即可。
效果:
demo3.html:
使用Jquery Ajax上传文件和显示进度
这个已经能获取到进度,至于进度条的css之类的,可以自己定制了。