当前位置:网站首页>从一次需求改良漫谈php文件分片上传
从一次需求改良漫谈php文件分片上传
2020-11-09 15:25:00 【gcudwork】
说下本文的前言,以前遇到过需要上传大文件比如视频和视频素材的压缩包的需求,需求方甚至要求G级文件的上传
我是该项目的承接人,原项目的流程是上传文件到服务器后调用ffmpeg给视频加水印,截图,生成预览视频,再用服务器上传oss,这样的方式很容易遇到客户端网络波动,服务器上传限制,php上传限制,服务器内存的影响,经常就是超时还根本没法传几百M的视频,而且上传到oss会占用服务器的上传带宽,ffmpeg处理视频同时会把服务器的效率降低到一个极低的程度
于是我进行了陆续的一些改良:使用阿里云的媒体处理服务来代替服务器的截图,水印添加和生成预览视频,中间过程之一的可见[https://my.oschina.net/u/3470006/blog/3104362],使用,又使用oss的客户端api直接上传到oss,除了需要服务器签名外就不需要服务器去和oss进行交互了,无需消耗大量带宽和高占用cpu,云服务很好地方便了我们实现各种需求
前些日子看到有提问说[如何解决上传大文件内存溢出以及超时问题,上传较大的文件比如几百兆的,会出现溢出和超时问题,如何解决这类问题],最简单的解决方案是改php配置,把upload_max_filesize,max_execution_time和max_input_time改大,这样就能简单直接解决问题
下面有人回答分片,看到这个我想起来其实我自己还没做过分片上传,就花了些时间尝试了下,其实也挺简单的
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>文件分片上传</title>
</head>
<body>
<input type="file" id="File">
<button id="Upload">上传</button>
<p id="Status"></p>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<script>
//2M的分片
let BlockSize=2*1048576;
let Url="Upload.php";
//用来对用户显示当前的状态提示信息
let StatusText=$("#Status");
//使用顺序队列来发送分片文件
function QueueSendFile(Url,File,BlockSize,Index,MaxNumber){
if(Index<MaxNumber){
let Data=new FormData();
Data.append("File",File.slice(Index*BlockSize,(Index+1)*BlockSize),File.name);
StatusText.text(`文件大小${File.size}字节,分片大小${BlockSize}字节,总共${MaxNumber}分片,当前正在上传第${Index+1}块分片`);
$.ajax(Url,{
data:Data,
type:"post",
//不要处理数据也不要指定类型
processData:false,
contentType:false,
success:function (Result) {
console.log(Result)
if(Result.Status===1){
StatusText.text(`文件大小${File.size}字节,分片大小${BlockSize}字节,总共${MaxNumber}分片,第${Index+1}块分片上传完成`);
Index++;
QueueSendFile(Url,File,BlockSize,Index,MaxNumber);
}
else if(Result.Status===0){
Index=MaxNumber;
StatusText.text("出现错误:"+Result.Message);
}
else{
Index=MaxNumber;
StatusText.text("服务器返回不正确的数据");
}
},
error:function (){
Index=MaxNumber;
StatusText.text("请求错误");
}
});
}
else{
StatusText.text("文件上传完成");
}
}
$("#Upload").click(function (){
let File=$("#File")[0].files;
if(File.length>0){
File=File[0];
QueueSendFile(Url,File,BlockSize,0,Math.ceil(File.size/BlockSize));
}
else{
StatusText.text("必须选择文件");
}
});
</script>
</body>
</html>
Upload.php
<?php
function return_data($Message,$IsSuccess=true){
header('content-type:application/json');
exit(json_encode(['Status'=>$IsSuccess?1:0,'Message'=>$Message]));
}
function error_return($Message){
return_data($Message,false);
}
function success_return($Message=''){
return_data($Message);
}
//应该使用正确的用户鉴权机制,这里用户鉴权不是主要内容所以手动设定
$UserId=1;
if(isset($_FILES['File'])){
$File=$_FILES['File'];
//检测创建保存的文件夹,为保证效率基础文件夹应一次性就创建完成
$BasePath=__DIR__.DIRECTORY_SEPARATOR.'Files'.DIRECTORY_SEPARATOR;
if(!file_exists($BasePath)){
mkdir($BasePath);
}
$UserPath=$BasePath.$UserId.DIRECTORY_SEPARATOR;
if(!file_exists($UserPath)){
mkdir($BasePath);
}
//我在这里采用的方案是直接每次追加文件内容
$SavePath=$UserPath.$File['name'];
if(!file_exists($SavePath)){
file_put_contents($SavePath,'');
}
file_put_contents($SavePath,file_get_contents($File['tmp_name']),FILE_APPEND);
success_return();
}
else{
error_return('参数错误');
}
其实写此文之前我也尝试过别的分片方案:
可以乱序同时上传的
由前端每次发送分片索引和总大小,php把上传的文件建立文件夹,分片文件按分片索引全放在里面,每次检测文件总大小和总大小是否相同,相同就执行合并分片文件的操作,但这个有个缺点,随着分片文件的增多每次计算大小会越来越慢,有两千多个分片时每个总处理时间都超过了100ms(有我电脑慢的原因)
可乱序但不支持同时上传的
大体与上面方案差不多,就是前段发送总大小改成了总数量,每次检测文件夹文件数量和总数量是否一致,仅仅获取文件数量会比上面快得多
这里再说下其它的几个问题:
合并分片操作费时如何处理?
可以在文件上传完成后单独请求执行分片合并操作,如果时间不是特别长可以同步完成,很长而且此类操作比较多就需要使用定时任务来执行合并分片的操作,更加及时些可以使用任务队列实现,前端可以简单地使用轮询来获取操作是否完成
如何保证上传文件的正确性?
在上传之前计算文件的哈希值,在进行合并分片或上传最后一个分片时将哈希值传给php,php来比对完整文件的哈希是否一致
版权声明
本文为[gcudwork]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/3470006/blog/4710040
边栏推荐
- MES系统在行业应用里区别于传统式管理
- 中国程序员 VS 日本程序员,满屏的羞羞!
- 我叫Mongo,收了「查询基础篇」,值得你拥有
- Leetcode algorithm (1)
- 实在是太棒了!阿里资深架构师20年经验整理分享ServiceMesh实战文档,涨薪就差这篇文章了!
- Cad2016 software installation tutorial
- cad教程 cad2016安装教程
- Cad2016 download autocad2016 download installation detailed tutorial CAD Download
- 博士在读时,把暗恋的师兄变成了老公是种怎样的体验?
- 高质量的缺陷分析:让自己少写 bug
猜你喜欢

5分钟GET我使用Github 5 年总结的这些骚操作!

MES system is different from traditional management in industry application

Service registration and discovery of go micro integration Nacos

JS method of judging object type_ How to use typeof_ How to use instanceof_ How to use constructor_ Object.prototype.toString How to use ()

Spark Learning (3) -- memory management and performance tuning

中国程序员 VS 日本程序员,满屏的羞羞!

最新版PyCharm 2020.3 :可实现结对编程,智能文本校对等|附下载体验

leetcode算法(1)

高质量的缺陷分析:让自己少写 bug

How can you be a big data worker with an annual salary of 40W if you don't work hard?
随机推荐
Hadoop learning (3) - Yarn
Application of EMQ X in the Internet of things platform of China Construction Bank
Kubernetes v1.19.3 kubeadm deployment notes (2)
CAD tutorial cad2016 installation course
同事笔记-小程序入坑点
【运维思考】如何做好云上运维服务?
CAD2020下载AutoCAD2020下载安装教程AutoCAD2020中文下载安装方法
使用Fastai开发和部署图像分类器应用
CAD2016软件安装教程
帮助企业摆脱困境,名企归乡工程师:能成功全靠有它!
CCF BDCI热门赛题:非结构化商业文本信息中隐私信息识别
Native地图与Web融合技术的应用与实践
Get this template, double your salary
高德全链路压测——语料智能化演进之路
js字符与ASCII码互转的方法
MES系统在行业应用里区别于传统式管理
Guest interview: Wang Jian
Method of conversion between JS character and ASCII code
H5 official account listens to events in the closed browser (left fork).
腾讯云服务器搭建wordpress网站的两种方式(详细图文新手版)