大文件上传分治上传前端分片

首先,安装Vue3.0以上版本:

npm install -g @vue/cli

安装异步请求库axios:

npm install axios --save

随后,安装Ant-desgin:

npm i --save ant-design-vue@next -S

Ant-desgin虽然因为曾经的圣诞节“彩蛋门”事件而声名狼藉,但客观地说,它依然是业界不可多得的优秀UI框架之一。

接着在项目程序入口文件引入使用:

import{createApp}from'vue'importAppfrom'./App.vue'import{router}from'./router/index'importaxiosfrom'axios'importqsfrom'qs'importAntdfrom'ant-design-vue';import'ant-design-vue/dist/antd.css';constapp=createApp(App)app.config.globalProperties.axios=axios;app.config.globalProperties.upload_dir="https://localhost/static/";app.config.globalProperties.weburl="http://localhost:8000";app.use(router);app.use(Antd);app.mount('#app')

随后,参照Ant-desgin官方文档:https://antdv.com/components/overview-cn构建上传控件:

<a-upload@change="fileupload":before-upload="beforeUpload"><a-button><upload-outlined></upload-outlined>上传文件</a-button></a-upload>

注意这里需要将绑定的before-upload强制返回false,设置为手动上传:

beforeUpload:function(file){returnfalse;}

接着声明分片方法:

fileupload:function(file){varsize=file.file.size;//总大小  varshardSize=200*1024;//分片大小  this.shardCount=Math.ceil(size/shardSize);//总片数  console.log(this.shardCount);for(vari=0;i<this.shardCount;++i){//计算每一片的起始与结束位置  varstart=i*shardSize;varend=Math.min(size,start+shardSize);vartinyfile=file.file.slice(start,end);letdata=newFormData();data.append('file',tinyfile);data.append('count',i);data.append('filename',file.file.name);constaxiosInstance=this.axios.create({withCredentials:false});axiosInstance({method:'POST',url:'http://localhost:8000/upload/',//上传地址  data:data}).then(data=>{this.finished+=1;console.log(this.finished);if(this.finished==this.shardCount){this.mergeupload(file.file.name);}}).catch(function(err){//上传失败  });}}

具体分片逻辑是,大文件总体积按照单片体积的大小做除法并向上取整,获取到文件的分片个数,这里为了测试方便,将单片体积设置为200kb,可以随时做修改。

随后,分片过程中使用Math.min方法计算每一片的起始和结束位置,再通过slice方法进行切片操作,最后将分片的下标、文件名、以及分片本体异步发送到后台。

当所有的分片请求都发送完毕后,封装分片合并方法,请求后端发起合并分片操作:

mergeupload:function(filename){this.myaxios(this.weburl+"/upload/","put",{"filename":filename}).then(data=>{console.log(data);});}

至此,前端分片逻辑就完成了。

后端异步IO写入

为了避免同步写入引起的阻塞,安装aiofiles库:

pip3 install aiofiles

aiofiles用于处理asyncio应用程序中的本地磁盘文件,配合Tornado的异步非阻塞机制,可以有效的提升文件写入效率:

importaiofiles# 分片上传  classSliceUploadHandler(BaseHandler):asyncdefpost(self):file=self.request.files["file"][0]filename=self.get_argument("filename")count=self.get_argument("count")filename='%s_%s'%(filename,count)# 构成该分片唯一标识符  contents=file['body']#异步读取文件  asyncwithaiofiles.open('./static/uploads/%s'%filename,"wb")asf:awaitf.write(contents)return{"filename":file.filename,"errcode":0}

这里后端获取到分片实体、文件名、以及分片标识后,将分片文件以文件名_分片标识的格式异步写入到系统目录中,以一张378kb大小的png图片为例,分片文件应该顺序为200kb和178kb,如图所示:

[图片上传失败...(image-1affef-1658759510726)]

当分片文件都写入成功后,触发分片合并接口:

importaiofiles# 分片上传  classSliceUploadHandler(BaseHandler):asyncdefpost(self):file=self.request.files["file"][0]filename=self.get_argument("filename")count=self.get_argument("count")filename='%s_%s'%(filename,count)# 构成该分片唯一标识符  contents=file['body']#异步读取文件  asyncwithaiofiles.open('./static/uploads/%s'%filename,"wb")asf:awaitf.write(contents)return{"filename":file.filename,"errcode":0}asyncdefput(self):filename=self.get_argument("filename")chunk=0asyncwithaiofiles.open('./static/uploads/%s'%filename,'ab')astarget_file:whileTrue:try:source_file=open('./static/uploads/%s_%s'%(filename,chunk),'rb')awaittarget_file.write(source_file.read())source_file.close()exceptExceptionase:print(str(e))breakchunk=chunk+1self.finish({"msg":"ok","errcode":0})

这里通过文件名进行寻址,随后遍历合并,注意句柄写入模式为增量字节码写入,否则会逐层将分片文件覆盖,同时也兼具了断点续写的功能。有些逻辑会将分片个数传入后端,让后端判断分片合并个数,其实并不需要,因为如果寻址失败,会自动抛出异常并且跳出循环,从而节约了一个参数的带宽占用。

轮询服务

在真实的超大文件传输场景中,由于网络或者其他因素,很可能导致分片任务中断,此时就需要通过降级快速响应,返回托底数据,避免用户的长时间等待,这里我们使用基于Tornado的Apscheduler库来调度分片任务:

pip install apscheduler

随后编写job.py轮询服务文件:

fromdatetimeimportdatetimefromtornado.ioloopimportIOLoop,PeriodicCallbackfromtornado.webimportRequestHandler,Applicationfromapscheduler.schedulers.tornadoimportTornadoScheduler      scheduler=Nonejob_ids=[]# 初始化  definit_scheduler():globalscheduler      scheduler=TornadoScheduler()scheduler.start()print('[Scheduler Init]APScheduler has been started')# 要执行的定时任务在这里  deftask1(options):print('{} [APScheduler][Task]-{}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'),options))classMainHandler(RequestHandler):defget(self):self.write('<a href="/scheduler?job_id=1&action=add">add job</a><br><a href="/scheduler?job_id=1&action=remove">remove job</a>')classSchedulerHandler(RequestHandler):defget(self):globaljob_ids          job_id=self.get_query_argument('job_id',None)action=self.get_query_argument('action',None)ifjob_id:# add  if'add'==action:ifjob_idnotinjob_ids:job_ids.append(job_id)scheduler.add_job(task1,'interval',seconds=3,id=job_id,args=(job_id,))self.write('[TASK ADDED] - {}'.format(job_id))else:self.write('[TASK EXISTS] - {}'.format(job_id))# remove  elif'remove'==action:ifjob_idinjob_ids:scheduler.remove_job(job_id)job_ids.remove(job_id)self.write('[TASK REMOVED] - {}'.format(job_id))else:self.write('[TASK NOT FOUND] - {}'.format(job_id))else:self.write('[INVALID PARAMS] INVALID job_id or action')if__name__=="__main__":routes=[(r"/",MainHandler),(r"/scheduler/?",SchedulerHandler),]init_scheduler()app=Application(routes,debug=True)app.listen(8888)IOLoop.current().start()

每一次分片接口被调用后,就建立定时任务对分片文件进行监测,如果分片成功就删除分片文件,同时删除任务,否则就启用降级预案。

作者:刘悦的技术博客

链接:https://www.jianshu.com/p/acf3e12c99d2

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,242评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,953评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,299评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,709评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,723评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,236评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,629评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,594评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,135评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,156评论 3 345
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,285评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,914评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,600评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,073评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,203评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,798评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,339评论 2 365

推荐阅读更多精彩内容