• C#实现分片上传文件


      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Web;
      5 using System.IO;
      6 using System.Configuration;
      7 
      8 namespace WebApplication1
      9 {
     10     /// <summary>
     11     /// PartialFileSet 的摘要说明
     12     /// </summary>
     13     public class UpLoadPartialFile : IHttpHandler
     14     {
     15         public static DateTime mkdirRecodeTime = DateTime.Now.AddDays(-2);
     16         public static string keyHead = DateTime.Now.Year + "" + DateTime.Now.Month + DateTime.Now.Day;
     17         public static string path = ConfigurationManager.AppSettings["filePath"].ToString();
     18         public static object mkdirLock = new object();
     19    
     20         /// <summary>
     21         /// 根据日期字符串返回路径
     22         /// </summary>
     23         /// <param name="keyHead">日期字符串yyyyMMdd</param>
     24         /// <returns></returns>
     25         private static string getCurrentPathByDate(string keyHead)
     26         {
     27 
     28             return path + "\" + keyHead;
     29         }
     30 
     31         private static string getCurrentFilePathByDate(string keyHead, string guid, int id)
     32         {
     33             return getCurrentPathByDate(keyHead) + "\" + guid + id;
     34         }
     35 
     36         private static string getNewFilePathByDate(string keyHead, string guid)
     37         {
     38             string fileType = HttpContext.Current.Request["fileType"];
     39             if(fileType!=null&& fileType.Length>0)
     40             return getCurrentPathByDate(keyHead) + "\" + guid+"."+fileType;
     41             return getCurrentPathByDate(keyHead) + "\" + guid;
     42         }
     43         /// <summary>
     44         /// 如果目标文件夹不存在 创建文件夹
     45         /// </summary>
     46         /// <param name="keyHead"></param>
     47         private static void mkdirIfNoExit()
     48         {
     49 
     50             if ((DateTime.Now-mkdirRecodeTime ).Days < 1)
     51                 return;
     52           
     53             //创建文件夹目录
     54             if (Directory.Exists(getCurrentPathByDate(keyHead)))
     55                 return;
     56             lock (mkdirLock)
     57             {
     58 
     59                 if (!Directory.Exists(path))
     60                     Directory.CreateDirectory(path);
     61                 if (!Directory.Exists(getCurrentPathByDate(keyHead)))
     62                     Directory.CreateDirectory(getCurrentPathByDate(keyHead));
     63 
     64                 keyHead = DateTime.Now.Year + "" + DateTime.Now.Month + DateTime.Now.Day;
     65                 mkdirRecodeTime = DateTime.Now;
     66                 return;
     67             }
     68 
     69         }
     70 
     71         private void uploadFile(HttpContext context)
     72         {
     73             try
     74             {
     75                 //检查目录
     76                 mkdirIfNoExit();
     77                 
     78 
     79 
     80                 if (context.Request.Files == null || context.Request.Files.Count == 0 || context.Request.Files[0].InputStream.Length == 0)
     81                 {
     82                     context.Response.Write("{"state":"error","code":-6,"msg":"接口调用出错 上传文件不能为空"}");
     83                     return;
     84                 }
     85                 if (context.Request.Files.Count > 1)
     86                 {
     87                     context.Response.Write("{"state":"error","code":-7,"msg":"接口调用出错 每次只能上传单个文件"}");
     88                     return;
     89                 }
     90                 string guid = Guid.NewGuid().ToString();
     91 
     92                 string keyHead = DateTime.Now.Year + "" + DateTime.Now.Month + DateTime.Now.Day;
     93                 string currentPath = getCurrentPathByDate(keyHead);
     94                 string filePath = getNewFilePathByDate(keyHead, guid);
     95               
     96                 lock (filePath)
     97                 {
     98                     //创建文件
     99                     try
    100                     {
    101                         context.Request.Files[0].SaveAs(filePath);
    102 
    103                     }
    104                     catch (Exception e)
    105                     {
    106                         Util.LogHelper.Info(e.Message + e.StackTrace);
    107 
    108                         context.Response.Write("{"state":"error","code":-2,"msg":"接口调用出错 guid为" + guid + "的文件写入时出现错误 已记录日志"}");
    109                         return;
    110                     }
    111                 }
    112                 context.Response.Write("{"state":"success","code":0,"msg":""+keyHead+guid+""}");
    113 
    114             }
    115             catch (Exception e)
    116             {
    117                 Util.LogHelper.Info(e.Message + e.StackTrace);
    118                 context.Response.Write("{"state":"error","code":-1,"msg":"接口调用出错 已记录日志"}");
    119             }
    120         }
    121 
    122         private void uploadPartialFile(HttpContext context) {
    123             try
    124             {
    125                 //检查目录
    126                 mkdirIfNoExit();
    127                 if (context.Request["date"] == null)
    128                 {
    129                     context.Response.Write("{"state":"error","code":-2,"msg":"接口调用出错 参数date(第一片上传时间)不能为空"}");
    130                     return;
    131                 }
    132                 DateTime uploadData;
    133                 if (!DateTime.TryParse(context.Request["date"], out uploadData))
    134                 {
    135 
    136                     context.Response.Write("{"state":"error","code":-2,"msg":"接口调用出错date参数错误 格式 yyyy-MM-dd"}");
    137                     return;
    138                 }
    139                 if (context.Request["guid"] == null || context.Request["guid"].Trim().Length != 32)
    140                 {
    141                     context.Response.Write("{"state":"error","code":-3,"msg":"接口调用出错 guid(文件唯一标示)不能为空且必须为32位长度"}");
    142                     return;
    143                 }
    144 
    145                 if (context.Request["id"] == null || context.Request["id"].Trim().Length == 0)
    146                 {
    147                     context.Response.Write("{"state":"error","code":-4,"msg":"接口调用出错 id(文件分组id)不能为空"}");
    148                     return;
    149                 }
    150                 int id = -1;
    151                 if (!int.TryParse(context.Request["id"], out id))
    152                 {
    153                     context.Response.Write("{"state":"error","code":-5,"msg":"接口调用出错 id(文件分组id)必须为数字"}");
    154                     return;
    155                 }
    156 
    157 
    158                 if (context.Request.Files == null || context.Request.Files.Count == 0 || context.Request.Files[0].InputStream.Length == 0)
    159                 {
    160                     context.Response.Write("{"state":"error","code":-6,"msg":"接口调用出错 上传文件不能为空"}");
    161                     return;
    162                 }
    163                 if (context.Request.Files.Count > 1)
    164                 {
    165                     context.Response.Write("{"state":"error","code":-7,"msg":"接口调用出错 每次只能上传单个文件"}");
    166                     return;
    167                 }
    168                 string guid = context.Request["guid"].Trim();
    169                 string keyHead = uploadData.Year + "" + uploadData.Month + uploadData.Day;
    170                 string currentPath = getCurrentPathByDate(keyHead);
    171                 string filePath = getCurrentFilePathByDate(keyHead, guid, id);
    172                 if (File.Exists(filePath))
    173                 {
    174                     context.Response.Write("{"state":"error","code":-8,"msg":"接口调用出错 guid为" + guid + "文件id为" + id + "的文件已存在"}");
    175                     return;
    176                 }
    177                 lock (filePath)
    178                 {
    179                     if (File.Exists(filePath))
    180                     {
    181                         context.Response.Write("{"state":"error","code":-8,"msg":" guid为" + guid + "文件id为" + id + "的文件已存在"}");
    182                         return;
    183                     }
    184                     //创建文件
    185                     try
    186                     {
    187                         context.Request.Files[0].SaveAs(filePath);
    188 
    189                     }
    190                     catch (Exception e)
    191                     {
    192                         Util.LogHelper.Info(e.Message + e.StackTrace);
    193 
    194                         context.Response.Write("{"state":"error","code":-9,"msg":"接口调用出错 guid为" + guid + "文件id为" + id + "的文件写入时出现错误 已记录日志"}");
    195                         return;
    196                     }
    197                 }
    198 
    199                 context.Response.Write("{"state":"success","code":0,"msg":" guid为" + guid + "文件id为" + id + "的文件写入成功"}");
    200 
    201             }
    202             catch (Exception e)
    203             {
    204                 Util.LogHelper.Info(e.Message + e.StackTrace);
    205                 context.Response.Write("{"state":"error","code":-1,"msg":"接口调用出错 已记录日志"}");
    206             }
    207         }
    208 
    209         private void joinfile(HttpContext context)
    210         {
    211             try
    212             {
    213                 //检查目录
    214                 if (context.Request["date"] == null)
    215                 {
    216                     context.Response.Write("{"state":"error","code":-2,"msg":"接口调用出错 参数date(第一片上传时间)不能为空"}");
    217                     return;
    218                 }
    219                 if (context.Request["guid"] == null || context.Request["guid"].Trim().Length != 32)
    220                 {
    221                     context.Response.Write("{"state":"error","code":-3,"msg":"接口调用出错 guid(文件唯一标示)不能为空且必须为32位长度"}");
    222                     return;
    223                 }
    224 
    225                 if (context.Request["idArray"] == null || context.Request["idArray"].Trim().Length == 0)
    226                 {
    227                     context.Response.Write("{"state":"error","code":-4,"msg":"接口调用出错 id(文件分组id列表)不能为空"}");
    228                     return;
    229                 }
    230                 DateTime uploadData;
    231                 if (!DateTime.TryParse(context.Request["date"],out uploadData)) {
    232 
    233                     context.Response.Write("{"state":"error","code":-6,"msg":"接口调用出错date参数错误 格式 yyyy-MM-dd"}");
    234                     return;
    235                 }
    236 
    237                 string keyHead = uploadData.Year + "" + uploadData.Month + uploadData.Day;
    238                 string guid = context.Request["guid"].Trim();
    239 
    240                 List<int> IdArray = context.Request["idArray"].StringArrayConvertInt(',').OrderBy(u=>u).ToList();
    241                 //开始检查文件是否全部存在
    242                 List<string> pathList = new List<string>();
    243                 if (IdArray.Count < 1)
    244                 {
    245                     context.Response.Write("{"state":"error","code":-8,"msg":"接口调用出错文件列表文件少于2个无法进行合并操作 请检查IdArray参数"}");
    246                     return;
    247                 }
    248                 foreach (var item in IdArray)
    249                 {
    250                     string path = getCurrentFilePathByDate(keyHead,guid,item);
    251                     if (!File.Exists(path))
    252                     {
    253                         context.Response.Write("{"state":"error","code":-7,"msg":"id编号为"+item+"的文件在服务器上不存在 请检查"}");
    254                         return;
    255                     }
    256                     pathList.Add(path);
    257 
    258                 }
    259                 string newGuid = Guid.NewGuid().ToString();
    260                 string newFilePath = getNewFilePathByDate(keyHead,newGuid);
    261                 using (System.IO.FileStream fileStram = File.Open(newFilePath, FileMode.Create,FileAccess.Write))
    262                 {
    263 
    264                     //开始合并文件  创建一个副本
    265                     pathList.ForEach(u =>
    266                 {
    267                     //读取文件
    268                     using (FileStream save = new FileStream(u, FileMode.Open, FileAccess.Read))
    269                     {
    270                         byte[] bt = new byte[1024];
    271                         int count = -1;
    272                         while ((count = save.Read(bt, 0, bt.Length)) > 0)
    273                         {
    274                             fileStram.Write(bt, 0, count);
    275                         }
    276                     }
    277 
    278                 });
    279                     context.Response.Write("{"state":"success","code":0,"msg":""+ keyHead + newGuid + ""}");
    280                     //删除文件列表
    281                     delFile(pathList);
    282                 }
    283 
    284             }
    285             catch (Exception e)
    286             {
    287                 Util.LogHelper.Info(e.Message + e.StackTrace);
    288                 context.Response.Write("{"state":"error","code":-1,"msg":"接口调用出错 已记录日志"}");
    289             }
    290         }
    291         public bool IsReusable
    292         {
    293             get
    294             {
    295                 return false;
    296             }
    297         }
    298         public static void delFile(List<string> pathList) {
    299             try
    300             {
    301 
    302                 for (int i = 0; i < pathList.Count; i++)
    303                 {
    304                     File.Delete(pathList[i].Replace("\","/"));
    305                 }
    306             }
    307             catch (Exception e)
    308             {
    309                 Util.LogHelper.Info("删除分片文件错误"+e.Message+e.StackTrace);
    310             }
    311         }
    312 
    313         /// <summary>
    314         /// 创建部分文件集合
    315         /// </summary>
    316         /// <param name="context"></param>
    317         public  void ProcessRequest(HttpContext context)
    318         {
    319             try
    320             {
    321 
    322           
    323             context.Response.ContentType = "application/json";
    324 
    325             string mode = context.Request["mode"].ToLower();
    326                 if (mode == "partialfile")
    327                     uploadPartialFile(context);
    328                 else if (mode == "joinfile")
    329                     joinfile(context);
    330                 else if (mode == "uploadfile")
    331                     uploadFile(context);
    332                 else
    333                 context.Response.Write("{"state":"error","code":-10,"msg":"接口调用出错 mode值范围为partialfile(上传分片文件)joinfile(合并分片文件)uploadfile上传单个文件 三种 "}");
    334 
    335             }
    336 
    337             catch (Exception e)
    338             {
    339 
    340                 Util.LogHelper.Info("ProcessRequest执行错误" + e.Message + e.StackTrace);
    341                 context.Response.Write("{"state":"error","code":-1,"msg":"接口调用出错 已记录日志"}");
    342 
    343 
    344             }
    345         }
    346 
    347 
    348 
    349 
    350 
    351 
    352     }
    353 }

    js调用

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>
        <script src="https://cdn.bootcss.com/jquery.form/3.51/jquery.form.js"></script>
        <form id="fileUpLoad" action="/UpLoadPartialFile.ashx?mode=partialfile" method="post" name="test">
            <input type="file" name="name" onchange="upload(this)" />上传分片文件
            <input type="hidden" id="guid" name="guid"value="aaaaaaaaaaaaaaaa" />
            <input type="hidden" name="date" value="2017-10-02" />
            <input type="hidden" id="id" name="id" value="0" />
    
        </form>
    
        <form id="fileUpLoadFile" action="/UpLoadPartialFile.ashx?mode=uploadfile" method="post" name="test">
            <input type="file" name="name2" onchange="uploadFile(this)"  />上传单个文件
    
        </form>
        <input type="text" id="fileType" name="fileType" placeholder="合并的文件后缀名称 可选(合并文件时有效)" value="mp4" />
        <button onclick="joinFIle()">合并文件</button>
    
    </body>
    </html>
    
    <script>
    
        function newGuid() {
            var guid = "";
            for (var i = 1; i <= 32; i++) {
                var n = Math.floor(Math.random() * 16.0).toString(16);
                guid += n;
              
            }
            return guid;
        }
        var idArray = '';
        var date = 2017-10-02;
       var  i = 0;
        var guid = newGuid();
        $('#guid').val(guid);
        function joinFIle() {
    
            $.ajax('/UpLoadPartialFile.ashx?mode=joinfile&date=2017-10-02&guid=' + guid + '&idArray=' + idArray + '&fileType=' + $('#fileType').val()).done(function (rs) {
                alert(rs.msg);
            })
    
        }
        function upload(obj) {
    
            if (obj.size == 0)
                return;
            //上传表单
            $('#id').val(++i);
            idArray += i+',';
            $('#fileUpLoad').ajaxSubmit();
    
        }
    
        function uploadFile() {
            $('#fileUpLoadFile').ajaxSubmit();
        }
    </script>

    C#调用

            private static void post1()
            {
                string url = @"http://localhost:1128/uploadPartialFile.ashx?mode=partialfile&date=2017-10-03&id=1&guid=5ed1eddf7e7213ed0db6d5150b488335";//这里就不暴露我们的地址啦
                string modelId = "4f1e2e3d-6231-4b13-96a4-835e5af10394";
                string updateTime = "2016-11-03 14:17:25";
                string encrypt = "f933797503d6e2c36762428a280e0559";
    
                string filePath = @"D:/test2";
                string fileName = "test2";
                byte[] fileContentByte = new byte[1024]; // 文件内容二进制
    
                #region 将文件转成二进制
    
                FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                fileContentByte = new byte[fs.Length]; // 二进制文件
                fs.Read(fileContentByte, 0, Convert.ToInt32(fs.Length));
                fs.Close();
    
                #endregion
    
    
                #region 定义请求体中的内容 并转成二进制
    
                string boundary = "ceshi";
                string Enter = "
    ";
    
                string modelIdStr = "--" + boundary + Enter
                        + "Content-Disposition: form-data; name="modelId"" + Enter + Enter
                        + modelId + Enter;
    
                string fileContentStr = "--" + boundary + Enter
                        + "Content-Type:application/octet-stream" + Enter
                        + "Content-Disposition: form-data; name="fileContent"; filename="" + fileName + """ + Enter + Enter;
    
                string updateTimeStr = Enter + "--" + boundary + Enter
                        + "Content-Disposition: form-data; name="updateTime"" + Enter + Enter
                        + updateTime;
    
                string encryptStr = Enter + "--" + boundary + Enter
                        + "Content-Disposition: form-data; name="encrypt"" + Enter + Enter
                        + encrypt + Enter + "--" + boundary + "--";
    
    
                var modelIdStrByte = Encoding.UTF8.GetBytes(modelIdStr);//modelId所有字符串二进制
    
                var fileContentStrByte = Encoding.UTF8.GetBytes(fileContentStr);//fileContent一些名称等信息的二进制(不包含文件本身)
    
                var updateTimeStrByte = Encoding.UTF8.GetBytes(updateTimeStr);//updateTime所有字符串二进制
    
                var encryptStrByte = Encoding.UTF8.GetBytes(encryptStr);//encrypt所有字符串二进制
    
    
                #endregion
    
    
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = "POST";
                request.ContentType = "multipart/form-data;boundary=" + boundary;
    
                Stream myRequestStream = request.GetRequestStream();//定义请求流
    
                #region 将各个二进制 安顺序写入请求流 modelIdStr -> (fileContentStr + fileContent) -> uodateTimeStr -> encryptStr
    
                myRequestStream.Write(modelIdStrByte, 0, modelIdStrByte.Length);
    
                myRequestStream.Write(fileContentStrByte, 0, fileContentStrByte.Length);
                myRequestStream.Write(fileContentByte, 0, fileContentByte.Length);
    
                myRequestStream.Write(updateTimeStrByte, 0, updateTimeStrByte.Length);
    
                myRequestStream.Write(encryptStrByte, 0, encryptStrByte.Length);
    
                #endregion
    
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();//发送
    
                Stream myResponseStream = response.GetResponseStream();//获取返回值
                StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
    
                string retString = myStreamReader.ReadToEnd();
    
                myStreamReader.Close();
                myResponseStream.Close();
            }

    Code=0表示接口调用成功

    其他情况下code为负数 msg中包含具体错误信息

    单个分片文件上传不能超过4000kb

    上传分片文件

    http://localhost:1128/UpLoadPartialFile.ashx?mode=partialfile

    method:post

    参数

    date 分片上传 参数格式yyyy-MM-dd日期 例如:     2017-10-02 所有分片上传都使用一个时间 服务端根据这个参数来区分文件夹

    id  (整数) 分片文件编号 如1  服务端合并文件将根据编号由小到大排序合并

    guid 客户端生成一个guid 后面所有同一个文件的分片均使用同一个guid

    guid为32位长度 即省略-分隔符

    浏览器js调用报文如下

    调用成功返回json

    {"state":"success","code":0,"msg":" guid为db334a03fc01b200c770871374734c7b文件id为1的文件写入成功"}

    合并分片文件

    http://localhost:1128/UpLoadPartialFile.ashx?mode=joinfile&date=2017-10-02&guid=db334a03fc01b200c770871374734c7b&idArray=1,2,&fileType=mp4

    method :post

    参数

    date 分片上传时间(必选)

    guid 上传分片时使用的guid (必选)

    idArray 编号列表 拼接逗号分隔 需要两个以上 否则无法合并文件(必选)

    filetype 文件后缀名称 可选 如果传递该参数 读取时也要传递 否则无法找到文件

    调用成功返回

    {"state":"success","code":0,"msg":"2017102cdb64b77-5f7c-4ccf-aa11-1e327dfcd49f"}

    Msg中的字符串用于读取文件

    上传单个文件

    http://localhost:1128/UpLoadPartialFile.ashx?mode=uploadfile

    method:post

    文件流写入到请求中

    调用成功返回

    {"state":"success","code":0,"msg":"2017102cdb64b77-5f7c-4ccf-aa11-1e327dfcd49f"}

    Msg中的字符串用于读取文件 

    安卓参考实现

    http://blog.csdn.net/ylbf_dev/article/details/50468984

    ios参考实现

    http://www.jianshu.com/p/a0e3c77d3164

  • 相关阅读:
    5G NR系列(四)物理下行共享信道(PDSCH)物理层过程详解
    5G NR系列(三)PDSCH的解调参考信号(DM-RS)
    Mac上重装pycharm打不开的解决方法
    Oracle parallel理解
    V$ASM_DISKGROUP视图信息解读
    深入了解 Oracle Flex ASM 及其优点
    使用typora和印象笔记高效输出
    Centos7.6部署k8s 集群
    DBA日常职责
    利用DCLI命令实现跨机器检查
  • 原文地址:https://www.cnblogs.com/ProDoctor/p/7625268.html
Copyright © 2020-2023  润新知