前端通信:ajax设计方案(四)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组 - 仲强 - 博客园

阅读 601
收藏 35
2018-01-30
原文链接:www.cnblogs.com
前端通信:ajax设计方案(四)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组 - 仲强 - 博客园

半个诗人

前端通信:ajax设计方案(四)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

马上要过年了,哎,回家的心情也特别的激烈。有钱没钱,回家过年,家永远是舔舐伤口最好的地方。新的一年继续加油努力。

上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行检查和限制。但是上次还有个心结一直没解开,就是本来前端浏览器的文件切割已经好了,但是后台文件重组一直没搞明白和处理好,所以就搁置了。主要也是对自己的代码负责,因为自己本身都没把这个技术搞透彻,外加各种测试都没通过,不想这样打脸。所以这个心结一直憋了好久,做梦都在想。终于功夫不负有心人,这周终于将这个问题干掉了,一个字:爽!!!下面咱们直接干!!

一些概念:

  前端ajax的level2的方案中可以发送很多数据类型,具体可查看ajax2的设计规范(PS:w3c的原版设计规范,打开有点慢)

  js新的技术中增加了File对象(实际上就是blob的具体化的一个东西),设计规范参考这里2个 File API 规范 和 FileSystem API 规范

  后台二进制文件重组

    1. 创建空的文件流

    2. 读取临时存储切割文件的文件夹,获得所有文件路径  切记:一定要按顺序进行排序,否则组合文件将会错误

    3. 按顺序将切割小文件读取成二进制流,写进空的文件流中

    4. 写入完成,关闭文件流,删除临时存储切割文件的地方

 

工具准备:

    1. 前端代码,包含ajax库和测试代码

    2. nginx服务器,做分离,反向代理后台代码

    3. 文件MD5计算工具,检查文件切割上传完成之后是否改变

    4. IIS服务器,部署后台切割文件组合代码

    5. postMan接口测试工具,测试接口状态

 

具体思路:

    1. 前端浏览器选择文件(如果默认没有超过切割大小将使用默认上传,否则将进行文件切割上传)

    2. 前端使用新的特性将文件进行切割成一片一片的子文件,并每次分配一个请求

    3. 后端接受文件,将子文件进行存储

    4. 后端根据请求参数判断,如果为最后一次切割文件上传,则执行切割文件重组

    5. 重组文件,并清除临时存储的子文件

 

前端切割文件代码:

        //切割大文件
        cutFile:function(file,cutSize){
            var count = file.size / cutSize | 0 ,fileArr = [];
            for (var i= 0; i< count ; i++){
                fileArr.push({
                    name:file.name+".part"+(i+1),
                    file:file.slice( cutSize * i , cutSize * ( i + 1 ))
                });
            };
            fileArr.push({
                name:file.name+".part"+(count+1),
                file:file.slice(cutSize*count,file.size)
            });
            return fileArr;
        }

