<!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/jquery@3.5.1/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来比对完整文件的哈希是否一致
hp文件分片上传
最新推荐文章于 2024-07-15 14:55:08 发布