【实测4g文件上传处理成功】【修改版】php+html5实现无刷新上传,大文件分片上传,断点续传
备注:
由于源代码[https://blog.csdn.net/qq43599939/article/details/79762042] 不支持超大文件上传[上传超大文件浏览器卡死,后台php内存不足],做了部分代码修改,实测上传超大文件浏览器不卡死,后台php代码限制2M内存也能够实现大文件的上传和操作。
原文:
转自:https://blog.csdn.net/qq43599939/article/details/79762042 php+html5实现无刷新上传,大文件分片上传,断点续传
理清思路:
引入了两个概念:块(block)和片(chunk)。每个块由一到多个片组成,而一个资源则由一到多个块组成
块是服务端的永久数据存储单位,片则只在分片上传过程中作为临时存储的单位。服务端会以约一个月为单位周期性的清除上传后未被合并为块的数据片
实现过程:
将文件分割,分片上传,然后合并
前端核心code:index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title></title> <link rel="stylesheet" href="layui-v2.5.4/css/layui.css"> </head> <body> <div class="layui-body"> <form class="layui-form" action=""> <!-- 内容主体区域 --> <div style="padding: 15px;">文件上传 链接:https://blog.csdn.net/qq43599939/article/details/79762042</div> <!-- 进度条 --> <div class="layui-form-item"> <div class="layui-progress layui-progress-big" lay-filter="rate" lay-showPercent="true"> <div class="layui-progress-bar" lay-percent="0%"></div> </div> </div> <!-- 你的HTML代码 --> <div class="layui-form-item"> <div> <input type="file" name="" id="file"> </div> </div> <div class="layui-form-item"> <div id="divlog"> MD5: </div> </div> <div class="layui-form-item"> <div class="layui-btn-group"> <button id="upstart" class="layui-btn layui-btn-normal">开始上传</button> <button id="stop" class="layui-btn layui-btn-normal">停止</button> <button id="restart" class="layui-btn layui-btn-normal"><i class="layui-icon layui-icon-upload"></i>重新开始</button> </div> </div> </form> </div> <script src="layui-v2.5.4/layui.js"></script> <script> //注意进度条依赖 element 模块,否则无法进行正常渲染和功能性操作 layui.use('element', function () { var element = layui.element, $=layui.jquery; // element.progress('rate', '50%'); }); </script> <script> var fileForm = document.getElementById("file"); var upstartBtn = document.getElementById('upstart'); var stopBtn = document.getElementById('stop'); var startBtn = document.getElementById('restart'); var rate = document.getElementById('rate'); // 进度 var divlog = document.getElementById('divlog'); //--------------------------- const LENGTH = 10 * 1024 * 1; var start = 0; var end = start + LENGTH; var blob; var blob_num = 1; var is_stop = 0 var file = null; var md5filename = ''; //----------------------------- var upload_instance = new Upload(); fileForm.onchange = function () { // browserMD5File(fileForm.files[0], function (err, md5) { //如果文件大,md5值生成较慢 md5值生成后才能上传处理,自己优化下吧 // md5filename = md5; //如果需要刷新后也能断点,可利用cookie记录,自行完善 // divlog.innerHTML = '文件md5为:' + md5filename; // }); divlog.innerHTML = '文件md5为:不检测'; } upstartBtn.onclick = function () { upload_instance.addFileAndSend(fileForm); return false; } stopBtn.onclick = function () { upload_instance.stop(); return false; } startBtn.onclick = function () { upload_instance.start(); return false; } function Upload() { // var xhr = new XMLHttpRequest(); if (window.XMLHttpRequest){ // code for IE7+, Firefox, Chrome, Opera, Safari var xhr=new XMLHttpRequest(); }else{ // code for IE6, IE5 var xhr=new ActiveXObject("Microsoft.XMLHTTP"); } //对外方法,传入文件对象 this.addFileAndSend = function (that) { file = that.files[0]; blob = cutFile(file); sendFile(blob, file); blob_num += 1; } //停止文件上传 this.stop = function () { xhr.abort(); is_stop = 1; } this.start = function () { sendFile(blob, file); is_stop = 0; } //切割文件 function cutFile(file) { var file_blob = file.slice(start, end); start = end; end = start + LENGTH; return file_blob; }; //发送文件 function sendFile(blob, file) { // 网上案例这里的 FormData 会因为上传文件过大导致上传浏览器卡死,写在这里每次实例化减少内存消耗。 // 如果不修改,超过100M浏览器即卡死 // 修改后上传速度比之前要快并且不会卡死 var form_data = new FormData(); var total_blob_num = Math.ceil(file.size / LENGTH); form_data.append('file', blob); form_data.append('blob_num', blob_num); form_data.append('total_blob_num', total_blob_num); form_data.append('md5_file_name', md5filename); form_data.append('file_name', file.name); xhr.open('POST', './test2.php', false); xhr.onreadystatechange = function () { // var progress; // var progressObj = document.getElementById('finish'); if (total_blob_num == 1) { progress = '100%'; } else { progress = (Math.min(100, (blob_num / total_blob_num) * 100)).toFixed(2) + '%'; } console.log('progress-----' + progress); // progressObj.style.width = progress; // rate.innerHTML = progress; layui.element.progress('rate', progress); var t = setTimeout(function () { if (start < file.size && is_stop === 0) { blob = cutFile(file); sendFile(blob, file); blob_num += 1; } else { setTimeout(t); } }, 1000); } xhr.send(form_data); //每次清空 form_data = ""; } } </script> </body> </html>
后端code test2.php
<?php ini_set('memory_limit',-1); //要有足够大的内存来允许处理上传的文件 // ini_set('memory_limit',"2M"); //实测2M可以实现大文件[600M]的文件上传及拼接 set_time_limit(0); //防止超时 class Upload{ private $filepath = './upload'; //上传目录 private $tmpPath; //PHP文件临时目录 private $blobNum; //第几个文件块 private $totalBlobNum; //文件块总数 private $fileName; //文件名 private $md5FileName; public function __construct($tmpPath,$blobNum,$totalBlobNum,$fileName, $md5FileName){ $this->tmpPath = $tmpPath; $this->blobNum = $blobNum; $this->totalBlobNum = $totalBlobNum; $this->fileName = $fileName; // $this->fileName = $this->createName($fileName, $md5FileName); $this->moveFile(); $this->fileMerge(); } private function fileMerge(){ if($this->blobNum == $this->totalBlobNum){ // 此处文件不应该拼接,应该是追加入内 // 实测修改后php占用很少的内存也可以实现大文件的上传和拼接操作 for($i=1; $i<= $this->totalBlobNum; $i++){ $blob = ''; $blob = file_get_contents($this->filepath.'/'. $this->fileName.'__'.$i); file_put_contents($this->filepath.'/'. $this->fileName, $blob, FILE_APPEND ); unset($blob); } $this->deleteFileBlob(); } } //删除文件块 private function deleteFileBlob(){ for($i=1; $i<= $this->totalBlobNum; $i++){ @unlink($this->filepath.'/'. $this->fileName.'__'.$i); } } private function moveFile(){ $this->touchDir(); $filename = $this->filepath.'/'. $this->fileName.'__'.$this->blobNum; move_uploaded_file($this->tmpPath,$filename); } //API返回数据 public function apiReturn(){ if($this->blobNum == $this->totalBlobNum){ if(file_exists($this->filepath.'/'. $this->fileName)){ $data['code'] = 2; $data['msg'] = 'success'; $data['file_path'] = 'http://'.$_SERVER['HTTP_HOST'].str_replace('.','',$this->filepath).'/'. $this->fileName; } }else{ if(file_exists($this->filepath.'/'. $this->fileName.'__'.$this->blobNum)){ $data['code'] = 1; $data['msg'] = 'waiting'; $data['file_path'] = ''; } } header('Content-type: application/json'); echo json_encode($data); } private function touchDir(){ if(!file_exists($this->filepath)){ return mkdir($this->filepath); } } private function createName($fileName, $md5FileName){ return $md5FileName . '.' . pathinfo($fileName)['extension']; } } $upload = new Upload($_FILES['file']['tmp_name'],$_POST['blob_num'],$_POST['total_blob_num'],$_POST['file_name'],$_POST['md5_file_name']); $upload->apiReturn();
博 主 :夏秋初
地 址 :https://www.cnblogs.com/xiaqiuchu/articles/11029477.html
如果对你有帮助,可以点一下 推荐 或者 关注 吗?会让我的分享变得更有动力~
转载时请带上原文链接,谢谢。
地 址 :https://www.cnblogs.com/xiaqiuchu/articles/11029477.html
如果对你有帮助,可以点一下 推荐 或者 关注 吗?会让我的分享变得更有动力~
转载时请带上原文链接,谢谢。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义