首页 / PHP / php之大文件分段上传、断点续传
php之大文件分段上传、断点续传
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了php之大文件分段上传、断点续传,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含20444字,纯文字阅读大概需要30分钟。
内容图文
![php之大文件分段上传、断点续传](/upload/InfoBanner/zyjiaocheng/1275/146af675203f476b97cb1152e65c5582.jpg)
前段时间做视频上传业务,通过网页上传视频到服务器。
视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制;2,请求时间过长,请求超时;3,传输中断,必须重新上传导致前功尽弃;
解决方案:
1 ,修改服务端上传的限制配置;Nginx 以及 PHP 的上传文件限制 不宜过大,一般5M 左右为好;
2 ,大文件分片,一片一片的传到服务端,再由服务端合并。这么做的好处在于一旦上传失败只是损失一个分片而已,不用整个文件重传,而且每个分片的大小可以控制在4MB以内,服务端限制在4M即可。
前端
Web 前端可使用HttpUploader6的大文件上传控件6;官网地址:http://t.cn/EyI6vHh
< div class ="section section6 section5">
< div class ="part1">< a href ="javascript:;" target ="_blank" class ="part1__btn"> 批量删除 </ a >< span class ="part1__txt">< em class ="part1__num" id ="upload_num"> 0 </ em > 个视频,共 < em class ="part1__num" id ="upload_size"> 0M </ em ></ span ></ div >
< table class ="section5__table">
< tbody id ="thelist">
< tr class ="thead">
< th class ="col1 allCkeck">< input type ="checkbox" name ="" class ="col1__checkBox"/> 视频名称 </ th >< th class ="col2"> 视频大小 </ th >< th class ="col3"> 视频分类 </ th >< th class ="col4"> 状态 </ th >< th class ="col5"> 进度 </ th >< th > 操作 </ th >
</ tr >
</ tbody >
</ table >
< div class ="selFile" id ="selFile">
< div id ="drag_tips">
< div id ="btns__add2"></ div >
< h2 class ="txt1"> 选择视频文件 </ h2 >
< span class ="txt2"> 或直接将文件拖拽至此窗口 </ span >
</ div >
</ div >
< div class ="btns">< span class ="btns__add" id ="btns__add"> + 添加视频文件 </ span >< span class ="btns__upload btns__upload-start" id ="uploadBtn">< i class ="btns__upload_icon"></ i > 开始上传视频 </ span ></ div >
</ div >
// 引入插件
< script type ="text/javascript" src ="media/js/lib/webuploader/js/webuploader.min.js"></ script >
upload.js
1 // 文件上传
2 jQuery( function () {
3 var $ = jQuery,
4 $list = $(‘#thelist‘),
5 $btn = $(‘#upload-start‘),
6 $thead = $(‘.thead‘),
7 $part_btn = $(‘.part1__btn‘), // 批量上传按钮
8 state = ‘pending‘,
9 fileCount = 0, // 上传文件总数
10 fileSize = 0, // 上传文件的总大小
11 // 上传按钮
12 $upload = $(‘#uploadBtn‘),
13 // 所有文件的进度信息,key为file id
14 percentages = {},
15 // 所有文件的md5,key为file id
16 md5Obj = {},
17 // 可能有pedding, ready, uploading, confirm, done.
18 state = ‘pedding‘,
19 uploader;
20
21 // 浏览器关闭提醒
22 window.is_confirm = false ;
23 $(window).bind(‘beforeunload‘, function (){
24 // 只有在标识变量is_confirm不为false时,才弹出确认提示
25 if (window.is_confirm !== false )
26 return ‘ 正在上传视频,该操作将丢失视频,是否继续?‘;
27 })
28
29 if ( !WebUploader.Uploader.support() ) {
30 alert( ‘Web Uploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级浏览器‘);
31 throw new Error( ‘WebUploader does not support the browser you are using.‘ );
32 }
33
34 $(".pop2 .btns__sure").click( function (){
35 $(‘.popup,.pop‘).hide();
36 });
37
38 uploader = WebUploader.create({
39 // 拖拽容器
40 dnd:‘#selFile‘,
41
42 // 不压缩image
43 resize: false ,
44
45 // swf 文件路径
46 swf: ‘/media/js/lib/webuploader/js/Uploader.swf‘,
47
48 // 文件接收服务端。
49 server: ‘/service/upload/upload_file‘,
50 //server:‘http://vod.test.4399sy.com/service/upload/ssl_upload_file‘,
51 formData: {
52 file_id: ‘file‘,
53 guid: new Date().getTime() + Math.ceil(Math.random()*100),
54 file_name:‘‘
55 },
56
57 // 选择文件的按钮。可选。
58 // 内部根据当前运行是创建,可能是input元素,也可能是flash.
59 pick: {
60 id:‘#btns__add‘,
61 innerHTML:"+ 添加视频文件"
62 },
63
64 // 开起分片上传。
65 chunked: true ,
66
67 // 如果要分片,分多大一片2M
68 chunkSize:2*1024*1024,
69
70 // 上传文件的类型
71 accept:{
72 title: ‘Videos‘,
73 extensions: ‘mp4,avi,flv‘,
74 mimeTypes: ‘video/*‘
75 },
76 // 验证文件总数量, 超出则不允许加入队列。
77 fileNumLimit: 10,
78 // 单个文件上传的大小限制 2G
79 fileSingleSizeLimit:2*1024*1024*1024,
80
81 });
82
83 // 添加文件具体函数
84 function addFile( file ){
85 var data = new Date(),
86 month = (data.getMonth()+1)<10 ? ‘0‘+(data.getMonth()+1) : (data.getMonth()+1),
87 day = data.getDate()<10 ? ‘0‘+ data.getDate(): data.getDate(),
88 time = data.getFullYear() + "-" + month + "-" + day,
89 $tr = $(‘<tr class="toBeUploaded" id="‘+file.id+‘"></tr>‘),
90 $td = $(‘<td class="col1"><input type="checkbox" name="" class="col1__checkBox"/><input type="text" value="‘+ file.name +‘" name="" class="name"/></td><td class="col2">‘+convert_size(file.size)+‘</td><td class="col3"><select class="class_id">‘+ class_options +‘</select></td><td class="col4"> 读取视频中</td><td class="col5">0%</td><td class="col6"><ul><li class="view"><a target="_blank" href="javascript:;">查看</a></li><li class="delete">删除</li></ul></td>‘).appendTo($tr),
91 $state = $tr.find(‘td.col4‘),
92 $prgress = $tr.find(‘td.col5‘),
93 $delbtn = $tr.find(‘li.delete‘);
94
95 $("#selFile").hide();
96
97 if ( file.getStatus() === ‘invalid‘ ) {
98 switch ( file.statusText ) {
99 case ‘exceed_size‘:
100 text = ‘ 文件大小超出‘;
101 break ;
102
103 case ‘interrupt‘:
104 text = ‘ 上传暂停‘;
105 break ;
106
107 default :
108 text = ‘ 上传失败,请重试‘;
109 break ;
110 }
111 showError(text);
112 } else {
113 // @todo lazyload
114 percentages[ file.id ] = [ file.size, 0 ];
115 file.rotation = 0;
116 }
117
118 file.on(‘statuschange‘, function ( cur, prev ) {
119 if ( prev === ‘progress‘ ) {
120 // 上传成功
121 } else if ( prev === ‘queued‘ ) {
122 // 开始上传
123 }
124
125 // 成功
126 if ( cur === ‘error‘ || cur === ‘invalid‘ ) {
127 console.log( file.statusText );
128 showError( file.statusText );
129 percentages[ file.id ][ 1 ] = 1;
130 } else if ( cur === ‘interrupt‘ ) {
131 showError( ‘interrupt‘ );
132 } else if ( cur === ‘queued‘ ) {
133 percentages[ file.id ][ 1 ] = 0;
134 } else if ( cur === ‘progress‘ ) {
135 // 正在上传
136
137 } else if ( cur === ‘complete‘ ) {
138 // 上传完成
139
140 }
141
142 $tr.removeClass( ‘state-‘ + prev ).addClass( ‘state-‘ + cur );
143 });
144 $delbtn.on(‘click‘, function (){
145 uploader.removeFile( file );
146 });
147 $tr.appendTo($list);
148 //$tr.insertAfter($thead);
149 }
150
151 // 负责view的销毁
152 function removeFile( file ) {
153 var $tr = $(‘#‘+file.id);
154
155 delete percentages[ file.id ];
156 $tr.off().find(‘.col6‘).off().end().remove();
157 }
158
159 function setState( val ) {
160 var file, stats;
161
162 if ( val === state ) {
163 return ;
164 }
165
166 $upload.removeClass( ‘state-‘ + state );
167 $upload.addClass( ‘state-‘ + val );
168 state = val;
169
170 switch ( state ) {
171 case ‘pedding‘:
172 uploader.refresh();
173 break ;
174
175 case ‘ready‘:
176 uploader.refresh();
177 break ;
178
179 case ‘uploading‘:
180 $upload.text( ‘ 暂停上传‘ );
181 break ;
182 case ‘paused‘:
183 $upload.text( ‘ 继续上传‘ );
184 break ;
185
186 case ‘confirm‘:
187 //$progress.hide();
188 $upload.text( ‘ 开始上传‘ ).addClass( ‘disabled‘ );
189
190 stats = uploader.getStats();
191 if ( stats.successNum && !stats.uploadFailNum ) {
192 setState( ‘finish‘ );
193 return ;
194 }
195 break ;
196 case ‘finish‘:
197 stats = uploader.getStats();
198 if ( stats.successNum ) {
199 alert( ‘ 上传成功‘ );
200 } else {
201 // 没有成功的图片,重设
202 state = ‘done‘;
203 location.reload();
204 }
205 break ;
206 }
207 }
208
209
210 // 当有文件添加进来的时候
211 uploader.on( ‘fileQueued‘, function ( file ) {
212 fileCount++;
213 fileSize += file.size;
214 $("#upload_num").text(fileCount);
215 $("#upload_size").text(convert_size(fileSize));
216 md5Obj[ file.id ] = ‘‘;
217 // 获取文件MD5 值
218 uploader.md5File( file )
219 // 及时显示进度
220 .progress( function (percentage) {
221 $( ‘#‘+file.id ).find(‘.col4‘).text(‘ 读取文件‘+parseInt(percentage*100)+"%");
222 })
223 // 完成
224 .then( function (val) {
225 console.log(‘md5 result:‘, val);
226 md5Obj[ file.id ] = val;
227 $( ‘#‘+file.id ).find(‘.col4‘).text(‘ 待上传‘);
228 setState( ‘ready‘ );
229 });
230 addFile( file );
231 });
232
233 // 删除文件
234 uploader.onFileDequeued = function ( file ) {
235 fileCount--;
236 fileSize -= file.size;
237 $("#upload_num").text(fileCount);
238 $("#upload_size").text(convert_size(fileSize));
239 if ( !fileCount ) {
240 setState( ‘pedding‘ );
241 }
242 removeFile( file );
243
244 };
245
246 // 添加“添加文件”的按钮,
247 uploader.addButton({
248 id: ‘#btns__add2‘,
249 label: ‘‘
250 });
251
252 // 文件上传过程中创建进度实时显示。
253 uploader.onUploadProgress = function ( file, percentage ) {
254 var $tr = $(‘#‘+file.id),
255 $percent = $tr.find(‘td.col5‘),
256 $state = $tr.find(‘td.col4‘);
257 percentage = parseInt(percentage*100);
258 if (! (percentage == 0 && percentage == 100)){
259 $state.text(" 正在上传");
260 }
261 $percent.text( percentage + "%")
262 percentages[ file.id ][ 1 ] = percentage;
263 };
264
265 // 上传前,请求服务端 判断文件是否已经上传过
266 uploader.on( ‘uploadStart‘, function ( file ) {
267 var type = ‘POST‘;
268 var url = ‘/service/upload/determine_video_exist‘;
269 var request_data = {
270 ‘md5‘: md5Obj[ file.id ],
271 ‘type‘:1
272 };
273 var success = function (r) {
274 uploader.upload( file );
275 console.log(r);
276 if (r.code == 1) {
277 uploader.skipFile( file );
278 $( ‘#‘+file.id ).find(‘.col4‘).text(‘ 视频已存在‘);
279 $( ‘#‘+file.id ).find(‘.col5‘).text(‘100%‘);
280 $(‘#‘+file.id).find(‘.view‘).find(‘a‘).attr(‘href‘,playmain +‘/?video_id=‘+ r.data.video_id);
281 $(‘.pop2 .video_game‘).text(" 所在游戏:"+r.data.game_name);
282 $(‘.pop2 .create_time‘).text(" 上传时间:"+r.data.create_time);
283 $(‘.pop‘).hide();
284 $(‘.pop2‘).show();
285 $(‘.popup‘).show();
286 } else if(r.code <= 0) {
287 showError(r.msg);
288 } else {
289
290 }
291 };
292 request(type, url, request_data, success);
293 });
294
295 // 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。
296 uploader.on(‘uploadBeforeSend‘, function (obj, data, headers) {
297 $tr = $("#"+data.id);
298 var file_name = $tr.find(".name").val();
299 var class_id = $tr.find("select.class_id").val();
300 var reg = /[1-9][0-9]*/g;
301 data.md5 = md5Obj[ obj.file.id ];
302 data.file_name = file_name;
303 data.class_id = class_id;
304 data.guid = data.guid + data.id.replace(/[^0-9]+/g, ‘‘);
305 });
306
307 uploader.on( ‘uploadSuccess‘, function ( file ,res) {
308 if (res.code == 1){
309 $( ‘#‘+file.id ).find(‘.col4‘).text(‘ 成功上传‘);
310 console.log(res);
311 $(‘#‘+file.id).find(‘.view‘).find(‘a‘).attr(‘href‘,playmain +‘/?video_id=‘+ res.data.video_id);
312 } else if(res.code == 2) {
313 $( ‘#‘+file.id ).find(‘.col4‘).text(‘ 视频已存在‘);
314 console.log(res);
315 $(‘#‘+file.id).find(‘.view‘).find(‘a‘).attr(‘href‘,playmain +‘/?video_id=‘+ res.data.video_id);
316 } else {
317 showError(res.msg);
318 }
319 });
320
321 uploader.on( ‘uploadError‘, function ( file,reason ) {
322 $( ‘#‘+file.id ).find(‘.col4‘).text(‘ 上传失败‘);
323 console.log(reason);
324 });
325
326 uploader.on( ‘uploadComplete‘, function ( file ) {
327 $( ‘#‘+file.id ).find(‘.progress‘).fadeOut();
328 });
329
330 uploader.on( ‘all‘, function ( type ) {
331 if ( type === ‘startUpload‘ ) {
332 state = ‘uploading‘;
333 } else if ( type === ‘stopUpload‘ ) {
334 state = ‘paused‘;
335 } else if ( type === ‘uploadFinished‘ ) {
336 state = ‘done‘;
337 }
338
339 if ( state === ‘uploading‘ ) {
340 window.is_confirm = true ;
341 $(‘.toBeUploaded‘).addClass("uploaded").removeClass("toBeUploaded");
342 $(‘input.name‘).attr("disabled","disabled");
343 $(‘input.col1__checkBox‘).hide();
344 $(‘input‘).attr("disabled","disabled");
345 $(‘select.class_id‘).attr("disabled","disabled");
346 $(‘.btns__add‘).remove();
347 $upload.addClass("btns__upload-ing").removeClass("btns__upload-start").html(‘<i class="btns__upload_icon"></i> 正在上传视频‘);
348
349 } else if(state === ‘done‘) {
350 window.is_confirm = false ;
351 console.log(" 上传完成");
352 $upload.addClass("btns__upload-start btns__upload-refresh").removeClass("btns__upload-ing").html(‘<i class="btns__upload_icon"></i> 开始上传视频‘);
353 }
354 });
355
356 /**
357 * 验证文件格式以及文件大小
358 */
359 uploader.on("error", function (type){
360 var msg = ‘‘
361 switch (type){
362 case "Q_TYPE_DENIED": msg = " 请上传mp4格式文件"; break ;
363 case "F_EXCEED_SIZE": msg = " 文件大小不能超过1G"; break ;
364 case "Q_EXCEED_NUM_LIMIT" : msg = " 一次最多能上传10个文件"; break ;
365 default : msg=‘‘;
366 }
367 if (msg != ‘‘){
368 showError(msg);
369 }
370 });
371
372 $part_btn.on(‘click‘, function (){
373 $(‘td .col1__checkBox‘).each( function (){
374 if ($( this ).is(‘:checked‘)){
375 var $tr = $( this ).parents(‘tr‘);
376 var id = $tr.attr(‘id‘);
377 uploader.removeFile( id );
378 }
379 });
380 });
381 $upload.on(‘click‘, function () {
382 var isbreak = false ;
383 $(".name").each( function (){
384 if (!$( this ).val()|| $( this ).val() == ‘‘){
385 isbreak = true ;
386 }
387 })
388 if (isbreak){
389 showError(" 文件名不能存在为空");
390 return ;
391 }
392 $("select.class_id").each( function (){
393 if (!$( this ).val()|| $( this ).val() == ‘‘){
394 isbreak = true ;
395 }
396 })
397 if (isbreak){
398 showError(" 分类不能为空,请先添加分类");
399 return ;
400 }
401 if ( $( this ).hasClass( ‘btns__upload-refresh‘ ) ) {
402 location.reload();
403 }
404 if ( $( this ).hasClass( ‘btns__upload-ing‘ ) ) {
405 return false;
406 }
407 var md5Ready = true ;
408 $.each(md5Obj, function (index,item){
409 if (!item || item==‘‘){
410 md5Ready = false ;
411 }
412 });
413 if (!md5Ready){
414 showError(‘ 文件尚未读取完成,请耐心等待‘);
415 return false;
416 }
417 if ( state === ‘ready‘ && md5Ready ) {
418 uploader.upload();
419 } else if ( state === ‘paused‘ ) {
420 uploader.upload();
421 } else if ( state === ‘uploading‘ ) {
422 uploader.stop();
423 }
424 });
425 $upload.addClass( ‘state-‘ + state );
426
427 });
后台(PHP)【仅分片上传相关代码】
1 public function action_upload_file(){
2 $file_id = R:: string (‘file_id‘, ‘file‘);
3 $keepFileName = R:: string (‘keepFileName‘, 0);
4 $unsize_change = R::numeric(‘unsize_change‘,0);
5 $id = R:: string (‘id‘); // 插件每上传一个视频自带id
6 $guid = R:: string (‘guid‘); // 标识视频
7 $chunks = R::numeric(‘chunks‘); // 分片数
8 $chunk = R::numeric(‘chunk‘); // 分片号
9 $file_name = R:: string (‘file_name‘);
10 $file = isset ( $_FILES [ $file_id ])? $_FILES [ $file_id ]:‘‘;
11 $md5 = R:: string (‘md5‘);
12 $this ->upload = new Common_Upload();
13
14 if ( empty ( $guid ) || empty ( $file_name ) || empty ( $md5 )){
15 $this ->response_msg(-1, ‘guid 或 file_name 或 md5 不能为空‘);
16 return ;
17 }
18
19 if ( empty ( $file [‘name‘])){
20 $this ->response_msg(-1, ‘ 请上传一个文件‘);
21 return ;
22 } else {
23 if ( $chunks ){
24 $res = $this ->upload->saveFile_chunks( $file , $chunks , $chunk , $guid );
25 if ( empty ( $res )){
26 $this ->response_msg(-2, ‘ 分片上传失败‘);
27 return ;
28 }
29
30 } else if($keepFileName){
31 $res = $this ->upload->saveFile_nochunks( $file , ‘‘, ‘‘, $keepFileName );
32 } else {
33 $res = $this ->upload->saveFile_nochunks( $file );
34 }
35 if ( empty ( $res )){
36 $err = $this ->upload->getError();
37 $this ->response_msg(-3, ‘ 上传文件出错!msg:‘. print_r ( $err , true ));
38 return ;
39 }
40 if ( $unsize_change ){
41 $size = $res [‘size‘];
42 } else {
43 $size = $this ->convert_size( $res [‘size‘]);
44 }
45
46 // 视频上传完成
47 if ( $chunks && $res [‘last_chunk‘]){
48 $domain = Kohana:: $config ->load(‘domain‘);
49 $video_domain = $domain [RUN_MOD][‘VIDEO‘];
50
51 if (! empty ( $file_name )){
52 $res [‘name‘] = $file_name ;
53 }
54 $video_data = array (
55 ‘video_name‘=> $file_name ,
56 ‘video_url‘=> $video_domain ."/". $res [‘path‘],
57 ‘size‘=> $res [‘size‘],
58 ‘create_time‘=> date (‘y-m-d H:i:s‘, time ()),
59 ‘update_time‘=> date (‘y-m-d H:i:s‘, time ()),
60 ‘duration‘=> $res [‘time‘],
61 ‘md5‘=> $md5
62 );
63 $video_mod = new Model_Videoinfo();
64 $video = $video_mod ->save_video( $video_data , $guid );
65 $res = array (
66 ‘path‘=> $res [‘path‘],
67 ‘chunks‘=> $chunks ,
68 ‘chunk‘=> $chunk ,
69 ‘size‘=> $size ,
70 ‘guid‘=> $guid ,
71 ‘video_id‘=> $video [0],
72 ‘file‘=> $file ,
73 ‘id‘=> $id
74 );
75 $this ->response_msg(1,‘ 视频上传成功‘, $res );
76 return ;
77 }
78 // 非分片上传
79 if (! $chunks ){
80 $domain = Kohana:: $config ->load(‘domain‘);
81 $video_domain = $domain [RUN_MOD][‘VIDEO‘];
82 if (! empty ( $file_name )){
83 $res [‘name‘] = $file_name ;
84 }
85 $video_data = array (
86 ‘video_name‘=> $file_name ,
87 ‘video_url‘=> $video_domain ."/". $res [‘path‘],
88 ‘size‘=> $res [‘size‘],
89 ‘create_time‘=> date (‘y-m-d H:i:s‘, time ()),
90 ‘update_time‘=> date (‘y-m-d H:i:s‘, time ()),
91 ‘duration‘=> $res [‘time‘],
92 ‘md5‘=> $md5
93 );
94 $video_mod = new Model_Videoinfo();
95 $video = $video_mod ->save_video( $video_data , $guid );
96 if ( empty ( $video )){
97 $this ->response_msg(-6, ‘ 视频信息保存失败‘);
98 return ;
99 }
100 $res = array (
101 ‘path‘=> $res [‘path‘],
102 ‘video_data‘=> $video_data ,
103 ‘size‘=> $size ,
104 ‘guid‘=> $guid ,
105 ‘video_id‘=> $video [0],
106 ‘file‘=> $file ,
107 ‘id‘=> $id
108 );
109 $this ->response_msg(1,‘ 视频上传成功‘, $res );
110 return ;
111 }
112 // 分片上传成功(未全部分片上传完成)
113 $res = array (
114 ‘chunks‘=> $chunks ,
115 ‘chunk‘=> $chunk ,
116 );
117 $this ->response_msg(2, ‘ 分片上传成功‘, $res );
118 }
119 }
1 /**
2 * 保存分片文件(注意先验证文件是否合法)
3 *
4 * @param array $file 单个文件
5 * @param string $attachdir 上传文件路径
6 * @param string $upload_type 上传文件类型
7 * @param bool $keepFileName 是否保持文件名,默认不不保持
8 * @return bool
9 */
10 public function saveFile_chunks($file,$chunks, $chunk, $guid)
11 {
12 if ( empty ( $guid ) || empty ( $file ) ){
13 return false;
14 }
15 $file_name = ( string ) $guid . $chunk ;
16 // 保存分片文件
17 $file_info = $this ->saveFile( $file , ‘‘, ‘‘, false , $file_name , true );
18 if ( $file_info ) {
19 $cache = Cache::instance(‘memcache‘);
20 // 记录已上传的分片编号,上传顺序并不是按编号顺序进行上传
21 $chunks_list_pre = $cache ->get( $guid );
22 if ( empty ( $chunks_list_pre )){
23 $strarr = array ();
24 for ( $i =0; $i < $chunks ; $i ++){
25 $strarr [] = $guid . $i ;
26 }
27 $cache ->set( $guid , $strarr ,60 * 60 * 24);
28 }
29 $file_path = $cache ->set( $guid . $chunk , $file_info [‘path‘],60 * 60 * 24);
30
31 $chunk_path_array = array ();
32 for ( $i =0; $i < $chunks ; $i ++){
33 if ( $cache ->get( $guid . $i )){
34 $chunk_path_array [ $i ] = $cache ->get( $guid . $i );
35 }
36 }
37 list ( $Y , $M , $D , $H , $I , $S ) = explode (‘-‘, date ("Y-m-d-H-i-s", time ()));
38 $file_info [‘chunks_path_count‘] = count ( $chunk_path_array );
39 $file_info [‘last_chunk‘] = false ;
40 if ( count ( $chunk_path_array ) == $chunks ) {
41 // 按目录类型存储
42 $dirType = substr ( $file_info [‘type‘], 1, strlen ( $file_info [‘type‘]));;
43 // 目录类型前面加上前缀url
44 $dirType = $this ->pre_url. $dirType ;
45 // 按年月二级存储
46 $month_file_path = $Y .‘/‘. $M ;
47 $saveName =‘upload/mp4/‘. $month_file_path .‘/original/‘. $guid . $file_info [‘type‘];
48 $join_file_name = $this ->attachDIR. $saveName ;
49 if (! is_dir ( $this ->attachDIR.‘upload/mp4/‘. $month_file_path .‘/original/‘)){
50 mkdir ( $this ->attachDIR.‘upload/mp4/‘. $month_file_path .‘/original/‘,0755, true );
51 }
52 if (! file_exists ( $join_file_name )){
53 $fp = fopen ( $join_file_name , "ab");
54 // 合并过程中对文件加锁,防止同时操作而出错
55 if ( flock ( $fp ,LOCK_EX)){
56 for ( $i = 0; $i < $chunks ; $i ++) {
57 $tmp_file = $this ->attachDIR . $chunk_path_array [ $i ];
58 $handle = fopen ( $tmp_file , "rb");
59 fwrite ( $fp , fread ( $handle , filesize ( $tmp_file )));
60 fclose ( $handle );
61 unset ( $handle );
62 unlink ( $tmp_file ); // 合并完毕的文件就删除
63 } // 组装分片
64 $cache ->delete( $guid );
65 for ( $i =0; $i < $chunks ; $i ++){
66 $cache ->delete( $guid . $i );
67 }
68 $time = $this ->getTime( $join_file_name , $file_info [‘type‘]);
69 $file_info [‘time‘] = $time ;
70 $file_info [‘path‘] = $saveName ;
71 $file_info [‘size‘] = filesize ( $join_file_name );
72 $file_info [‘last_chunk‘] = true ;
73
74 $model_mod = new Model_Base();
75 $model_mod ->disconnect();
76 $pid = pcntl_fork();
77 // 父进程和子进程都会执行下面代码
78 if ( $pid == -1) {
79 // 错误处理:创建子进程失败时返回-1.
80 die (‘could not fork‘);
81 } else if ($pid) {
82 $model_mod ->connect();
83 // 对上传完成的视频进行排队转码
84 $this ->thread( $join_file_name , $file_info [‘type‘], $guid );
85 // 父进程会得到子进程号,所以这里是父进程执行的逻辑
86 pcntl_wait( $status ); // 等待子进程中断,防止子进程成为僵尸进程。
87 } else {
88 return $file_info;
89 // 子进程得到的$pid为0, 所以这里是子进程执行的逻辑。
90 }
91
92 }
93 }
94
95 }
96 return $file_info;
97 } else {
98 $this ->error[] = ‘ 分片上传失败‘;
99 return false;
100 }
101 /*}}}*/
102 }
1 ,实现了分片上传;
2 ,同时在上传前检查视频md5 是否在库,如已存在可实现“秒传” 功能,即直接复制数据信息,指向同一个文件,不必再上传;
3 ,可实现断点续传,上传过程中中断;之前上传的分片已保留在服务器,只需重新上传尚未上传的分片即可;
原文:https://www.cnblogs.com/songsu/p/12033267.html
内容总结
以上是互联网集市为您收集整理的php之大文件分段上传、断点续传全部内容,希望文章能够帮你解决php之大文件分段上传、断点续传所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。