欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

PHP+JS實現(xiàn)大文件切片上傳功能實現(xiàn)實例源碼

 更新時間:2023年05月27日 15:19:13   投稿:wdc  
這篇文章主要介紹了PHP+JS實現(xiàn)大文件切片上傳功能實現(xiàn)實例源碼,需要的朋友可以參考下

近期公司的項目中,涉及到上傳大文件的問題,大文件上傳用普通表單上傳時出現(xiàn)的問題是,無法斷點續(xù)存,一但中途中斷上傳,就要重頭開始,這很明顯不是我們想要的,所以經(jīng)過一番查詢,學習了一下大文件分割上傳的方法。并且使用簡單的php做服務端處理程序實現(xiàn)一個功能demo,供以后回顧使用。本人也是初出茅廬的前端小白,記錄下各種功能的實現(xiàn)總結,代碼有錯誤的地方,請多多指正。

1.簡單文件上傳

普通表單上傳

表單上傳是我們經(jīng)常使用的功能,而且使用起來也是非常簡單,我們只需要聲明表單內(nèi)容類型為enctype="multipart/form-data",表明表單上傳文件的二進制數(shù)據(jù)。

點擊上傳按鈕,就可以將表單發(fā)送到服務器,并使用index.php接受到對應的表單數(shù)據(jù),存入$_GET/$_POST超級全局變量中,我們只需要使用move_uploaded_file方法,將接收到的文件數(shù)據(jù),存儲起來,就實現(xiàn)了文件上傳功能了。

$myfile = $_FILES['myfile'];
?//上傳路徑
?$path = "upload/" . $myfile['name']; 
?if(move_uploaded_file($myfile['tmp_name'], $path)){
?  echo "上傳成功";
?} else{
?  echo "上傳失敗";
?};

ajax模擬表單上傳文件

當我們有需求,需要異步提交表單或者需要對上傳文件做一定修改(例如:裁剪尺寸)時,普通的表單上傳就不能滿足我們的需求,因為我們無法修改表單的file值,這時候就需要ajax出場了。這里我們使用jQuery使用ajax更方便快捷。

我們需要做如下修改:

HTML

我們不需要配置form,只需要配置相應的ID,用于獲取DOM元素對象。

JQuery

注意,jQuery的ajax方法,會默認配置一些請求信息,所以我們需要重新配置放置jQuery的默認行為導致數(shù)據(jù)格式或請求頭信息出現(xiàn)問題。

這里的contentTypeprocessData為必須項。

$('#submitForm').on('click', function(e){
? ?// 阻止默認表單提交
? ?e.preventDefault();
??
? ?// 創(chuàng)建表單
? ?// 默認配置了enctype="multipart/form-data"
? ?var formData = new FormData();
? ?formData.append('myfile',$('#myFile')[0].files[0])
??
? ?// 提交表單
? ?$.ajax({
? ? ?type: "POST",
? ? ?url: 'post.php',
? ? ?data: formData,
? ? ?// 阻止jquery賦予默認屬性,使用FormData默認配置enctype="multipart/form-data"
? ? ?contentType: false,
? ? ?// 阻止jquery自動序列化數(shù)據(jù)
? ? ?processData: false,
? ? ?success: function(data){
? ? ? ?console.log('請求正常',data);
? ?  }
?  })
?})

2.大文件分割上傳

簡單上傳痛點

簡單上傳,使用表單提交文件到服務器時,如果網(wǎng)絡不好或者中途中斷,會使文件上傳失敗,試想一下如果要上傳文件很大,當你上傳到99%時,突然間中斷,又要重新上傳,那該有多崩潰,那時你可能電腦的想砸了。

實現(xiàn)思路

大文件上傳,實現(xiàn)的方法,就是將上傳文件的二進制文件通過分割的形式,逐個上傳到服務器,在上傳完成后,服務器再對文件進行拼接操作。

為了能識別上傳的數(shù)據(jù),是哪個文件,我們必須要擁有一個文件標識符,用于識別接收到的文件數(shù)據(jù)是屬于哪個文件的,以及可以實現(xiàn)避免重復上傳,實現(xiàn)秒傳功能等。

不要忘記由于是異步操作,而且操作的數(shù)據(jù)段大小不一,會導致整合時無法確認拼接熟悉怒,所以我們需要一個index標識數(shù)據(jù)段的位置。

通過初步整理,我們就需要以下的參數(shù)

文件唯一標識符

分割后數(shù)據(jù)段

分割數(shù)據(jù)段的順序索引值

經(jīng)過思考,我們可以建立兩個處理程序,來分別處理接受chunk數(shù)據(jù)段和合并chunk數(shù)據(jù)段。

file_getchunk.php