前端切割文件上传代码:

        /*
         *   ajax大文件切割上传(支持单个文件)  -- level2的新特性,请保证你的项目支持新的特性再使用
         *       url                 文件上传地址
         *       fileSelector        input=file 选择器
         *       cutSize             切割文件大小
         *       fileType            文件限制类型 mime类型
         *       successEvent        上传成功处理
         *       progressEvent       上传进度事件
         *       errorEvent          上传失败处理
         *       timeoutEvent        超时处理事件
         *
         *   return: status:  0      请选择文件
         *                    1      非允许文件格式
         * */
        upload_big:function(url,fileSelector,cutSize,fileType,successEvent,progressEvent,errorEvent,timeoutEvent){
            var file = document.querySelector(fileSelector).files,result ={};
            //以下为上传文件限制检查
            if (file.length === 1){
                if (fileType != "*"){
                    if (fileType.indexOf(file.type)=== -1 ){
                        result["status"] = 1;
                        result["errMsg"] = "非允许文件格式";
                    }
                }
            }else{
                result["status"] = 0;
                result["errMsg"] = "请选择文件/只能上传一个文件";
            };

       if (result.status !== undefined)  return result;   //如果有错误信息直接抛出去,结束运行
//判断上传文件是否超过需要切割的大小 if (file[0].size > cutSize){ var fileArr = tool.cutFile(file[0],cutSize); //切割文件 cutFile_upload(fileArr); }else{ return tempObj.upload(url,fileSelector,file[0].size,fileType,successEvent,errorEvent,timeoutEvent); }; /* * 切割文件上传,配合后台接口进行对接 * 传输参数: * count -- 当前传输part的次数 * name -- 做过处理的文件名称 * file -- 上传的.part的切割文件 * isLast -- 是否为最后一次切割文件上传(默认值:"true" 字符串,只有最后一次才附加) * */ function cutFile_upload(fileArr,count){ var formData = new FormData(); if (count == undefined){ count = 0; formData.append("count",count); formData.append("name",fileArr[0].name); formData.append("file".name,fileArr[0].file); }else{ if (count === fileArr.length-1){ formData.append("isLast","true") }; formData.append("count",count); formData.append("name",fileArr[count].name); formData.append("file".name,fileArr[count].file); }; var ajaxParam ={ type:"post", url:url, data:formData, isFormData:true, success:function(data){ /* * data 参数设置 需要后台接口配合 * 建议:如果后台成功保存.part文件,建议返回下次所需要的部分,比如当前发送count为0,则data返回下次为1。 * 如果保存不成功,则可false,或者返回错误信息,可在successEvent中处理 * * */ progressEvent(count+1,fileArr.length); //上传进度事件,第一个参数:当前上传次数;第二个参数:总共文件数 var currCount = Number(data); if (currCount){ if (currCount != fileArr.length){ cutFile_upload(fileArr,currCount); }; }; successEvent(data); //成功处理事件 }, error:errorEvent, timeout:timeoutEvent }; ajax.common(ajaxParam); } }

后端文件重组代码(.NET webAPI)-- 其他任何后端语言思想是通用的:

        [Route("upload5")]
        public int Post_bigFile1()
        {
            //前端传输是否为切割文件最后一个小文件
            var isLast = HttpContext.Current.Request["isLast"];
            //前端传输当前为第几次切割小文件
            var count = HttpContext.Current.Request["count"];
            //获取前端处理过的传输文件名
            string fileName = HttpContext.Current.Request["name"];
            //存储接受到的切割文件
            HttpPostedFile file = HttpContext.Current.Request.Files[0];

            //处理文件名称(去除.part*,还原真实文件名称)

            string newFileName = fileName.Substring(0, fileName.LastIndexOf('.'));
            //判断指定目录是否存在临时存储文件夹,没有就创建
            if (!System.IO.Directory.Exists(@"D:\" + newFileName))
            {
                //不存在就创建目录 
                System.IO.Directory.CreateDirectory(@"D:\" + newFileName);
            }
            //存储文件
            file.SaveAs("D:\\" + newFileName + "\\" + HttpContext.Current.Request["name"]);
            //判断是否为最后一次切割文件传输
            if (isLast == "true")
            {
                //判断组合的文件是否存在
                if (File.Exists(@"L:\\" + newFileName))//如果文件存在
                {
                    File.Delete(@"L:\\" + newFileName);//先删除,否则新文件就不能创建
                }
                //创建空的文件流
                FileStream FileOut = new FileStream(@"L:\\" + newFileName, FileMode.CreateNew,FileAccess.ReadWrite);
                BinaryWriter bw = new BinaryWriter(FileOut);
                //获取临时存储目录下的所有切割文件
                string[] allFile = Directory.GetFiles("D:\\" + newFileName);
                //将文件进行排序拼接
                allFile = allFile.OrderBy(s => int.Parse(Regex.Match(s, @"\d+$").Value)).ToArray();
                //allFile.OrderBy();
                for (int i = 0; i < allFile.Length; i++)
                {
                    FileStream FileIn = new FileStream(allFile[i], FileMode.Open);
                    BinaryReader br = new BinaryReader(FileIn);
                    byte[] data = new byte[1048576];   //流读取,缓存空间
                    int readLen = 0;                //每次实际读取的字节大小
                    readLen = br.Read(data,0, data.Length);
                    bw.Write(data,0, readLen);
                    //关闭输入流
                    FileIn.Close();
                };
                //关闭二进制写入
                bw.Close();
                FileOut.Close();
            }
            return int.Parse(count) + 1;
        }

 

以下为测试代码:

  html页面代码:

选择文件:<input type="file" id="file1" multiple accept="*"/><br />
<input type="button" id="upload" value="上传" />

  js代码:

$("#upload").click(function () {
   var temp = ajax.upload_big("/api/ajaxUpload/upload5/","#file1",1024*1024,"*",function(x){},function(count,all){console.log("当前传输进度:"+count+"/"+all);})
});

浏览器测试结果:

   IE10-11:

  chrome

  opera:

  火狐

  edge

  360浏览器

 

 代码已集成github:github.com/GerryIsWarr…     点颗星星是我最大的鼓励,有什么问题可以博客、邮箱、github上留言

这一次上传版本,代码做过变动,变动如下:

  1. 增加大文件传输方法upload_big,工具类增加文件切割tool.cutFile
  2. 解决火狐浏览器默认要求后台返回xml类型的问题

 

遗留问题待确认:这次safair浏览器没有测试,因为File对象的slice方法不支持,加各种前缀测试都不支持,主要我是window版的safair,这个问题先记着,有测试过的兄弟可以帮忙看一下,我的window版safair浏览器是不支持的。

 

还有最重要的一点,如果有问题欢迎指出来,我在github上维护这个库,这段时间专注于前端的通信技术的研究,第一个阶段是ajax的通信技术,后期包括服务器的SSE推送技术,还有webScoket技术。

 

其实研究这个技术最大的感慨就是,本来前端切割文件相对来说简单,但是后台文件重组有点问题,查阅各种资料,FQ去谷歌等等,搞了好长一段时间才把后台接口的设计和实现完善。以后技术的发展不仅仅局限于一端的,所以能全栈发展就全栈发展,新的前端技术,都是需要后台进行配合才可以溜起来。对了,其实这些东西国外11年左右就开始搞了,国内还是相对不是很跟进,这次去外面看看发现了好多东西,还是需要多看看的,增长技术视野的。哦了,吃饭去了,再不吃要饿死了。

 

我的前端分布式,容器化,组件化的框架正在从无到有,到时候欢迎大家给建议和指正,3q。

 

再啰嗦几句,马上要过年了,大家吃好喝好玩好,来年继续奋战。代码改变世界。

posted on 2017-01-18 12:51 仲强 阅读(2890) 评论(18) 编辑 收藏

评论

#1楼 2017-01-18 14:17 小不了  

沙发一波!推荐~ 支持(0)反对(0)   

#2楼 2017-01-18 14:21 Sam Xiao  

现在js真的很牛X了,能把客户端的文件都可以切割了。

会不会带来安全隐患啊? 支持(0)反对(0) http://pic.cnblogs.com/face/48022/20150116085345.png   

#3楼[楼主] 2017-01-18 15:31 仲强  

@ Sam Xiao
浏览器有沙盒机制的,这个放心。而且这个切割是用户选择了一个文件之后才能切割的,至于切割文件上传,可以在二进制流中自己做偏移加密,后台去解密 支持(0)反对(0) http://pic.cnblogs.com/face/801930/20161012082514.png   

#4楼 2017-01-18 20:31 yaoworld  

前段时间 做过类似的。
不过客户的文件 单文件 有 300G的。 支持(0)反对(0) http://pic.cnblogs.com/face/822899/20170208114142.png   

#5楼[楼主] 2017-01-18 20:53 仲强  

@ yaoworld
前端做的话相对来说 分担了服务器的压力,而且前端相对好控制。其实还有容错机制,哪部分失败了,只需要重新截取那个部分的子文件,不需要整体重新来。这是ajax技术给的优势,合理使用还是会分担很多压力的 支持(0)反对(0) http://pic.cnblogs.com/face/801930/20161012082514.png   

#6楼 2017-01-19 10:02 2604529  

mark 支持(0)反对(0) http://pic.cnblogs.com/face/u117648.jpg?id=08152557   

#7楼 2017-01-19 13:01 为之则易  

以前切割都是要借助于插件(flash/silverlight/activex),js也可以切割了,这个兼容性怎么样,IE早期的版本是否兼容? 支持(0)反对(0) http://pic.cnblogs.com/face/128345/20130726125603.png   

#8楼[楼主] 2017-01-19 13:13 仲强  

@ 为之则易
兼容性为IE10+ 其他都测试过了,只有safair没测试,因为我是window版的safair,测试没通过,其他我都测试了 支持(0)反对(0) http://pic.cnblogs.com/face/801930/20161012082514.png   

#9楼 2017-01-19 14:20 哈喽程序猿  

不错加油 支持(0)反对(0)   

#10楼[楼主] 2017-01-19 14:56 仲强  

@ 哈喽程序猿
一起加油!! 支持(0)反对(0) http://pic.cnblogs.com/face/801930/20161012082514.png   

#11楼 2017-01-19 15:00 代码改造世界  

不错,以后准备试试。 支持(0)反对(0) http://pic.cnblogs.com/face/602871/20150715150012.png   

#12楼[楼主] 2017-01-19 15:44 仲强  

@ 代码改造世界
嗯嗯 可以的 对后端压力分担等等 还是很有帮助的 支持(0)反对(0) http://pic.cnblogs.com/face/801930/20161012082514.png   

#13楼 2017-01-19 15:48 代码改造世界  

@ 仲强
主要是我想实现,web断点续传 支持(0)反对(0) http://pic.cnblogs.com/face/602871/20150715150012.png   

#14楼[楼主] 2017-01-19 15:59 仲强  

@ 代码改造世界
局部的断点续传是可以的,但是整体是不可以的。因为web是有沙盒的,只有在用户选择了文件之后才能操作文件,所以用户刷新浏览器之后,就没这个文件了,续传不了。但是局部是可以的,用户点击上传的时候点了暂停,客户端记录当时传输记录到哪个片段了记下来,然后继续重新上传 支持(0) 反对(0) http://pic.cnblogs.com/face/801930/20161012082514.png   

#15楼 2017-01-19 16:04 代码改造世界  

@ 仲强
只要能保证当次使用过程中断点续传就OK了。主要是应对网络不稳定的问题 支持(0)反对(0)http://pic.cnblogs.com/face/602871/20150715150012.png   

#16楼[楼主] 2017-01-19 16:06 仲强  

@ 代码改造世界
那就简单了,没次发送请求的时候 在error和timeout事件中做处理就好了呀,记录当前次数,重新上传 支持(0)反对(0)http://pic.cnblogs.com/face/801930/20161012082514.png   

#17楼 2017-01-20 14:13 通用C#系统架构  

能做好那么几件事情不容易。 支持(0)反对(0)http://pic.cnblogs.com/face/u35584.jpg   

#18楼[楼主]36076712017/1/20 15:32:49 2017-01-20 15:32 仲强  

@ 通用C#系统架构
嗯嗯 是的 对自己代码负责嘛 支持(0)反对(0)http://pic.cnblogs.com/face/801930/20161012082514.png    刷新评论刷新页面返回顶部 注册用户登录后才能发表评论,请 登录注册访问网站首页。 【推荐】超50万VC++源码: 大型工控、组态\仿真、建模CAD源码2018!
【腾讯云】小程序普惠节精美模板1元起
tencent0122 最新IT新闻:
· 乐视网复牌第二日再跌停 逾800万手卖单排队出逃
· 知名80后创业者茅侃侃离世 家中开煤气自杀
· 支付宝今年香港上市?马云终于发话了!
· 马云达沃斯论坛谈全球贸易:贸易不是武器,而是解决方案
· 两家机构数据显示:小米超三星 成印度手机老大
» 更多新闻... 阿里云C2-1208 最新知识库文章:
· 领域驱动设计在互联网业务开发中的实践
· 步入云计算
· 以操作系统的角度述说线程与进程
· 软件测试转型之路
· 门内门外看招聘
» 更多知识库文章...

导航

公告

昵称:仲强
园龄:2年5个月
粉丝:73
关注: 0 +加关注
< 2018年1月 >
31 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 1 2 3
4 5 6 7 8 9 10

统计

  • 随笔 - 58
  • 文章 - 0
  • 评论 - 209
  • 引用 - 0

搜索

 

常用链接

我的标签

随笔分类

随笔档案

积分与排名

  • 积分 - 32332
  • 排名 - 11378

最新评论

阅读排行榜

评论排行榜

推荐排行榜

Powered by:
博客园
Copyright © 仲强
评论