集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

概念 

  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;
        }

原文:https://www.cnblogs.com/GerryOfZhong/p/6295211.html

展开阅读全文

没有更多推荐了,返回首页