功能:將分割chunk數(shù)據(jù),整理并保存,此處我們用文件形式實現(xiàn)。

file_integration.php

功能:接收到整合通知,將數(shù)據(jù)段拼接,并生成文件。

PHP.ini配置

由于PHP默認配合中,限制了POST與上傳的大小,所以我們?yōu)榱藴y試,需要修改php.ini中的默認配置。

post_max_size = 50M
upload_max_filesize = 50M

HTML

<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
?<form id="myForm">
? ?<input type="file" name="myfile" id="myFile" />
? ?<input type="submit" value="上傳" id="submitForm"/>
?</form>

JQuery

獲取文件對象,文件標識符,分割文件,通過ajax發(fā)送切割好的blob數(shù)據(jù)段。

$('#submitForm').on('click', function(e){
? ?// 阻止默認表單提交
? ?e.preventDefault();
? ?var myfile = $('#myFile')[0].files[0];
? ?// 定義文件標識符 ?
? ?var fileId = getFileIdentifier(myfile);
? ?// 數(shù)據(jù)切片
? ?var chunks = fileSlice(myfile);
? ?// 發(fā)送分割數(shù)據(jù)段
?sendChunk(fileId, chunks);
?})

生成文件唯一標識getFileIdentifier()

此處可以使用md5,生成文件唯一的md5(相同文件md5相同),作為標識符。這里只初略的處理了一下文件標識。

function getFileIdentifier(file){
? ? ?// 獲取文件標識符
? ? ?return file.size + file.name;
?  }

分割方法fileSlice()

先將文件使用blob文件繼承的方法slice進行切割,生成blob字串。

function fileSlice(file, chunkSize = 1024*1024*0.2){
? ? // 1.初始化數(shù)據(jù)
? ? var totalSize = file.size;
? var start = 0;
? ? var end = start + chunkSize;
? ? var chunks = [];
? ? // 2.使用bolb提供的slice方法切片
? ? while(start < totalSize){
? ? ? var chunk = file.slice(start, end);
? ? ? chunks.push(chunk);
? ? ? start = end;
? ? ? end += chunkSize;
? ? }
? ? // 3.返回切片組chunk[]
? ? return chunks;
? }

發(fā)送chunk方法sendChunk()

使用ajax依次發(fā)送已經(jīng)分割好的chunk,并提供對應的數(shù)據(jù),請求file_getchunk.php進行處理。此處task列表,用于保證文件分隔符全部已經(jīng)完成上傳。

function sendChunk(id, chunks){
? ?// 逐個提交
? ?// 用于保證ajax發(fā)送完畢
? ?var task = [];
??
? ?chunks.forEach(function(chunk, index){
? ? ?var formData = new FormData();
? ? ?formData.append('fileId', id);
? ? ?formData.append('myFileChunk', chunk);
? ? ?formData.append('chunkIndex', index);
? ? ?$.ajax({
? ? ? ?type: "POST",
? ? ? ?url: 'file_getchunk.php',
? ? ? ?data: formData,
? ? ? ?contentType: false,
? ? ? ?processData: false,
? ? ? ?success: function(done){
? ? ? ? ?// 移除已完成任務
? ? ? ? ?task.pop();
? ? ? ? ?console.log(done,' 已完成');
? ? ? ? ?if (task.length === 0) {
? ? ? ? ? ?// 發(fā)送完畢,整合文件
? ? ? ? ? ?console.log('通知整合');
? ? ? ? ? ?makeFileIntegration(id, chunks.length);
? ? ? ?  }
? ? ?  }
? ?  })
? ? ?task.push('file Working');
?  })
?}

通知整合方法makeFileIntegration()

接收到整合通知,請求file_integration.php進行文件的整合處理。

function makeFileIntegration(id, size){
? ?// 通知已傳輸完成
? ?$.post(
? ? ?"file_integration.php",
? ?  {
? ? ? ?id: id,
? ? ? ?size: size
? ?  },
? ? ?function(data){
? ? ? ?console.log(data);
? ?  }
?  );
?}

PHP- file_getchunk.php

當PHP監(jiān)聽到請求時,獲取對應的數(shù)據(jù),生成文件夾,按照chunkIndex存儲數(shù)據(jù)段。

if(!is_dir('upload')){
? ?mkdir('upload', 0777);
?}
??
?$chunk = $_FILES['myFileChunk'];
?// 文件唯一標識
?$fileId = $_POST['fileId'];
?// 臨時文件夾名稱
?$length = strlen($fileId) - (strlen($fileId) - strpos($fileId, '.'));
?$filedir = substr($fileId, 0, $length);
??
?$chunkIndex = $_POST['chunkIndex'];
??
?$filepath = 'upload/' . $filedir;
??
?$filename = $filepath . '/' . $chunkIndex;
??
?if(!is_dir($filepath)){
? ?mkdir($filepath, 0777);
?}
?move_uploaded_file($chunk['tmp_name'], $filename);
??
?echo $chunkIndex;

