当前位置:网站首页>从一次需求改良漫谈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
边栏推荐
- Service registration and discovery of go micro integration Nacos
- 微服务框架 Go-Micro 集成 Nacos 实战之服务注册与发现
- Spark Learning (3) -- memory management and performance tuning
- 5分钟GET我使用Github 5 年总结的这些骚操作!
- Data consistency of cache
- CCF BDCI hot topic: privacy information recognition in unstructured business text information
- CAD2020下载AutoCAD2020下载安装教程AutoCAD2020中文下载安装方法
- H5 official account listens to events in the closed browser (left fork).
- I heard that you changed your registered residence overnight. How can you help yourself if you work like ping?
- CCF BDCI热门赛题:非结构化商业文本信息中隐私信息识别
猜你喜欢
Decrypting the future database design: implementation of mongodb's new storage engine wiredtiger (transaction)
A quick start to Shell Scripting
【分享】接口测试如何在post请求中传递文件
Embedded assembly in IOS
在Python中创建文字云或标签云
如何使用Camtasia制作动态动画场景?
程序员过高工资导致加班?应该降低程序员工资?网友:放过其他苦逼的程序员吧
spark学习(三)--内存管理和性能调优
JS method of judging object type_ How to use typeof_ How to use instanceof_ How to use constructor_ Object.prototype.toString How to use ()
CAD2020下载AutoCAD2020下载安装教程AutoCAD2020中文下载安装方法
随机推荐
A letter to myself
布客·ApacheCN 编程/后端/大数据/人工智能学习资源 2020.11
Embedded assembly in IOS
JS design pattern
International top journal radiology published the latest joint results of Huawei cloud, AI assisted detection of cerebral aneurysms
Performance comparison of serialization tools such as Jackson, fastjson, kryo, protostuff
MES系统在行业应用里区别于传统式管理
The worst hacker in history: stealing $1 billion of bitcoin without spending it for seven years, and finally being seized by the Department of justice
低功耗蓝牙单芯片为物联网助力
I heard that you changed your registered residence overnight. How can you help yourself if you work like ping?
AUTOCAD2020安装包&安装教程
5 minutes get I use GitHub's 5-year summary of these operations!
High quality defect analysis: let yourself write fewer bugs
Idea solves garbled Chinese output of YML configuration file
CAD tutorial cad2016 installation course
大厂面试系列(二):并发编程
在Python中创建文字云或标签云
瞧瞧,这样的『函数』才叫 Pythonic
cad教程 cad2016安装教程
CAD2016软件安装教程