webuploader+PHP实现超大文件分片上传的功能
在開發《工單地圖》的時候,后臺平面圖上傳的功能需要處理10M以上大小的文件上傳,單個超大文件上傳的時候容易出現各種問題,后來采用了分片上傳的思路。將大文件分成多個小的文件分片,逐個上傳到服務器之后再合并還原成大文件。
效果演示
1、選擇待上傳的文件
2、點擊開始上傳按鈕
點擊開始上傳按鈕之后,會在前端將文件分割成固定大小的分片,逐個上傳到服務器,返回服務器上的相對路徑,從網絡請求日志里可以看到有4個上傳文件的請求,分別返回的路徑則在頁面通過表單顯示出來。
3、合并文件
點擊合并按鈕,向服務器發送合并請求,將上述4個分片文件合并為一個大的文件,并將相對路徑返回,完成大文件上傳的過程。
這是一種比較簡單的分片文件上傳方法。由于整個過程存在文件上傳、寫入、刪除和合并,而且是需要前端和后端配合才能完成整個上傳操作,因此在部署到生產環境時需要在代碼里針對于潛在的安全漏洞進行防護。
源代碼分析
1、?前端使用上傳組件是百度的webuploader。
<script type="text/javascript" src="<?=$base_url?>static/plugins/webuploader/webuploader.js"></script> <script>$list_map_json = $("#fileList_map_json"),$btn_map_json = $("#btn-star_map_json"),state = "pending",uploader_map_json; ?var uploader_map_json = WebUploader.create({auto: false,swf: '<?=$base_url?>static/plugins/webuploader/Uploader.swf', ?// 文件接收服務端。server: '<?=$base_url?>admin_map/webuploader',chunked:true,chunkSize:2000000,threads:1,// 選擇文件的按鈕。可選。// 內部根據當前運行是創建,可能是input元素,也可能是flash.pick: {id: '#filePicker_map_json',innerHTML: '點擊選擇文件',multiple:false},// 不壓縮image, 默認如果是jpeg,文件上傳前會壓縮一把再上傳!resize: false,// 只允許選擇圖片文件。accept: {title: 'file',extensions: 'json',mimeTypes: 'application/*'}});uploader_map_json.on( 'fileQueued', function( file ) {var $li = $('<div id="' + file.id + '" class="item">' +'<div class="pic-box"><img></div>'+'<div class="info">' + file.name + '</div>' +'<p class="state">等待上傳...</p>'+'</div>'),$img = $li.find('img');$list_map_json.append( $li );// 創建縮略圖// 如果為非圖片文件,可以不用調用此方法。// thumbnailWidth x thumbnailHeight 為 100 x 100var thumbnailWidth = 100;var thumbnailHeight = 100;uploader_map_json.makeThumb( file, function( error, src ) {if ( error ) {$img.replaceWith('<span>不能預覽</span>');return;}$img.attr( 'src', src );}, thumbnailWidth, thumbnailHeight );});// 文件上傳過程中創建進度條實時顯示。uploader_map_json.on( 'uploadProgress', function( file, percentage ) {var $li = $( '#'+file.id ),$percent = $li.find('.progress-box .sr-only');// 避免重復創建if ( !$percent.length ) {$percent = $('<div class="progress-box"><span class="progress-bar radius"><span class="sr-only" style="width:0%"></span></span></div>').appendTo( $li ).find('.sr-only');}$li.find(".state").text("上傳中");$percent.css( 'width', percentage * 100 + '%' );});var str;// 文件上傳成功,給item添加成功class, 用樣式標記上傳成功。uploader_map_json.on( 'uploadAccept', function( file,rep) {var response = eval(rep);if(response.code === 201){$( '#'+file.id ).addClass('upload-state-success').find(".state").text("已上傳");str = '<input name="'+response.message+'" id="'+response.message+'" value="'+response.message+'" class="form-control" type="text"> ';$("#input_group").append(str);}else{$( '#'+file.id ).addClass('upload-state-error').find(".state").text(response.message);}});// 文件上傳成功,給item添加成功class, 用樣式標記上傳成功。uploader_map_json.on( 'uploadSuccess', function( file,rep) {var response = eval(rep);if(response.code === 201){$( '#'+file.id ).addClass('upload-state-success').find(".state").text("已上傳");}else{$( '#'+file.id ).addClass('upload-state-error').find(".state").text(response.message);}});// 文件上傳失敗,顯示上傳出錯。uploader_map_json.on( 'uploadError', function( file ) {$( '#'+file.id ).addClass('upload-state-error').find(".state").text("上傳出錯");});// 完成上傳完了,成功或者失敗,先刪除進度條。uploader_map_json.on( 'uploadComplete', function( file ) {$( '#'+file.id ).find('.progress-box').fadeOut();});uploader_map_json.on('all', function (type) {if (type === 'startUpload') {state = 'uploading';} else if (type === 'stopUpload') {state = 'paused';} else if (type === 'uploadFinished') {state = 'done';} ?if (state === 'uploading') {$btn_map_json.text('暫停上傳');} else {$btn_map_json.text('開始上傳');}}); ?$btn_map_json.on('click', function () {if (state === 'uploading') {uploader_map_json.stop();} else {$("#input_group").empty();uploader_map_json.upload();}return false;}); </script> <script type="text/javascript"> $(function(){$(".select2").select2(); });function hebin() {var file_list = new Array();$("#input_group input[type='text']").each(function(){var file;file = $(this).val();file_list.push(file);});var file_list_post = new Array();file_list_post = {'file_list[]':file_list};$.ajax({type:"POST",url:"<?=$base_url?>admin_map/map_merge",data:file_list_post,traditional:true,dataType:"json",success:function(result){$("#map_json").val(result.map_json);}}); } </script>?文件分片的功能是上傳組件內置的,只需要在創建對象的時候配置即可。
var uploader_map_json = WebUploader.create({auto: false,swf: '<?=$base_url?>static/plugins/webuploader/Uploader.swf', ?// 文件接收服務端。server: '<?=$base_url?>admin_map/webuploader',chunked:true,chunkSize:2000000,threads:1,在代碼中將分片的大小配置為2M,線程設置為1,這樣可以控制上傳的順序和進度。
2、后端代碼用php實現
public function webuploader() {$config['upload_path'] = './svg/fjs';$config['allowed_types'] = 'json|';$config['max_size'] = 100000;$config['file_ext_tolower'] = TRUE;$config['overwrite'] = TRUE;$config['encrypt_name'] = TRUE;$this->load->library('upload', $config); $this->output->set_header('Content-Type: application/json;charset=utf-8');if ( ! $this->upload->do_upload('file')){$error = array('error' => $this->upload->display_errors());echo '{"jsonrpc" : "2.0", "code": 101, "message": "'.implode($error).'"}';print_r($this->upload->data());} else{$upload_data = $this->upload->data();$uploads = $upload_data['full_path'];$uploads = str_replace(str_replace('\\','/', getcwd()), '', $uploads);echo '{"jsonrpc" : "2.0", "code": 201, "message": "'.$uploads.'"}';}}public function map_merge() {$file_list_post = $this->input->post();$file_list = $file_list_post['file_list'];$file_tmp = $file_list[0];$file_path_root = dirname(dirname(dirname(__FILE__)));$file_path = dirname($file_tmp);$file_name_tmp = basename($file_tmp,'.json');$file_name_temp = md5($file_name_tmp);$file_name_return = $file_path.'/'.$file_name_temp.'.json';$file_name = $file_path_root.$file_path.'/'.$file_name_temp.'.json';$fp = fopen($file_name,"ab");foreach ($file_list as $key => $value){if ( ! empty($value)){$handle = fopen($file_path_root.$value,"rb");fwrite($fp, fread($handle, filesize($file_path_root.$value)));fclose($handle);unset($handle);unlink($file_path_root.$value);}}fclose($fp);$res = array('map_json'=>$file_name_return);$this->output->set_header('Content-Type: application/json;charset=utf-8');echo json_encode($res); }?后端由于使用的是框架,上傳和文件合并的代碼只需要簡單幾行。在上傳的時候嚴格限制上傳的后綴名,保存文件到服務器上時也將后見面死死的寫住,合并的時候前端傳過來的文件參數進行多重校驗,確保文件操作安全。
總結
以上是生活随笔為你收集整理的webuploader+PHP实现超大文件分片上传的功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WAF果真是个好东西
- 下一篇: 使用axios上传文件+参数