PHP-file_integration.php

監(jiān)聽到整合請求,對文件夾下面的所有文件,進行依次拼接,并生成最終還原出來的文件。

$fileId = $_POST['id'];
?// 臨時文件夾名稱
?$length = strlen($fileId) - (strlen($fileId) - strpos($fileId, '.'));
?$filedir = substr($fileId, 0, $length);
??
?$size = $_POST['size'];
?$file = './upload/' . $fileId;
??
?// 創(chuàng)建最終文件
?if(!file_exists($file)){
? ?// 最終文件不存在,創(chuàng)建文件
? ?$myfile = fopen($file, 'w+');
? ?fclose($myfile);
?} 
?// 用增加方式打開最終文件
?$myfile = fopen($file, 'a');
??
?for ($i = 0; $i < $size; $i++) {
? ?// 單文件路徑
? ?$filePart = 'upload/' . $filedir . '/' . $i;
??
? ?if(file_exists($filePart)){
? ? ?$chunk = file_get_contents($filePart);
? ? ?// 寫入chunk
? ? ?fwrite($myfile, $chunk);
?  } else{
? ? ?echo "缺少Part$i 文件,請重新上傳";
? ? ?break;
?  }
?}
??
?fclose($myfile);
?echo "整合完成";
??

3.更進一步

大文件分割上傳功能已經(jīng)基本實現(xiàn),但是我們還可以擁有很多優(yōu)化的地方

1.斷點續(xù)存。

我們需要的文件已經(jīng)可以正常的分割上傳,服務端也可以正常接收切片,完成數(shù)據(jù)段切片的合并了。此時我們就可以進一步實現(xiàn)斷點續(xù)存了。

斷點續(xù)存,實現(xiàn)方法很簡單,我們只需要獲取到上傳完成的數(shù)據(jù)段切片信息,就可以判斷我們應該從哪個數(shù)據(jù)段開始繼續(xù)傳輸數(shù)據(jù)。

獲取已經(jīng)完成數(shù)據(jù)段切片的信息,我們可以使用前端保存或者服務端獲取。此處我們使用服務端接口檢測,返回數(shù)據(jù)缺失位置來實現(xiàn)斷點續(xù)存。

思路整理

我們要在上傳前,請求服務端查詢出中斷時的位置,利用位置信息,篩選上傳的數(shù)據(jù)段切片。

那么我們要增加的邏輯就是:

offset中斷位置信息

查詢中斷位置接口:file_get_breakpoint.php

實現(xiàn)

getFileBreakpoint()獲取文件斷點函數(shù)

此處要保證ajax執(zhí)行順序,才能正確獲取offset偏移量,實現(xiàn)思路有很多。此處只使用jquery提供的將ajax請求變?yōu)橥?,進行處理。

注:同步請求時,success函數(shù)返回值不可以直接return,要保存在一個變量中,在ajax請求外return才能生效。

// 獲取文件斷點
?function getFileBreakpoint(id, size){
? ?var offset = '';
? ?$.ajax({
? ? ?type:"post",
? ? ?url:"file_get_breakpoint.php",
? ? ?data: {
? ? ? ?id: id,
? ? ? ?size: size
? ?  },
? ? ?async: false,
? ? ?success:function(res){
? ? ? ?offset = parseInt(res);
? ?  }
?  })
? ?return offset;
?}

sendChunk()發(fā)送數(shù)據(jù)前獲取offset

// 上傳前,請求file_integration.php接口獲取數(shù)據(jù)段開始傳輸?shù)奈恢?
?var offset = getFileBreakpoint(id, chunks.length);

遍歷chunks發(fā)送數(shù)據(jù)段時,增加篩選邏輯

chunks.forEach(function(chunk, index){
? ? // ==============新增=================
? ? // 從offset開始傳輸
? ? if (index < offset) {
? ? ? return;
? ? }
? ? // ==============新增=================
? ? var formData = new FormData();
? ? formData.append('fileId', id);
? ? formData.append('myFileChunk', chunk);
? ? formData.append('chunkIndex', index);
? ? $.ajax({
? ? ? type: "POST",
? ? ? url: 'file_getchunk.php',
? ? ? data: formData,
? ? ? contentType: false,
? ? ? processData: false,
? ? ? success: function(done){
? ? ? ? task.pop();
? ? ? ? console.log(done,' 已完成');
? ? ? ? if (task.length === 0) {
? ? ? ? ? console.log('通知整合');
? ? ? ? ? makeFileIntegration(id, chunks.length);
? ? ? ? }
? ? ? }
? ? })
? ? task.push(index+' is Working');
? })

