前两天有人在群里问文件分片上传如何实现,当时没多想就直接说js对文件进行分片,上传分片,后端接受分片,js判断分片上传完成,发起合并请求,后端合并文件即可。后来就用webuploader插件实现了一下,然后他问我可不可以帮他写一个不用插件的分片上传,所以才有了下面的文章。
前端代码
file.html
分片多线程上传--梦中程序员出品#info {
width: 400px;
height: 200px;
}
.percent_bg {
position: relative;
width: 400px;
height: 20px;
border: 1px solid #ccc;
}
.percent {
position: absolute;
width: 0%;
height: 100%;
background: #0b821c;
}
.percent_num {
position: absolute;
width: 100%;
height: 100%;
text-align: center;
}
文件分片多线程上传,上传成功后返回url
选择文件后自动上传
线程数:
1
2
3
5
10
// 文件分片信息
var blocksInfo = {
"bufferSize": 512 * 1024, // 512K为一片
"blocks": [], // 所有分片文件
"threadNum": 1, // 上传线程数
"filename": "" // 文件名字
};
// 线程信息
var threadInfo = {
"index": 0, // 准备上传的分片索引
"activeThread": 0, // 几个线程在工作
"sendBlocksNum": 0 // 分片上传完成个数
};
// 选择文件触发change事件
$("#file").change(function () {
var file = document.getElementById("file").files[0];
setBlocksInfo(file); // 设置分片信息
// 启动线程
var realThread = Math.min(blocksInfo.threadNum, blocksInfo.blocks.length); // 如果分片数小于线程则只运行分片数的线程
showInfo("分片完成,分了" + blocksInfo.blocks.length + "片,线程数为:" + realThread);
showInfo("------------");
for (var i = 0;i < realThread;i++) {
threadInfo.activeThread++;
// 应该用js的线程插件来控制线程,我们就不用了,我们知道ajax的异步就是用多线程发送请求的,我们使用ajax来模拟多线程,这块虽然函数是单线程,进入事件队列,但是运行到ajax则是多线程了
startThread(i);
}
});
// 设置分片信息
function setBlocksInfo(file)
{
blocksInfo.threadNum = $("#thread_num").val();
blocksInfo.filename = file.name;
var startByte = endByte = 0;
while (true) {
if (endByte + blocksInfo.bufferSize >= file.size) {
endByte = file.size;
} else {
endByte = startByte + blocksInfo.bufferSize;
}
var block = file.slice(startByte, endByte);
blocksInfo.blocks.push(block);
startByte = endByte;
if (endByte >= file.size) {
break;
}
}
}
// 显示运行信息
function showInfo(info)
{
var msg = $("#info").val() + info + "\r\n";
$("#info").val(msg);
var scrollTop = $("#info")[0].scrollHeight;
$("#info").scrollTop(scrollTop);
}
// 线程运行
function startThread(i)
{
if (threadInfo.index >= blocksInfo.blocks.length) {
showInfo("线程" + i + "结束");
threadInfo.activeThread--;
if (threadInfo.activeThread == 0) {
showInfo("------------");
showInfo("分片上传完成,正在处理分片");
combineBlocks(); // 发起合并分片请求
}
return;
}
uploadBlock(i, threadInfo.index); // 使用指定线程上传指定分片
threadInfo.index++; // 准备下一个分片
}
// 上传分片
function uploadBlock(i, index)
{
showInfo("线程" + i + "开始:上传" + index + "分片");
// 组装上传信息,ajax上传文件需要formdata
var fd = new FormData();
fd.append("index", index); // 上传分片序号
fd.append("file", blocksInfo.blocks[index]);
$.ajax({
url: "upload.php",
type: "post",
data: fd,
dataType: "json",
contentType: false, // 文件上传和参数传递请求头和请求体都不一样,ajax设置false就可以
processData: false, // 有文件上传,不对参数序列化
success: function (data) {
threadInfo.sendBlocksNum++; // 设置这个分片已经上传完成
showPercent(); // 上传进度条
showInfo("分片" + index + "上传完成,线程" + i + "即将上传下一个分片");
startThread(i); // 启动线程继续下一个分片上传
}
});
}
// 发起合并分片请求
function combineBlocks()
{
$.ajax({
url: "upload.php",
type: "post",
data: {"act": "combine", "blocks": blocksInfo.blocks.length, "filename": blocksInfo.filename},
dataType: "json",
success: function (data) {
showInfo("分片数据处理完成,任务结束,URL:" + data.url);
}
});
}
// 进度条
function showPercent()
{
var percent = parseInt(threadInfo.sendBlocksNum / blocksInfo.blocks.length * 100);
$(".percent").stop(true, true).animate({"width": percent + "%"}, 10);
$(".percent_num").html(percent + "%");
}
后端代码
upload.php
class Uploader
{
public $tmpPath = __DIR__ . '/tmp/'; // 分片目录
public $filePath = __DIR__ . '/file/'; // 合并分片目录,这俩个目录需手动创建,你也可以使用mkdir创建
public function upload()
{
if (isset($_POST['act']) && $_POST['act'] == 'combine') {
$blocks = $_POST['blocks'];
$filename =time() . $_POST['filename'];
$file = fopen($this->filePath . $filename, 'a+');
for ($i = 0;$i < $_POST['blocks'];$i++) {
$chunkFile = fopen($this->tmpPath . $i, 'r');
fwrite($file, fread($chunkFile, filesize($this->tmpPath . $i)));
fclose($chunkFile);
unlink($this->tmpPath . $i);
}
fclose($file);
$data = [
'url' => "http://" . $_SERVER['HTTP_HOST'] . '/file/' . $filename
];
} else {
$index = $_POST['index']; // 分片索引
move_uploaded_file($_FILES['file']["tmp_name"], $this->tmpPath . $index);
$data = [
'code' => 1
];
}
return json_encode($data);
}
}
echo (new Uploader)->upload();
有疑问可联系QQ305530751
梦中程序员系列教程