Python实现大文件分片上传
来源:互联网 发布:数控车床编程实例 简单 编辑:程序博客网 时间:2024/05/17 02:18
转载请注明出处:http://blog.csdn.net/jinixin/article/details/77545140
方案一:开一个字符串,一边接收分片,一边将分片里的内容读取出来后添加到字符串末尾;全部分片接收完毕后,再将字符串写入新文件中。
方案二:创建一个文件,一边接收分片,一边将分片里的内容读取出来写入文件末尾。
3)后端如何区分不同用户的文件?如何区分同个文件不同分片的先后顺序?
通过http请求携带的task_id可以区分不同的文件。同个文件分片的先后顺序,可以通过http请求携带的chunk来区分。因此,task_id+chunk的组合可以在众多不同用户不同文件的分片中唯一标记某个分片,即某个文件的某个分片名称是task_id+chunk。
引言
想借着这篇文章简要谈谈WebUploader大文件上传与Python结合的实现。
WebUploader是百度团队对大文件上传的前端实现,而后端需要根据不同的语言自己实现。这里我采用Python语言的Flask框架搭建后端,配合使用Bootstrap前端框架渲染上传进度条,效果图在文章底部。WebUploader官网:点这里;WebUploader API:点这里 ;
实施
http协议并不是非常适合上传大文件,所以要考虑分片,即把大文件分割后再上传,而WebUploader所做的事,正是将一个大文件分片,一部分一部分的上传到服务器。在上传每个分片的http请求中,需要同时携带:
1)该文件的唯一标识:task_id;
2)该文件的分片总数:chunks;
3)该分片在该文件所有分片中的位置:chunk;
其中后两个WebUploader已经替我们自动上传了,而第一个task_id仅需要我们调用对应函数即可产生,然后再将其写入form-data。
WebUploader是一个前端框架,所以接收文件的部分需要我们自己实现,而我选用了Python和其的Flask框架。 后端要做的是接收这一大堆分片,然后将它们重新合并成一个文件,那么有如下三个问题:
WebUploader已经为我们解决了,详见下面代码。
<script type="text/javascript">$(document).ready(function() { var task_id = WebUploader.Base.guid(); //产生task_id,唯一标识该文件 var uploader = WebUploader.create({ server: '/upload/accept', //服务器接收并处理分片的url地址 formData: { task_id: task_id, //上传分片的http请求携带的数据 }, }); uploader.on('uploadSuccess', function(file) { //当该文件所有分片均上传成功时调用该函数 //上传的信息(文件唯一标识符,文件后缀名) var data = { 'task_id': task_id, 'ext': file.source['ext'], 'type': file.source['type'] }; $.get('/upload/complete', data); //ajax携带data向该url发请求 });});</script>
方案一:开一个字符串,一边接收分片,一边将分片里的内容读取出来后添加到字符串末尾;全部分片接收完毕后,再将字符串写入新文件中。
方案二:创建一个文件,一边接收分片,一边将分片里的内容读取出来写入文件末尾。
方案三:为每个分片创建一个新的临时文件来保存其内容;待全部分片上传完毕后,再按顺序读取所有临时文件的内容,将数据写入新文件中。
前两个方案看似不错,但其实有些问题。方案一因等待所有分片的时间过长容易造成内存溢出;由于分片不一定是按序上传,所以方案二也不行;故只能选择方案三了。
3)后端如何区分不同用户的文件?如何区分同个文件不同分片的先后顺序?
通过http请求携带的task_id可以区分不同的文件。同个文件分片的先后顺序,可以通过http请求携带的chunk来区分。因此,task_id+chunk的组合可以在众多不同用户不同文件的分片中唯一标记某个分片,即某个文件的某个分片名称是task_id+chunk。
关键代码
前端代码
<!DOCTYPE html><html><head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <script src="./static/jquery-1.11.1.min.js"></script> <script src="./static/bootstrap/js/bootstrap.min.js"></script> <script src="./static/webuploader/webuploader.min.js"></script> <link rel="stylesheet" type="text/css" href="./static/webuploader/webuploader.css"> <link rel="stylesheet" type="text/css" href="./static/bootstrap/css/bootstrap.min.css"></head><body> <div> <div id="picker">请选择</div> <!-- 上传按钮,必须指定id选择器的值 --> <div class="progress"> <!-- 进度条 --> <div class="progress-bar progress-bar-striped active" role="progressbar" style="width:0%;"></div> </div> </div> <script type="text/javascript"> $(document).ready(function() { var task_id = WebUploader.Base.guid(); //产生task_id var uploader = WebUploader.create({ //创建上传控件 swf: './static/webuploader/Uploader.swf', //swf位置,这个可能与flash有关 server: '/upload/accept', //接收每一个分片的服务器地址 pick: '#picker', //填上传按钮的id选择器值 auto: true, //选择文件后,是否自动上传 chunked: true, //是否分片 chunkSize: 20 * 1024 * 1024, //每个分片的大小,这里为20M chunkRetry: 3, //某分片若上传失败,重试次数 threads: 1, //线程数量,考虑到服务器,这里就选了1 duplicate: true, //分片是否自动去重 formData: { //每次上传分片,一起携带的数据 task_id: task_id, }, }); uploader.on('startUpload', function() { //开始上传时,调用该方法 $('.progress-bar').css('width', '0%'); $('.progress-bar').text('0%'); }); uploader.on('uploadProgress', function(file, percentage) { //一个分片上传成功后,调用该方法 $('.progress-bar').css('width', percentage * 100 - 1 + '%'); $('.progress-bar').text(Math.floor(percentage * 100 - 1) + '%'); }); uploader.on('uploadSuccess', function(file) { //整个文件的所有分片都上传成功,调用该方法 //上传的信息(文件唯一标识符,文件后缀名) var data = {'task_id': task_id, 'ext': file.source['ext'], 'type': file.source['type']}; $.get('/upload/complete', data); //ajax携带data向该url发请求 $('.progress-bar').css('width', '100%'); $('.progress-bar').text('上传完成'); }); uploader.on('uploadError', function(file) { //上传过程中发生异常,调用该方法 $('.progress-bar').css('width', '100%'); $('.progress-bar').text('上传失败'); }); uploader.on('uploadComplete', function(file) {//上传结束,无论文件最终是否上传成功,该方法都会被调用 $('.progress-bar').removeClass('active progress-bar-striped'); }); }); </script></body></html>
后端代码
@app.route('/', methods=['GET', 'POST'])def index(): # 一个分片上传后被调用 if request.method == 'POST': upload_file = request.files['file'] task = request.form.get('task_id') # 获取文件唯一标识符 chunk = request.form.get('chunk', 0) # 获取该分片在所有分片中的序号 filename = '%s%s' % (task, chunk) # 构成该分片唯一标识符 upload_file.save('./upload/%s' % filename) # 保存分片到本地 return rt('./index.html')@app.route('/success', methods=['GET'])def upload_success(): # 所有分片均上传完后被调用 task = request.args.get('task_id') ext = request.args.get('ext', '') upload_type = request.args.get('type') if len(ext) == 0 and upload_type: ext = upload_type.split('/')[1] ext = '' if len(ext) == 0 else '.%s' % ext # 构建文件后缀名 chunk = 0 with open('./upload/%s%s' % (task, ext), 'w') as target_file: # 创建新文件 while True: try: filename = './upload/%s%d' % (task, chunk) source_file = open(filename, 'r') # 按序打开每个分片 target_file.write(source_file.read()) # 读取分片内容写入新文件 source_file.close() except IOError: break chunk += 1 os.remove(filename) # 删除该分片,节约空间 return rt('./index.html')
结果
效果图
测试
三台计算机,一台做服务器,分别在另两台上同时各上传一本电影,大小为2.6G与3.8G;上传完毕后,两本电影均可在服务器上正常播放。未来
后续会对Django和Tornado框架做补充,如果有想要源码的朋友,可以移步这里。对该项目还会不断改进,如果你感兴趣,不妨star一下,谢谢。
阅读全文
1 0
- Python实现大文件分片上传
- webuploader 实现大文件 分片上传
- webuploader分片上传大文件
- webupload大文件分片上传
- 大文件分片上传服务器
- HttpUrlconnection 实现大文件分片断点上传,支持Https
- Java实现浏览器端大文件分片上传
- iOS大文件分片上传和断点续传
- 大文件分片断点上传显示进度条
- 大文件分片上传和断点续传
- iOS大文件分片上传和断点续传
- 基于NodeJs的Express及Webuploader实现大文件分片上传与合并(一)
- 前段WebUploader;JavaEE大文件分片上传接收
- js分片上传大文件,前端代码,亲测ok
- 百度Webuploader 大文件分片上传(.net接收)
- 基于NodeJs的Express及Webuploader实现大文件分片上传与合并(二)——组件化实现
- 大文件上传实现
- 使用xUtils3和RandomAccessFile来实现文件的分片上传
- jquery day1
- Pat(A) 1073. Scientific Notation (20)
- ERROR 1558 (HY000): Column count of mysql.user is wrong. Expected 43, found 42. Created with MySQL 5
- 第三次面试总结
- MFC常用空间类别及说明
- Python实现大文件分片上传
- C++有趣的问题
- 初识python
- java动态/静态绑定以及双分派
- spring framework test3种方式
- Codeforces 785D Anton and School
- python 练习一
- ubuntu环境下vlcj报错:A fatal error has been detected #C [libc.so.6+0x121024] _IO_file_underflow+0x64
- 关于WebService 的用法demo 总结