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

上次做了前端的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[]; //处理文件名称(去除.part*,还原真实文件名称) string newFileName = fileName.Substring(, 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 = ; i < allFile.Length; i++)
{
FileStream FileIn = new FileStream(allFile[i], FileMode.Open);
BinaryReader br = new BinaryReader(FileIn);
byte[] data = new byte[]; //流读取,缓存空间
int readLen = ; //每次实际读取的字节大小
readLen = br.Read(data,, data.Length);
bw.Write(data,, readLen);
//关闭输入流
FileIn.Close();
};
//关闭二进制写入
bw.Close();
FileOut.Close();
}
return int.Parse(count) + ;
}

以下为测试代码:

  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:https://github.com/GerryIsWarrior/ajax     点颗星星是我最大的鼓励,有什么问题可以博客、邮箱、github上留言

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

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

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

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

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

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

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

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

  1. 框架基础:ajax设计方案(三)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

    马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...

  2. ASP.NET 使用ajaxfileupload.js插件出现上传较大文件失败的解决方法(ajaxfileupload.js第一弹)

    在写这篇的时候本来想把标题直接写成报错的提示,如下: “SecurityError:Blocked a frame with origin "http://localhost:55080&q ...

  3. 前端通信:ajax设计方案(三)--- 集成ajax上传技术

    在此之前让我感慨一下现在的前端开发的氛围.我遇到好多人,给我的观念都是,这个东西这个框架有了,那个东西那个框架做了,前端嘛,学几个框架,这个拼凑一下那个拼凑一下就好了.其实我想问,东西都框架做了,那你 ...

  4. 前端通信:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码(改迭代已作废,移步迭代10)

    该迭代已作废,最新的请移步这里:https://www.cnblogs.com/GerryOfZhong/p/10726306.html 距离上一篇博客书写,又过去了大概几个月了,这段时间暂时离开了这 ...

  5. 前端通信:ajax设计方案(一)---集成核心请求

    报告,我要说话!xp被历史淘汰了,IE6 say goodbye了,太TM开心了,从此不要兼容IE6了,哈哈哈哈哈哈 报告,我要说话!IE这sb为啥不早点被杀掉呢,找工作听说要兼容IE,立马软了,唉唉 ...

  6. 框架基础:ajax设计方案(三)---集成ajax上传技术

    之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询.这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的l ...

  7. 框架基础:关于ajax设计方案(三)---集成ajax上传技术

    之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询.这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的l ...

  8. 前端通信:ajax设计方案(八)--- 设计请求池,复用请求,让前端通信快、更快、再快一点

    直接进入主题,本篇文章有点长,包括从设计阶段,到摸索阶段,再到实现阶段,最后全面覆盖测试阶段(包括数据搜集清洗),还有与主流前端通信框架进行对比PK阶段. 首先介绍一下一些概念: 1. 浏览器的并发能 ...

  9. 前端通信:ajax设计方案(六)--- 全局配置、请求格式拓展和优化、请求二进制类型、浏览器错误搜集以及npm打包发布

    距离上一次博客大概好多好多时间了,感觉再不搞点东西出来,感觉就废了的感觉.这段时间回老家学习驾照,修养,然后7月底来上海求职(面了4家,拿了3家office),然后入职同程旅游,项目赶进度等等一系列的 ...

随机推荐

  1. Object-C中方法

    //方法         //方法分了两种         //1.类方法,类调用,方法以+开头         //2.实例方法,对象调用,方法以-开头              //类方法和实例方 ...

  2. C++中的矩阵运算

    C++中的矩阵运算 1. 2阶矩阵的逆矩阵公式

  3. MFC中和定时器使用

    在MFC中和定时器相关的有三个函数: 1.设置定时器(定义一个定时器的属性):         SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBAC ...

  4. php学习之路-笔记分享20150327

    由于公司需要,不得已是php也学习并用了段时间做了两个项目,现也分享出笔记.需要源文档的留下邮箱,.

  5. 图书助手Alpha版使用说明

    一.产品介绍 我们做的是一个基于安卓的手机app,通过连接图书馆的数据库,实现查询图书馆的书目信息的功能. 二.软件运行 我们只做了安卓版本,需要在安卓环境下运行. 三.软件结构 本软件主要包括客户端 ...

  6. Python2.7升级至Python3.6

    Python2.7升级至Python3.6 今天在CentOS7.2上将python2.7升级至python3.6时遇到了诸多问题,下面将升级步骤以及解决方法一一列举. 1.安装Python3.6 安 ...

  7. js实现回车登陆

    2018-11-15 $(document).keydown(function (event) { if (event.keyCode == 13) { $("#LoginBtn" ...

  8. 理解go语言 协程之间的通讯

    go已经越来越被重视了,特别适合大型互联网公司基础服务的编写,高效,高并发,可以同时允许多个明星出轨,多个明星结婚 都不在话下,下面介绍下GO协程通讯的过程 直接上代码 package main im ...

  9. BitAdminCore框架更新日志20180523

    20180523更新内容 本次更新两个内容,一是增加视频处理功能,二是增加定时服务功能. 视频处理 定时服务 BitAdminCore框架,用最少的代码,实现最多的功能 本次新暂未发布,后续有空发布 ...

  10. log4net 未生成log 原因分析

    本文假定你对log4net的配置以及在代码中的使用都非常熟悉,但就是没有按预想的生成log文件,正当你抓耳挠腮之时,那以下原因很可能是你解决问题的办法: 1.log4net.dll是否生成到程序运行目 ...