• 使用HTML5 FormData对象实现大文件分块上传(断点上传)功能


      FormData是HTML5新增的一个对象,通过FormData对象可以组装一组用 XMLHttpRequest发送请求的键/值对。它可以更灵活方便的发送表单数据,因为可以独立于表单使用。如果你把表单的编码类型设置为multipart/form-data ,则通过FormData传输的数据格式和表单通过submit() 方法传输的数据格式相同。具体用法参考 FormData对象的使用

      实现逻辑:客户端首先请求接口,获取一个唯一的UploadID,然后每次按照固定大小读取文件块,同时计算需要上传的总块数total,将UploadID、total、当前上传文件块的下标index、文件名称以及文件块上传到服务端,服务端根据以上参数把文件块保存到临时目录,同时判断文件块是否已经全部上传完,如果已全部上传,则需要进行合并操作,最后会返回合并后的文件信息。我的例子中,把获取UploadID的步骤跟上传放在了一起,上传接口中会判断如果UploadID为空并且index为1,则会生成一个UploadID并返回,后续每次上传都需要带上UploadID参数。

      一下前端代码

      前端代码

     1 @{
     2     Layout = null;
     3 }
     4 
     5 <!DOCTYPE html>
     6 
     7 <html>
     8 <head>
     9     <meta name="viewport" content="width=device-width" />
    10     <title>断点上传</title>
    11     <script src="@Url.Content("~/Scripts/jquery-3.1.1.min.js")"></script>
    12 </head>
    13 <body>
    14     <input id="myFile" type="file">
    15     <button onclick="Start()">上传</button>
    16     <button onclick="Pause()">暂停</button>
    17     <button onclick="Continue()">继续</button>
    18     <label>当前进度:<span id="progress"></span></label>
    19     <script>
    20         var uploadId = '';
    21         var index = 1;
    22         var pause = false; //暂停
    23 
    24         function Start() {
    25             index = 1;
    26             uploadId = '';
    27             Upload();
    28         }
    29 
    30         function Upload() {
    31             var files = document.getElementById('myFile').files;
    32             if (files.length < 1) {
    33                 alert('请选择文件~');
    34                 return;
    35             }
    36             var file = files[0];
    37             var totalSize = file.size;//文件大小
    38             var blockSize = 1024 * 1024 * 2;//块大小
    39             var blockCount = Math.ceil(totalSize / blockSize);//总块数
    40 
    41             //创建FormData对象
    42             var formData = new FormData();
    43             formData.append('fileName', file.name);//文件名
    44             formData.append('total', blockCount);//总块数
    45             formData.append('index', index);//当前上传的块下标
    46             formData.append('uploadId', uploadId);//上传编号
    47             formData.append('data', null);
    48 
    49             UploadPost(file, formData, totalSize, blockCount, blockSize);
    50         }
    51 
    52         function UploadPost(file, formData, totalSize, blockCount, blockSize) {
    53             if (pause) {
    54                 return; //暂停
    55             }
    56             try {
    57                 var start = index * blockSize;
    58                 var end = Math.min(totalSize, start + blockSize);
    59                 var block = file.slice(start, end);
    60                 formData.set('data', block);
    61                 formData.set('index', index);
    62                 formData.set('uploadId', uploadId);
    63 
    64                 $.ajax({
    65                     url: '',
    66                     type: 'post',
    67                     data: formData,
    68                     processData: false,
    69                     contentType: false,
    70                     success: function (res) {
    71                         block = null;
    72                         if (res.Code === 1) {
    73                             if (index === 1)
    74                                 uploadId = res.UploadID;
    75 
    76                             $('#progress').text((index / blockCount * 100).toFixed(2) + '%');
    77                             if (index < blockCount) {
    78                                 index++;
    79                                 UploadPost(file, formData, totalSize, blockCount, blockSize);
    80                             }
    81                         }
    82                     }
    83                 });
    84             } catch (e) {
    85                 alert(e);
    86             }
    87         }
    88         ///暂停
    89         function Pause() {
    90             pause = true;
    91         }
    92         //继续
    93         function Continue() {
    94             pause = false;
    95             Upload();
    96         }
    97     </script>
    98 </body>
    99 </html>
    前端代码

      服务端代码

      1 using System;
      2 using System.IO;
      3 using System.Linq;
      4 using System.Web;
      5 
      6 namespace UploadTest
      7 {
      8     public class UploadHelper
      9     {
     10 
     11         private UploadHelper()
     12         {
     13 
     14         }
     15 
     16         public UploadHelper(string fileRootPath)
     17         {
     18             if (string.IsNullOrWhiteSpace(fileRootPath))
     19                 throw new ArgumentNullException("fileRootPath", "fileRootPath is null");
     20 
     21             FileRootPath = fileRootPath;
     22             BlockRootPath = fileRootPath + "/blocktmp/";
     23         }
     24         /// <summary>
     25         /// 块文件存储根路径
     26         /// </summary>
     27         private string BlockRootPath { get; set; }
     28 
     29         /// <summary>
     30         /// 文件存储根路径
     31         /// </summary>
     32         public string FileRootPath { get; set; }
     33 
     34         /// <summary>
     35         /// 分块上传
     36         /// </summary>
     37         public UploadResult Upload(string uploadId, int blockCount, int currIndex, string fileName, HttpPostedFileBase file)
     38         {
     39             try
     40             {
     41                 if (file == null)
     42                     return new UploadResult { Msg = "请选择文件~" };
     43                 if (blockCount < 1)
     44                     return new UploadResult { Msg = "块数量不能小于1~" };
     45                 if (currIndex < 0)
     46                     return new UploadResult { Msg = "块数量小于0~" };
     47                 if (string.IsNullOrWhiteSpace(uploadId) && currIndex > 1)
     48                     return new UploadResult { Msg = "上传编号为空~" };
     49 
     50                 var result = new UploadResult { Code = 1, Msg = "上传成功~" };
     51 
     52                 //首次上传需创建上传编号
     53                 if (string.IsNullOrWhiteSpace(uploadId) || uploadId.Equals("undefind"))
     54                     uploadId = GenerateUploadId();
     55 
     56                 result.UploadID = uploadId;
     57 
     58                 #region ==块处理==
     59 
     60                 //块文件名称
     61                 var blockName = $"{uploadId}_{currIndex}.block";
     62                 //块文件目录路径
     63                 var blockPath = Path.Combine(BlockRootPath, uploadId);
     64                 //块文件目录对象
     65                 DirectoryInfo blockDirectoryInfo = Directory.Exists(blockPath) ? new DirectoryInfo(blockPath) : Directory.CreateDirectory(blockPath);
     66                 //块文件完整路径
     67                 var blockFullPath = Path.Combine(blockPath, blockName);
     68                 if (File.Exists(blockFullPath))
     69                 {
     70                     //块已上传,不做失败处理
     71                     return new UploadResult { Code = 1, Msg = "该文件块已上传~" };
     72                 }
     73 
     74                 file.SaveAs(blockFullPath);
     75 
     76                 #endregion
     77 
     78                 #region ==块合并处理==
     79 
     80                 //判断块文件是否已将上传完,上传完合并文件
     81                 if (blockDirectoryInfo.GetFiles().Count().Equals(blockCount))
     82                 {
     83                     var timestamp = DateTime.Now.ToString("yyyMMdd");
     84                     fileName = uploadId + "." + GetExtension(fileName);
     85                     var filePath = Path.Combine(FileRootPath, timestamp);
     86                     if (!Directory.Exists(filePath))
     87                     {
     88                         Directory.CreateDirectory(filePath);
     89                     }
     90                     //完整文件存储路径
     91                     var fileFullPath = Path.Combine(filePath, fileName);
     92                     using (var fs = new FileStream(fileFullPath, FileMode.Create))
     93                     {
     94                         for (var i = 1; i <= blockCount; i++)
     95                         {
     96                             var path = Path.Combine(blockPath, $"{uploadId}_{i}.block");
     97                             var bytes = File.ReadAllBytes(path);
     98                             fs.Write(bytes, 0, bytes.Length);
     99                         }
    100                         Directory.Delete(blockPath, true);
    101 
    102                         result.FileInfo = new UploadFileInfo
    103                         {
    104                             FileName = fileName,
    105                             FilePath = Path.Combine(timestamp, fileName)
    106                         };
    107                     }
    108                 }
    109 
    110                 return result;
    111                 #endregion
    112             }
    113             catch (Exception ex)
    114             {
    115                 return new UploadResult { Msg = ex.Message };
    116             }
    117         }
    118 
    119         /// <summary>
    120         /// 生成上传唯一编号
    121         /// </summary>
    122         /// <returns></returns>
    123         public string GenerateUploadId()
    124         {
    125             var guid = Guid.NewGuid().ToString();
    126             return guid.Replace("-", "");
    127         }
    128 
    129         /// <summary>
    130         /// 获取文件扩展名
    131         /// </summary>
    132         /// <param name="fileName"></param>
    133         /// <returns></returns>
    134         public string GetExtension(string fileName)
    135         {
    136             if (string.IsNullOrWhiteSpace(fileName) || fileName.IndexOf(".") < 0)
    137             {
    138                 return string.Empty;
    139             }
    140             var arr = fileName.Split('.');
    141             return arr[arr.Length - 1];
    142         }
    143     }
    144     /// <summary>
    145     /// 文件上传结果
    146     /// </summary>
    147     public class UploadResult
    148     {
    149         /// <summary>
    150         /// 状态码 0失败 1成功
    151         /// </summary>
    152         public int Code { get; set; }
    153         /// <summary>
    154         /// 消息
    155         /// </summary>
    156         public string Msg { get; set; }
    157         /// <summary>
    158         /// 上传编号,唯一
    159         /// </summary>
    160         public string UploadID { get; set; }
    161         /// <summary>
    162         /// 文件保存信息
    163         /// </summary>
    164         public UploadFileInfo FileInfo { get; set; }
    165 
    166     }
    167     public class UploadFileInfo
    168     {
    169         /// <summary>
    170         /// 文件保存名称
    171         /// </summary>
    172         public string FileName { get; set; }
    173         /// <summary>
    174         /// 文件保存路径
    175         /// </summary>
    176         public string FilePath { get; set; }
    177         /// <summary>
    178         /// 文件MD5值
    179         /// </summary>
    180         public string MD5 { get; set; }
    181     }
    182 }
    UploadHelper

      Controller代码

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace UploadTest.Controllers
    {
        [RoutePrefix("upload")]
        public class UploadController : Controller
        {
            [Route]
            [HttpGet]
            public ActionResult Index()
            {
                return View();
            }
            [Route]
            [HttpPost]
            public ActionResult Upload(string uploadId,int total,int index,string fileName)
            {
                var helper = new UploadHelper("D:\Upload");
                var result = helper.Upload(uploadId, total, index, fileName, Request.Files[0]);
    
                return Json(result);
            }
        }
    }
    为之则易,不为则难
  • 相关阅读:
    JS 日期加多少天,减多少天
    SQL 触发器
    SGU100
    连续子数组的最大和
    字符串的排列
    二叉搜索树与双向链表
    数组中出现次数超过一半的数字
    复杂链表的复制
    二叉树中和为某一值的路径
    二叉搜索树的后序遍历序列
  • 原文地址:https://www.cnblogs.com/oldli/p/7271675.html
Copyright © 2020-2023  润新知