java Servlet与 Ajax显示上传文件进度

网页显示文件上传进度问题的分析:

显示上传进度的前提是获得上传的进度信息。我们可以想到的就是监听器,同时这有可以分两种情况:监听器可以放服务端,也可以放客户端。

下面的方法,其实更多的关键是前端,后端的话,可以用其他语言处理。

下面是对几种方法的总结:

##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>

时间: 2024-09-18 05:48:19

java Servlet与 Ajax显示上传文件进度的相关文章

Java上传文件进度条的实现方法(附demo源码下载)_java

本文实例讲述了Java上传文件进度条的实现方法.分享给大家供大家参考,具体如下: 东西很简单,主要用到commons-fileupload,其中有一个progressListener的接口,该接口可以实现实时更新已上传文件的大小,有了这个还说什么呢? 这里给出代码: package lc.progress; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import lc.

jQuery Ajax方式上传文件的方法_AJAX相关

jQuery Ajax方式上传文件用到两个对象 第一个对象:FormData 第二个对象:XMLHttpRequest 目前新版的Firefox 与 Chrome 等支持HTML5的浏览器完美的支持这两个对象,但IE9尚未支持 FormData 对象,还在用IE6 ? 只能仰天长叹.... 有了这两个对象,我们可以真正的实现Ajax方式上传文件. 示例代码: <!DOCTYPE html> <html> <head> <title>Html5 Ajax 上传

jQuery Ajax方式上传文件的方法

jQuery Ajax方式上传文件用到两个对象 第一个对象:FormData 第二个对象:XMLHttpRequest 目前新版的Firefox 与 Chrome 等支持HTML5的浏览器完美的支持这两个对象,但IE9尚未支持 FormData 对象,还在用IE6 ? 只能仰天长叹.... 有了这两个对象,我们可以真正的实现Ajax方式上传文件. 示例代码: <!DOCTYPE html> <html> <head> <title>Html5 Ajax 上传

Ajax上传文件进度条Codular

现在,人们喜欢在浏览网页时做一些其他事情而不离开该网页,这通常是通过ajax来实现.大多数情况,人们使用jQuery来实现,但是随着浏览器的进步,人们比不需要这么做.这里我们将介绍如何在不离开页面的情况下将文件上传到服务器,我们将使用与我们之前的文章中使用的相同的后端PHP代码. 该脚本将上传文件至服务器,同时显示上传进度,并最终返回上传文件的链接地址.在某些情况下,你可能想要返回上传文件的id或者其他的应用信息. Note: 该代码不支持较老的ie浏览器,通过Can I use我们只支持ie1

JAVA如何利用JSON能上传文件?

问题描述 JAVA如何利用JSON能上传文件? 现在需要通过HTTP的POST请求实现文件发送的功能,其中发送的格式是通过JSON格式的,就是JSON里面携带的有其他参数,还有文件名和文件体,这个怎么实现? 解决方案 java上传文件到oracle数据库 解决方案二: String dataString = ""{name:""filenanme""content:""contentStr""}"

通过Ajax方式上传文件使用FormData进行Ajax请求_AJAX相关

通过传统的form表单提交的方式上传文件: Html代码  <form id= "uploadForm" action= "http://localhost:8080/cfJAX_RS/rest/file/upload" method= "post" enctype ="multipart/form-data"> <h1 >测试通过Rest接口上传文件 </h1> <p >指定文

基于jQuery Ajax实现上传文件_jquery

本文实例为大家分享了基于jQuery Ajax实现上传文件的关键代码,供大家参考,具体内容如下 JS代码: //保存 function btnAdd() { var formData = new FormData($("#frm")[0]); $.ajax({ url: "/Admin/ContentManage/SaveEdit", type: "POST", data: formData, contentType: false, //必须fa

通过Ajax方式上传文件使用FormData进行Ajax请求

通过传统的form表单提交的方式上传文件: Html代码 <form id= "uploadForm" action= "http://localhost:8080/cfJAX_RS/rest/file/upload" method= "post" enctype ="multipart/form-data"> <h1 >测试通过Rest接口上传文件 </h1> <p >指定文件

Ajax异步上传文件实例代码分享_AJAX相关

非常不多说,直接给大家上干货,写的不好还请见谅. 具体代码如下所示: <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <script src="~/Scripts/jquery-1.8.2.min.js"></script> <title>Ind