webUploader上传组件 实际运用小结

WebUploader组件实际介绍:

  1. 官网:http://fex.baidu.com/webuploader/doc/index.html
  2. 组件优势及优化总结:http://itindex.net/detail/49267-webuploader-%E6%96%87%E4%BB%B6-%E4%B8%8A%E4%BC%A0
  3. 组件暂停功能的问题:https://github.com/fex-team/webuploader/issues/488
  4. 断点续传问题:https://github.com/fex-team/webuploader/issues/142
  5. 具体思路可以先参考链接:http://www.sxrczx.com/pages/blog.kazaff.me/uxgb1423648892626.html

以下内容于你对上述组件有了大致了解的基础上,结合C#实现大文件的上传、断点续传、分块等功能,展开说明的:

设计思路:在每次文件上传前,获取该文件的MD5值作为该文件的唯一标识符,然后对该文件进行分块处理(此处每块设置5M大小,详见2),按块分发请求(此处设置为3个线程发请求,详见2)文件开始上传前触发,一个文件只会触发一次 uploadStart事件,该事件会验证之前该文件是否已上传部分,返回已上传部分的块数编号列表,然后在下一个事件before-send(在文件分片处理之后,上传之前会触发)判断哪些块数已存在,上传不存在的文件块,至此基本上传流程已完成。

    /// <summary>
    /// 文件上传消息类
    /// </summary>
    public class UploaderResult
    {
        public UploaderResult()
        {
            this.IsOver = false;
            this.Chunk = 0;
            this.Chunks = 0;
            this.FileExtist = false;
            this.ChunkNum = new List<int>();
        }

        /// <summary>
        /// 文件名称、当上传完成时将返回最终的文件名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 文件是否全部上传完成
        /// </summary>
        public bool IsOver { get; set; }

        /// <summary>
        /// 消息
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// 如果为分块上传、返回当前的分块索引
        /// </summary>
        public int Chunk { get; set; }

        /// <summary>
        /// 总的分块大小
        /// </summary>
        public int Chunks { get; set; }

        /// <summary>
        /// 文件的MD5码
        /// </summary>
        public string Md5 { get; set; }

        /// <summary>
        /// 上传的文件是否已经存在于服务器
        /// </summary>
        public bool FileExtist { get; set; }

        /// <summary>
        /// 服务器已经存在的区块序号
        /// </summary>
        public List<int> ChunkNum { get; set; }

        /// <summary>
        /// 文件扩展名
        /// </summary>
        public string FileExtension { get; set; }

        /// <summary>
        /// 文件路径
        /// </summary>
        public string FilePath { get; set; }

        /// <summary>
        /// 文件大小
        /// </summary>
        public int FileSize { get; set; }
    }
        /// <summary>
        /// 断点续传检测、MD5检测 秒传亦可以在这个方法块中实现,看需求
        /// </summary>
        public static UploaderResult ProcessCheck(HttpRequestBase request, string savepath = null
            , Func<HttpPostedFileBase, string> setfilename = null, Func<string, bool> md5check = null)
        {
            UploaderResult obj = new UploaderResult();
            string tempFilePath = savepath + "temp\\" + request["md5"] + "\\";
          
            //文件大小
            long size = request.Form["size"] == null ? 0 : Convert.ToInt64(request.Form["size"]);

            //文件分块大小
            long chunksize = request.Form["chunksize"] == null ? 0 : Convert.ToInt64(request.Form["chunksize"]);

            //文件区块总数
            int chunks = chunksize != 0 ? Convert.ToInt32(size / chunksize) : 1;
            int j = 0;
            for (int i = 0; i <= chunks; i++)
            {
                if (File.Exists(tempFilePath + i.ToString()))
                {
                    obj.ChunkNum.Add(i);//服务器已经存在的区块编号
                    j++;
                }
            }
            obj.Message = string.Format("服务器已经存在区块数量{0},总区块数量{1},占比{2}%", j
                , chunks + 1, chunks != 0 && j != 0 ? Convert.ToDouble(Convert.ToDouble(j) / Convert.ToDouble(chunks)) * 100 : 0);
            return obj;
        }     
        /// <summary>
        /// 文件上传、保存
        /// </summary>
        /// <param name="request"></param>
        /// <param name="savepath"></param>
        /// <returns></returns>
        public static UploaderResult UploadSingleProcess(HttpRequestBase request, string savepath = null)
        {
            UploaderResult obj = new UploaderResult();
            if (request.Files.Count == 0)
            {
                obj.Message = "请求中不包含文件流信息";
                return obj;
            }
            if (request["size"] == null)
            {
                obj.Message = "文件大小为空";
                return obj;
            }
            string tempFilePath = savepath + "temp\\" + request["md5"] + "\\";
            string saveFilePath = savepath + "files\\";
            if (!Directory.Exists(saveFilePath))
            {
                Directory.CreateDirectory(saveFilePath);
            }
            //判断是否分片,若文件大小不足分片,则直接保存
            if (request.Form.AllKeys.Any(m => m == "chunk"))
            {
                //分片时创建临时文件目录
                if (!Directory.Exists(tempFilePath))    
                {
                    Directory.CreateDirectory(tempFilePath);
                }
                //取得chunk和chunks
                int chunk = Convert.ToInt32(request.Form["chunk"]);
                int chunks = Convert.ToInt32(request.Form["chunks"]);
                HttpPostedFileBase file = request.Files[0];
                //根据GUID创建用该GUID命名的临时文件
                file.SaveAs(tempFilePath + chunk.ToString());
                //判断是否所有的分块都已经上传完毕
                string[] fileArr = Directory.GetFiles(tempFilePath);
                if (fileArr.Length == chunks) {
                    obj.IsOver = true;
                }
            }
            else
            {
                request.Files[0].SaveAs(saveFilePath + request.Files[0].FileName);
                obj.IsOver = true;
            }
            return obj;
        }
        /// <summary>
        /// 文件块合并
        /// </summary>
        /// <returns></returns>
        public ActionResult MergeFiles()
        {
            string ext = Request.Form["fileExt"];
            string fileName = Request.Form["fileName"];
            string chunkNum = Request.Form["chunkNum"];
            string md5 = Request.Form["md5"];
            string tempFilePath = this.RootFolder + "\\temp\\" + md5 + "\\";
            string saveFilePath = this.RootFolder + "\\files\\" + fileName;  
            string[] fileArr = Directory.GetFiles(tempFilePath);
            try
            {
                lock (tempFilePath)
                {
                    if (Convert.ToInt32(chunkNum) == fileArr.Length)
                    {
                        if (System.IO.File.Exists(saveFilePath))
                        {
                            System.IO.File.Delete(saveFilePath);
                        }
                        FileStream addStream = new FileStream(saveFilePath, FileMode.Append, FileAccess.Write);
                        BinaryWriter AddWriter = new BinaryWriter(addStream);
                        for (int i = 0; i < fileArr.Length; i++)
                        {
                            //以小文件所对应的文件名称和打开模式来初始化FileStream文件流,起读取分割作用
                            FileStream TempStream = new FileStream(tempFilePath+i, FileMode.Open);
                            //用FileStream文件流来初始化BinaryReader文件阅读器,也起读取分割文件作用
                            BinaryReader TempReader = new BinaryReader(TempStream);
                            //读取分割文件中的数据,并生成合并后文件
                            AddWriter.Write(TempReader.ReadBytes((int)TempStream.Length));
                            //关闭BinaryReader文件阅读器
                            TempReader.Close();
                            TempStream.Close();
                        }
                        AddWriter.Close();
                        addStream.Close();
                        AddWriter.Dispose();
                        addStream.Dispose();
                        Directory.Delete(tempFilePath, true);
                    }
                    //验证文件的MD5,确定文件的完整性 前端文件MD5值与后端合并的文件MD5值不相等
                    //if (UploaderHelper.GetMD5HashFromFile(saveFilePath) == md5)
                    //{
                    //    int i = 1;
                    //}
                    return Json("{hasError:\"false\"}");
                }
            }
            catch (Exception ex)
            {
                return Json("{hasError:\"true\"}");
            }
        }

后台代码,还需要整理优化,当然如果有问题,大家可以一块学习谈论。

 

posted @ 2016-03-03 17:15  米兰_跳跳虎  阅读(4590)  评论(3编辑  收藏  举报