PHP大文件分割上传详解
这篇文章主要为大家详细介绍了PHP大文件分割上传,PHP分片上传,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
服务端为什么不能直接传大文件?跟php.ini里面的几个配置有关
upload_max_filesize = 2M
//PHP最大能接受的文件大小
post_max_size = 8M
//PHP能收到的最大POST值'
memory_limit = 128M
//内存上限
max_execution_time = 30
//最大执行时间
当然不能简单粗暴的把上面几个值调大,否则服务器内存资源吃光是迟早的问题。
解决思路
好在HTML5开放了新的FILE API,也可以直接操作二进制对象,我们可以直接在浏览器端实现文件切割,按照以前的做法就得用Flash的方案,实现起来会麻烦很多。
JS思路
1.监听上传按钮的onchange事件
2.获取文件的FILE对象
3.把文件的FILE对象进行切割,并且附加到FORMDATA对象中
4.把FORMDATA对象通过AJAX发送到服务器
5.重复3、4步骤,直到文件发送完。
PHP思路
1.建立上传文件夹
2.把文件从上传临时目录移动到上传文件夹
3.所有的文件块上传完成后,进行文件合成
4.删除文件夹
5.返回上传后的文件路径
DEMO代码
前端部分代码
<!doctype html>
<html lang=
"en"
>
<head>
<meta charset=
"UTF-8"
>
<meta name=
"viewport"
content=
"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
>
<meta http-equiv=
"X-UA-Compatible"
content=
"ie=edge"
>
<title>Document</title>
<style>
#progress{
width: 300px;
height: 20px;
box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);
border-radius:4px;
background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);
}
#finish{
background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);
background-size:40px 40px;
height: 100%;
}
form{
margin-top: 50px;
}
</style>
</head>
<body>
<p id=
"progress"
>
<p id=
"finish"
style=
"width: 0%;"
progress=
"0"
></p>
</p>
<form action=
"./upload.php"
>
<input type=
"file"
name=
"file"
id=
"file"
>
<input type=
"button"
value=
"停止"
id=
"stop"
>
</form>
<script>
var
fileForm = document.getElementById(
"file"
);
var
stopBtn = document.getElementById(
'stop'
);
var
upload =
new
Upload();
fileForm.onchange =
function
(){
upload.addFileAndSend(this);
}
stopBtn.onclick =
function
(){
this.value =
"停止中"
;
upload.stop();
this.value =
"已停止"
;
}
function
Upload(){
var
xhr =
new
XMLHttpRequest();
var
form_data =
new
FormData();
const
LENGTH = 1024 * 1024;
var
start = 0;
var
end
= start + LENGTH;
var
blob;
var
blob_num = 1;
var
is_stop = 0
//对外方法,传入文件对象
this.addFileAndSend =
function
(that){
var
file = that.files[0];
blob = cutFile(file);
sendFile(blob,file);
blob_num += 1;
}
//停止文件上传
this.stop =
function
(){
xhr.abort();
is_stop = 1;
}
//切割文件
function
cutFile(file){
var
file_blob = file.slice(start,
end
);
start =
end
;
end
= start + LENGTH;
return
file_blob;
};
//发送文件
function
sendFile(blob,file){
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(
'file_name'
,file.name);
xhr.open(
'POST'
,
'./upload.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 ) +
'%'
;
}
progressObj.style.width = 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);
}
}
</script>
</body>
</html>
PHP部分代码
<?php
class
Upload{
private
$filepath
=
'./upload'
;
//上传目录
private
$tmpPath
;
//PHP文件临时目录
private
$blobNum
;
//第几个文件块
private
$totalBlobNum
;
//文件块总数
private
$fileName
;
//文件名
public
function
__construct(
$tmpPath
,
$blobNum
,
$totalBlobNum
,
$fileName
){
$this
->tmpPath =
$tmpPath
;
$this
->blobNum =
$blobNum
;
$this
->totalBlobNum =
$totalBlobNum
;
$this
->fileName =
$fileName
;
$this
->moveFile();
$this
->fileMerge();
}
//判断是否是最后一块,如果是则进行文件合成并且删除文件块
private
function
fileMerge(){
if
(
$this
->blobNum ==
$this
->totalBlobNum){
$blob
=
''
;
for
(
$i
=1;
$i
<=
$this
->totalBlobNum;
$i
++){
$blob
.=
file_get_contents
(
$this
->filepath.
'/'
.
$this
->fileName.
'__'
.
$i
);
}
file_put_contents
(
$this
->filepath.
'/'
.
$this
->fileName,
$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'
].dirname(
$_SERVER
[
'DOCUMENT_URI'
]).
str_replace
(
'.'
,
''
,
$this
->filepath).
'/'
.
$this
->fileName;
}
}
else
{
if
(
file_exists
(
$this
->filepath.
'/'
.
$this
->fileName.
'__'
.
$this
->blobNum)){
$data
[
'code'
] = 1;
$data
[
'msg'
] =
'waiting for all'
;
$data
[
'file_path'
] =
''
;
}
}
header(
'Content-type: application/json'
);
echo
json_encode(
$data
);
}
//建立上传文件夹
private
function
touchDir(){
if
(!
file_exists
(
$this
->filepath)){
return
mkdir
(
$this
->filepath);
}
}
}
//实例化并获取系统变量传参
$upload
=
new
Upload(
$_FILES
[
'file'
][
'tmp_name'
],
$_POST
[
'blob_num'
],
$_POST
[
'total_blob_num'
],
$_POST
[
'file_name'
]);
//调用方法,返回结果
$upload
->apiReturn();
参考文章:http://blog.ncmem.com/wordpress/2023/11/02/php%e5%a4%a7%e6%96%87%e4%bb%b6%e5%88%86%e5%89%b2%e4%b8%8a%e4%bc%a0%e8%af%a6%e8%a7%a3/
欢迎入群一起讨论