zoukankan      html  css  js  c++  java
  • JQ+asp.net实现文件上传的断点续传功能

    一、功能原理

    断点续传,顾名思义就是将文件分割成一段段的过程,然后一段一段的传。

    以前文件无法分割,但随着HTML5新特性的引入,类似普通字符串、数组的分割,我们可以可以使用slice方法来分割文件。

    所以断点续传的最基本实现也就是:前端通过FileList对象获取到相应的文件,按照指定的分割方式将大文件分段,然后一段一段地传给后端,后端再按顺序一段段将文件进行拼接。

    同时,将传送的进度记录记录到浏览器的缓存中。每次传送数据都更新浏览器的缓存

    二、实现过程

      1、前端代码

               <table id="tbResult" class="table table-normal">
                    <thead>
                        <tr>
                            <td>序号</td>
                            <td>地图名称</td>
                            <td>地图版本</td>
                            <td>地图类型</td>
                            <td>类型名称</td>
                            <td>创建时间</td>
                            <td style="250px;">操作</td>
                        </tr>
                    </thead>
                    <tbody>
                        <tr index="0">
                        </tr>
                    </tbody>
                </table>

      2、计算文件的大小

     // 计算文件大小
           var size = file.size > 1024
                    file.size / 1024 > 1024
                    ? file.size / (1024 * 1024) > 1024
                    ? (file.size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
                    : (file.size / (1024 * 1024)).toFixed(2) + 'MB'
                    : (file.size / 1024).toFixed(2) + 'KB'
                    : (file.size).toFixed(2) + 'B';
    

      3、选择文件后显示文件的信息,在模版中替换一下数据

                var fileList = "";
                var uploadVal = '开始上传';
                var files = document.getElementById('myFile').files;
                fileCount = files.length;
                if (files) {
                    for (var i = 0, j = files.length; i < j; ++i) {
                        var file = this.files[i];
                        // 计算文件大小
                        var size = file.size > 1024
                            ? file.size / 1024 > 1024
                                ? file.size / (1024 * 1024) > 1024
                                    ? (file.size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
                                    : (file.size / (1024 * 1024)).toFixed(2) + 'MB'
                                : (file.size / 1024).toFixed(2) + 'KB'
                            : (file.size).toFixed(2) + 'B';
                        // 初始通过本地记录,判断该文件是否曾经上传过
                        var percent = window.localStorage.getItem(file.name + '_p');
                        if (percent && percent !== '100.0') {
                            uploadVal = "继续上传"
                        }
                        fileList += "<tr><td>" + file.name + "</td><td>" + file.type + "</td><td>" + size + "</td><td class='upload-progress'>" + percent + "</td><td><div class='upload-item-btn' data-name='" + file.name + "' data-size='" + file.size + "' data-state='default' style='display:none'>" + uploadVal + "</div></td></tr>";
                    }
                }
                $("#fileList").append(fileList);
    

      4、不过,在显示文件信息的时候,可能这个文件之前之前已经上传过了,为了断点续传,需要判断并在界面上做出提示通过查询本地看是否有相应的数据(这里的做法是当本地记录的是已经上传100%时,就直接是重新上传而不是继续上传了) 

                // 初始通过本地记录,判断该文件是否曾经上传过
                        var percent = window.localStorage.getItem(file.name + '_p');
                        if (percent && percent !== '100.0') {
                            uploadVal = "继续上传"
                        }

      5、显示文件信息列表

       

          6、点击开始上传,可以上传相应的文件

              var $this = $(this);
                    var fileName = $this.attr('data-name');
                    var totalSize = $this.attr('data-size');
                    var eachSize = 1024 * 1024;
                    var chunks = Math.ceil(totalSize / eachSize);
                    var $progress = $this.closest('tr').find('.upload-progress')

      7、接下来是分段过程

                // 上传之前查询是否以及上传过分片
                        var chunk = window.localStorage.getItem(fileName + '_chunk') || 0;
                        chunk = parseInt(chunk, 10);
                        // 判断是否为末分片
                        var isLastChunk = (chunk == (chunks - 1) ? 1 : 0);
                        // 如果第一次上传就为末分片,即文件已经上传完成,则重新覆盖上传
                        if (times === 'first' && isLastChunk === 1 && totalSize > eachSize) {
                            window.localStorage.setItem(fileName + '_chunk', 0);
                            chunk = 0;
                            isLastChunk = 0;
                        }
                        // 设置分片的开始结尾
                        var blobFrom = chunk * eachSize, // 分段开始
                            blobTo = (chunk + 1) * eachSize > totalSize ? totalSize : (chunk + 1) * eachSize, // 分段结尾
                            percent = (100 * blobTo / totalSize).toFixed(1), // 已上传的百分比
                            timeout = 5000, // 超时时间
                            fd = new FormData($('#myForm')[0]);
    
    
                        fd.append('json', JSON.stringify(record)); // 文件名
                        fd.append('theFile', findTheFile(fileName).slice(blobFrom, blobTo)); // 分好段的文件(实际上传递的就是这个文件)
                        fd.append('fileName', fileName); // 文件名
                        //fd.append('totalSize', totalSize); // 文件总大小
                        fd.append('isLastChunk', isLastChunk); // 是否为末段
                        //fd.append('isFirstUpload', times === 'first' ? 1 : 0); // 是否是第一段(第一次上传)
                        fd.append('chunks', chunks); // 总片段
                        fd.append('chunk', chunk); // 当前片段

      8、AJAX上传  

    $.ajax({
                            url: serviceBaseUrl + URL.ADD_MapManage,
                            type: 'POST',
                            data: fd,
                            async: true,  
                            //cache: false,  
                            contentType: false,
                            processData: false
                        }).then(function (res) {
                            // 已经上传完毕
                            window.localStorage.setItem(fileName + '_p', percent);
                            if (chunk === (chunks - 1)) {
    
                                $progress.text('上传完毕');
                                if (!$('#upload-list').find('.upload-item-btn:not(:disabled)').length) {
                                    $('#upload-all-btn').val('已经上传').prop('disabled', true).css('cursor', 'not-allowed');
                                }
                                uploadCount++;
                                if (uploadCount == fileCount) {
                                    swal({
                                        title: "操作成功!",
                                        type: "success",
                                        text: "2秒后自动关闭。",
                                        timer: 2000,
                                        showConfirmButton: true
                                    });
                                    closeEdit();
                                }
                            } else {
                                // 记录已经上传的分片
                                window.localStorage.setItem(fileName + '_chunk', ++chunk);
                                $progress.text(percent + '%');
                                startUpload();
                            }
                        })

      9、完成的js逻辑如下

      

        //附件选择
            $('body').on('change', '#myFile', function (e) {
                var fileList = "";
                var uploadVal = '开始上传';
                var files = document.getElementById('myFile').files;
                fileCount = files.length;
                if (files) {
                    for (var i = 0, j = files.length; i < j; ++i) {
                        var file = this.files[i];
                        // 计算文件大小
                        var size = file.size > 1024
                            ? file.size / 1024 > 1024
                                ? file.size / (1024 * 1024) > 1024
                                    ? (file.size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
                                    : (file.size / (1024 * 1024)).toFixed(2) + 'MB'
                                : (file.size / 1024).toFixed(2) + 'KB'
                            : (file.size).toFixed(2) + 'B';
                        // 初始通过本地记录,判断该文件是否曾经上传过
                        var percent = window.localStorage.getItem(file.name + '_p');
                        if (percent && percent !== '100.0') {
                            uploadVal = "继续上传"
                        }
                        fileList += "<tr><td>" + file.name + "</td><td>" + file.type + "</td><td>" + size + "</td><td class='upload-progress'>" + percent + "</td><td><div class='upload-item-btn' data-name='" + file.name + "' data-size='" + file.size + "' data-state='default' style='display:none'>" + uploadVal + "</div></td></tr>";
                    }
                }
                $("#fileList").append(fileList);
            })

         //附件全部上传
            $('body').on('click', '#btnSaveAll', function (e) {
                // 未选择文件
                if (!$('#myFile').val()) {
                    //$('#myFile').focus();
                    var fd = new FormData($('#myForm')[0]);
                    fd.append('json', JSON.stringify(record)); // 文件名
                    $.ajax({
                        url: serviceBaseUrl + URL.ADD_MapManage,
                        type: 'POST',
                        data: fd,
                        //async: false,  
                        //cache: false,  
                        contentType: false,
                        processData: false
                    }).then(function (res) {
                        swal({
                            title: "操作成功!",
                            type: "success",
                            text: "2秒后自动关闭。",
                            timer: 2000,
                            showConfirmButton: true
                        });
                        closeEdit();
                    })
                }
                // 模拟点击其他可上传的文件
                else {
                    $('#upload-list .upload-item-btn').each(function () {
                        $(this).click();
                        uploadCount = 0;
                    });
                }
            })
        
        
         //文件单个上传功能
            $('body').on('click', '.upload-item-btn', function () {
                if (inputValidator !== undefined) {
                    inputValidator.checkValidity();
                }
                if ($(".validContainer input.invalid").length == 0) {
                    var $this = $(this);
                    var fileName = $this.attr('data-name');
                    var totalSize = $this.attr('data-size');//文件的总大小
                    var eachSize = 1024 * 1024;//每次上传1M的数据
                    var chunks = Math.ceil(totalSize / eachSize);//一共多少片段
                    var $progress = $this.closest('tr').find('.upload-progress')

                    //var fileCount=document.getElementById('myFile').files;
                    // 第一次点击上传
                    startUpload('first');
                    // 上传操作 times: 第几次
                    function startUpload(times) {
                        // 上传之前查询是否以及上传过分片
                        var chunk = window.localStorage.getItem(fileName + '_chunk') || 0;
                        chunk = parseInt(chunk, 10);
                        // 判断是否为末分片
                        var isLastChunk = (chunk == (chunks - 1) ? 1 : 0);
                        // 如果第一次上传就为末分片,即文件已经上传完成,则重新覆盖上传
                        if (times === 'first' && isLastChunk === 1 && totalSize > eachSize) {
                            window.localStorage.setItem(fileName + '_chunk', 0);
                            chunk = 0;
                            isLastChunk = 0;
                        }
                        // 设置分片的开始结尾
                        var blobFrom = chunk * eachSize, // 分段开始
                            blobTo = (chunk + 1) * eachSize > totalSize ? totalSize : (chunk + 1) * eachSize, // 分段结尾
                            percent = (100 * blobTo / totalSize).toFixed(1), // 已上传的百分比
                            timeout = 5000, // 超时时间
                            fd = new FormData($('#myForm')[0]);


                        fd.append('json', JSON.stringify(record)); // 文件名
                        fd.append('theFile', findTheFile(fileName).slice(blobFrom, blobTo)); // 分好段的文件(实际上传递的就是这个文件)
                        fd.append('fileName', fileName); // 文件名
                        //fd.append('totalSize', totalSize); // 文件总大小
                        fd.append('isLastChunk', isLastChunk); // 是否为末段
                        //fd.append('isFirstUpload', times === 'first' ? 1 : 0); // 是否是第一段(第一次上传)
                        fd.append('chunks', chunks); // 总片段
                        fd.append('chunk', chunk); // 当前片段
                        //$progress.text(percent + '%');
                        $.ajax({
                            url: serviceBaseUrl + URL.ADD_MapManage,
                            type: 'POST',
                            data: fd,
                            async: true,  
                            //cache: false,  
                            contentType: false,
                            processData: false
                        }).then(function (res) {
                            // 已经上传完毕
                            window.localStorage.setItem(fileName + '_p', percent);
                            if (chunk === (chunks - 1)) {

                                $progress.text('上传完毕');
                                if (!$('#upload-list').find('.upload-item-btn:not(:disabled)').length) {
                                    $('#upload-all-btn').val('已经上传').prop('disabled', true).css('cursor', 'not-allowed');
                                }
                                uploadCount++;
                                if (uploadCount == fileCount) {
                                    swal({
                                        title: "操作成功!",
                                        type: "success",
                                        text: "2秒后自动关闭。",
                                        timer: 2000,
                                        showConfirmButton: true
                                    });
                                    closeEdit();
                                }
                            } else {
                                // 记录已经上传的分片
                                window.localStorage.setItem(fileName + '_chunk', ++chunk);
                                $progress.text(percent + '%');
                                startUpload();
                            }
                        })
                    }
                }
            })
     

    三、后端实现

      

          try
                {
                    HttpPostedFile file = fileCollection[0];
                    //二进制数组
                    byte[] fileBytes = null;
                    fileBytes = new byte[file.ContentLength];
                    //创建Stream对象,并指向上传文件
                    Stream fileStream = file.InputStream;
                    //从当前流中读取字节,读入字节数组中
                    fileStream.Read(fileBytes, 0, file.ContentLength);
                    //全路径(路劲+文件名)
                    string timePath = DateTime.Now.ToString("yyyy") + DateTime.Now.ToString("MM");
                    string fullPath = path + "Files\Drones\" + timePath + "\" + fileName + "_" + chunk;
                    //保存到磁盘
                    var fullAllPath = Path.GetDirectoryName(fullPath);
                    //如果没有此文件夹,则新建
                    if (!Directory.Exists(fullAllPath))
                    {
                        Directory.CreateDirectory(fullAllPath);
                    }
                    //创建文件,返回一个 FileStream,它提供对 path 中指定的文件的读/写访问。
                    using (FileStream stream = File.Create(fullPath))
                    {
                        //将字节数组写入流
                        stream.Write(fileBytes, 0, fileBytes.Length);
                        stream.Close();
                    }
                    //最后的片段需要将文件进行合并
                    if (isLastChunk == "1")
                    {
                        List<string> list = new List<string>();
                        for (int i = 0; i < Convert.ToInt32(chunks); i++)
                        {
                            //获取所有片段文件的位置
                            list.Add(path + "Files\Drones\" + timePath + "\" + fileName + "_" + i);
                        }
                        int coutSize = 0;
                        //文件合并
                        using (FileStream fileNew = new FileStream(path + "Files\Drones\" + timePath + "\" + fileName, FileMode.Create, FileAccess.Write))
                        {
                            int count = -1;
    
                            for (int i = 0; i < list.Count; i++)
                            {
                                using (FileStream readStream = new FileStream(list[i], FileMode.Open, FileAccess.Read))
                                {
                                    byte[] buffer = new byte[readStream.Length];
                                    coutSize += buffer.Length;
                                    while ((count = readStream.Read(buffer, 0, buffer.Length)) > 0)
                                    {
                                        fileNew.Write(buffer, 0, count);
                                    }
                                }
                            }
                        }
                        //每次航线新增完毕都需要做查询是否有相同ID的数据,如果有就不做添加
                        var entity = JsonConvert.DeserializeObject<T_DRO_MAPMANAGEEntity>(josn);
                        int retCount = mDAL.GetCount($" ID='{entity.ID}'").Result;
                        if (retCount == 0)
                        {
                            //entity.ID = Guid.NewGuid().ToString();
                            entity.CHUANGJIAN_SJ = DateTime.Now;
                            entity.STA = "A";
                            var result = mDAL.AddData(entity, addId: true);
                        }
                        else
                        {
                            entity.STA = "U";
                            var result = mDAL.UpdateData(entity);
                        }
                        //添加附件实体
                        T_DRO_MAPFILESEntity mapfile = new T_DRO_MAPFILESEntity();
                        mapfile.ID = Guid.NewGuid().ToString();
                        mapfile.FILEPATH = "Files\Drones\" + timePath + "\" + fileName;
                        mapfile.FILESIZE = coutSize.ToString();
                        mapfile.CREATETIME = DateTime.Now;
                        mapfile.PK_MAP_ID = entity.ID;
                        mapfile.SUFFIX = System.IO.Path.GetExtension(fileName);
                        mapfile.FILENAME = fileName;
                        //mapfile.State = "已完成";
                        var ret = fDAL.AddData(mapfile, addId: true);
    
    
                        //删除片段文件
                        for (int i = 0; i < list.Count; i++)
                        {
                            //获取所有片段文件的位置
                            if (File.Exists(list[i]))
                            {
                                File.Delete(list[i]);
                            }
                        }
    
                        //fDAL
                        return new SerializeJson<int>(Enum.ResultType.succeed, entity.ID, 1).ToString();
                    }
                    return "";
                }
                catch (Exception ex)
                {
                    return new SerializeJson<int>(Enum.ResultType.failed, ex.Message, -1).ToString();
                }

    四、最后的结果如图所

      

  • 相关阅读:
    Python——PYQT:控件基本使用
    Python——GUI编程 利息计算器 作业9(python programming)
    Python——GUI编程(python programming)
    weblogic 8.1教程之部署(三)
    iOS CocoaPods安装和使用图解
    黑马day11 事务的四大特性
    实现按条件查询
    hdu 1078 FatMouse and Cheese【dp】
    Android-风格和主题
    Oracle PGA
  • 原文地址:https://www.cnblogs.com/dushaojun/p/14922760.html
Copyright © 2011-2022 走看看