JavaScript File API应用——如何设计和实现Ajax文件上传组件

本文详细介绍了如何使用JavaScript和jQuery构建一个带有进度条的文件上传组件,并逐步实现暂停、重启功能以及并行异步上传。通过XMLHttpRequest的progress事件监控文件上传进度,借助Blob对象的slice方法实现文件分块上传,同时讨论了服务器端(Struts2)的处理逻辑。最后提出进一步完善组件的需求,如文件完整性校验、取消上传和拖拽上传等。

(1)精简“带进度条文件上传组件”的设计与实现

这里写图片描述

XMLHttpRequest第二版为我们提供了便利的progress事件,通过为xhr.upload.onprogress指定处理函数,可以快速制作进度条,JQuery插件参考代码如下:

(function($) {
   
   
    $.fn.uploader = function(userOptions) {
   
   
        var options = {
            id : "uploader",
            url : "uploadAction.action?fileName=",//处理上传文件的服务器url
            width : 540,//整个控件div层的宽度
            height : 180
        //整个控件div层的高度
        };
        $.extend(options, userOptions);

        var createUploader = function(where) {
   
   
            var uploader = $("<div id='" + options.id + "'></div>").css({
                "display" : "block",
                "position" : "relative",
                "width" : options.width,
                "height" : options.height,
                "overflow" : "hidden"
            });

            //创建上传文件控件
            var fileUI = $("<input id='file' type='file'>").addClass("");
            uploader.append(fileUI);

            //创建上传文件按钮,并实现点击事件
            var uploadBtn = $("<input id='uploadBtn' type='button' value='上传'>")
                    .click(
                            function() {
   
   
                                var fileUI = document.getElementById("file");
                                if (fileUI.files.length > 0) {
                                    var file = fileUI.files[0];//取出文件blob

                                    var xhr = new XMLHttpRequest();
                                    //指定处理上传进度的函数
                                    xhr.upload.onprogress = updateProgress;
                                    //处理上传
                                    xhr.open("POST", options.url
                                            + encodeURIComponent(file.name),
                                            true);
                                    xhr.overrideMimeType("application/octet-stream");
                                    xhr.send(file);
                                    //处理响应
                                    xhr.onreadystatechange = function() {
   
   
                                        if (xhr.readyState == 4
                                                && xhr.status == 200) {
                                            console.log(xhr.responseText);
                                        } else {
                                            console.log(xhr.statusText);
                                        }
                                    };
                                    console.log("upload complete");
                                } else {
                                    alert("请选择上传文件!");
                                }

                            });
            uploader.append(uploadBtn);

            var progressBar = $("<div></div>").addClass("progressBar");
            var progress = $("<p></p>");
            progressBar.append(progress);
            uploader.append(progressBar);

            var updateProgress = function(event) {
   
   
                if (event.lengthComputable) {
                    var percentage = Math.round((event.loaded * 100)
                            / event.total);
                    console.log("percentage:" + percentage);
                    if (percentage < 100) {
                        progress.css("width", (percentage * 2) + "px");
                        progress.text(percentage + "%");
                    } else {
                        progress.css("width", "200px");
                        progress.text("100%");
                    }
                }
            };
            where.append(uploader);
        }
        createUploader(this);
    }
}(jQuery));

服务器端可以利用struts2对上传请求进行处理,将其保存到文件系统,参考代码如下:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class FileUploadAction extends ActionSupport {
   
   

    private static String SEPARATOR = File.separator;

    public InputStream inputStream = null;

    public InputStream getIn() {
        return inputStream;
    }

    @Override
    public String execute() {

        BufferedInputStream fileIn = null;
        BufferedOutputStream fileOut = null;
        String fileName = ServletActionContext.getRequest().getParameter("fileName");
        String path = ServletActionContext.getRequest().getSession().getServletContext().getRealPath("/");

        try{
            fileIn = new BufferedInputStream(
                    ServletActionContext.getRequest().getInputStream());
            File file = new File(path + SEPARATOR + fileName);

            byte[] buf = new byte[1024];
            fileOut = new BufferedOutputStream(
                    new FileOutputStream(file, true));
            int length = 0;
            while ((length = fileIn.read(buf)) != -1) {
                fileOut.write(buf, 0, length);
            }

        } catch(IOException e){

            e.printStackTrace();
            return ERROR;

        } finally {
            if(fileIn != null){
                try {
                    fileIn.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(fileOut != null){
                try {
                    fileOut.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        try {
            inputStream = new ByteArrayInputStream("upload finished".getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return SUCCESS;
    }
}

Struts2配置参考:

<package name="myjson" namespace="" extends="json-default">
    <action name="uploadAction" class="FileUploadAction">
        <result type="stream">
            <param name="contentType">
                text/html
            </param>
            <param name="inputName">
                inputStream
            </param>
        </result>
    </action>
</package>

进度条可以简单的利用div进行实现,参考样式如下:

.progressBar {
    width: 200px;
    height: 20px;
    border: 1px solid #000;
    -moz-border-radius: 8px;
    margin-bottom: 10px;
}

.progressBar p {
    margin: 0px;
    width: 0px;
    height: 20px;
    -moz-border-radius: 8px;
    background-color: #00FF00;
}

使用插件实现文件上传功能:
这里写图片描述

(2)改进一下,如何实现暂停和重启

这里写图片描述
暂停和重启功能,前端需要依赖Blob对象(事实上一个File原型链的上一层就是Blob)的分割(slice)方法,典型的分割函数如下:

var sliceBlob = function(blob, start, end, type) {
   
   
             var force_saveable_type = 'application/octet-stream';
             if (type && type == force_saveable_type) {
                 var slice = blob.slice || blob.webkitSlice || blob.mozSlice;
                 blob = slice.call(blob, start, end, type);
             }
             return blob;
         }

通过分割函数我们就可以完成对文件进行分块上传并且控制是否暂停和进度条的显示逻辑,JQuery插件参考代码如下:

(function($){
   
   
     $.fn.uploader = function(userOptions){
   
   
         var options = {
             id : "uploader",
             url: "uploadAction.action?fileName=",
             width : 540,//整个控件div层的宽度
             height : 180//整个控件div层的高度
         };
         $.extend(options, userOptions);

         var sliceBlob = function(blob, start, end, type) {
   
   
             var force_saveable_type = 'application/octet-stream';
             if (type && type == force_saveable_type) {
                 var slice = blob.slice || blob.webkitSlice || blob.mozSlice;
                 blob = slice.call(blob, start, end, type);
             }
             return blob;
         }

         va
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值