獲取中斷位置接口file_get_breakpoint.php

這里使用的獲取中斷位置的邏輯很簡單(不是最優(yōu)),只需要檢測文件夾是否存在,再依次檢測數(shù)據(jù)段是否缺失。缺失時返回缺失段的index,已存在返回chunks長度size,不存在時返回0

// 1.檢測數(shù)據(jù)文件是否存在(文件標識,數(shù)據(jù)段總數(shù))
?$fileId = $_POST['id'];
?$size = $_POST['size'];
?// 臨時文件夾名稱
?$length = strlen($fileId) - (strlen($fileId) - strpos($fileId, '.'));
?$filedir = substr($fileId, 0, $length);
??
?// 2.按順序檢測缺失的數(shù)據(jù)段的位置
?// 檢測是否存在文件夾
?if (is_dir("upload/$filedir")) {
? ?$offset = $size;
? ?// 檢測數(shù)據(jù)段缺失下標
? ?for ($i = 0; $i < $size; $i++) {
? ? ?$filepath = "upload/$filedir/$i";
? ? ?if(!file_exists($filepath)){
? ? ? ?// 缺失i部分
? ? ? ?$offset = $i;
? ? ? ?break;
? ?  }
?  }
? ?// 輸出偏移量
? ?echo $offset;
?} 
?else {
? ?// 是否存在已合并文件
? ?if(file_exists("upload/$fileId")){
? ? ?echo $size;
?  } else{
? ? ?// 文件尚未上傳
? ? ?echo 0;
?  }
?}

2.文件秒傳

文件秒傳的概念,按照我的理解,就是在上傳文件請求后,服務器端檢測數(shù)據(jù)庫中是否存在相同的文件,如果存在相同的文件,就可以告訴用戶上傳完成了。

此處在獲取offset后,增加一個判斷就可以實現(xiàn)

var offset = getFileBreakpoint(id, chunks.length);
?// 增加判斷
?if(chunks.length === offset) {
? ?console.log('文件已經(jīng)上傳完成');
? ?return;
?}

當然,這里僅僅是非常簡單的處理,我們還可以使用MD5來作為文件標識符,在在服務器端使用這個標識符是否存在相同文件。

3.MD5檢測文件完整性。

通過md5對文件加密,傳輸?shù)椒掌鞫?,服務器端實現(xiàn)合并后對文件再進行一次md5加密,比對兩串md5字串是否相同,就可以知道文件傳輸過程中是否完整。

3.上傳完成后,存儲數(shù)據(jù)段文件夾進行刪除操作。

我們最后做一步就是將臨時文件移除操作,在整合完成后,我們只需要在file_integration.php接口中,整合完成后,移除文件夾及其下面的所有文件。

function deldir($path){
? ? //如果是目錄則繼續(xù)
? ?if(is_dir($path)){
? ? ? ?//掃描一個文件夾內(nèi)的所有文件夾和文件并返回數(shù)組
? ? ?$p = scandir($path);
? ? ?foreach($p as $val){
? ? ? ?//排除目錄中的.和..
? ? ? ?if($val !="." && $val !=".."){
? ? ? ? ?//如果是目錄則遞歸子目錄,繼續(xù)操作
? ? ? ? ?if(is_dir($path.$val)){
? ? ? ? ? ?//子目錄中操作刪除文件夾和文件
? ? ? ? ? ?deldir($path.$val.'/');
? ? ? ? ? ?//目錄清空后刪除空文件夾
? ? ? ? ? ?@rmdir($path.$val.'/');
? ? ? ?  }else{
? ? ? ? ? ?//如果是文件直接刪除
? ? ? ? ? ?unlink($path.$val);
? ? ? ?  }
? ? ?  }
? ?  }
? ? ?// 刪除文件夾
? ? ?rmdir($path);
?  }
?}
?//刪除臨時文件夾
?deldir("upload/$filedir/");

4.總結

按照上述步驟,可以跟著實現(xiàn)簡單上傳、大文件分割上傳、斷點續(xù)存等知識,起碼下次遇到上傳文件,心里也有了點底氣。由于本人是前端小白,所以寫的代碼比較簡陋,只是實現(xiàn)了功能,還有許多可以優(yōu)化的地方,如果代碼有誤,還望指正。

以上就是PHP+JS實現(xiàn)大文件上傳功能實現(xiàn)(切片上傳)全過程的詳細內(nèi)容,更多關于PHP+JS實現(xiàn)大文件上傳功能實現(xiàn)的資料請關注腳本之家其它相關文章!

相關文章

最新評論