- 理解大文件上传的原理
大文件上传一般采用分片上传的方式,通过对大文件进行分割,分多个请求上传到服务器,最终由服务器将多个分片合并成一个完整的文件。这样做可以降低单个上传请求的大小,避免大文件上传时出现网络波动、服务器负载等问题。
- 实现思路
(1)前端实现
前端实现分两部分,一部分是将大文件分割成多个小文件,每个小文件在数据上传前进行MD5计算,确保服务器接收到的文件是完整的;另一部分是将分割后的小文件与相关信息一起发送到服务器进行上传。
(2)后端实现
后端逻辑包括处理分片上传的请求、存储上传的文件、合并分片等操作。
- 实现代码
以下是一个基于PHP实现的大文件分割分片上传的示例代码:
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
//获取上传的文件名与路径
$filename = $_POST['filename'];
$filepath = $_POST['filepath'];
$chunk = isset($_POST['chunk']) ? $_POST['chunk'] : 0; //获取当前分片编号
$chunkSize = isset($_POST['chunkSize']) ? $_POST['chunkSize'] : 0; //获取每个分片大小
$totalSize = isset($_POST['totalSize']) ? $_POST['totalSize'] : 0; //获取文件总大小
//服务器本地存储路径为uploads目录下的$filefolder路径
$filefolder = 'test';
//如果文件夹不存在则创建
if (!is_dir('uploads/'.$filefolder)) {
mkdir('uploads/'.$filefolder, 0755, true);
}
$file = fopen('uploads/'.$filefolder.'/'.$filename, 'ab'); //打开文件
//获取分片文件内容
$data = file_get_contents('php://input');
fwrite($file, $data); //将分片文件内容写入
fclose($file); //关闭文件
//如果已经上传完成,则进行文件合并
if (($chunk+1) * $chunkSize >= $totalSize) {
$fp = fopen($filepath, 'ab');
for ($i=0; $i <= $chunk; $i++) {
$handle = fopen('uploads/'.$filefolder.'/'.$filename, 'rb');
fseek($handle, $i * $chunkSize);
fwrite($fp, fread($handle, $chunkSize));
fclose($handle);
}
fclose($fp);
//处理完后删除分片文件
for ($i=0; $i <= $chunk; $i++) {
unlink('uploads/'.$filefolder.'/'.$filename.$i);
}
//显示上传成功的信息
echo json_encode(['status' => 'success', 'msg' => '上传成功']);
} else {
//显示当前分片以及上传进度的信息
echo json_encode(['status' => 'continue', 'msg' => '继续上传', 'chunk' => $chunk+1, 'chunkSize' => $chunkSize, 'totalSize' => $totalSize]);
}
}
?>
- 示例说明
(1) 上传分片文件
//分片上传文件
function upload() {
var chunkSize = 1024 * 1024; //每个分片大小
var file = document.getElementById('file').files[0]; //上传的文件
var filename = file.name; //获取文件名
var totalSize = file.size; //获取总大小
var currentChunk = 0; //当前传输的分片编号
var filePath = '/uploads/' + filename; //上传的文件完整路径
//获取分片文件MD5
var md5;
var fileReader = new FileReader();
fileReader.onloadend = function (event) {
var md5File = hex_md5(event.target.result);
md5 = md5File;
}
//分割文件并上传
var sliceFile = function() {
var start = currentChunk * chunkSize;
var end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
var filedata = file.slice(start, end); //获取当前分片内容
//当前分片进行MD5计算
fileReader.readAsBinaryString(filedata);
var formdata = new FormData();
formdata.append('chunk', currentChunk);
formdata.append('chunkSize', chunkSize);
formdata.append('totalSize', totalSize);
formdata.append('filename', filename);
formdata.append('filepath', filePath);
formdata.append('data', filedata);
$.ajax({
url: 'upload.php',
type: 'POST',
data: formdata,
cache: false,
processData: false,
contentType: false,
success: function (data) {
data = JSON.parse(data);
if (data.status === 'success') {
//上传完成,显示上传成功的信息
} else {
currentChunk = data.chunk; //获取下一个分片编号
//显示上传进度的信息
}
if (start + chunkSize < file.size) {
sliceFile(); //上传下一个分片
}
}
});
}
sliceFile(); //开始上传
}
(2) 合并分片文件
<?php
//获取上传的文件名与路径
$filename = $_POST['filename'];
$filepath = $_POST['filepath'];
$chunk = isset($_POST['chunk']) ? $_POST['chunk'] : 0; //获取当前分片编号
$chunkSize = isset($_POST['chunkSize']) ? $_POST['chunkSize'] : 0; //获取每个分片大小
$totalSize = isset($_POST['totalSize']) ? $_POST['totalSize'] : 0; //获取文件总大小
//服务器本地存储路径为uploads目录下的$filefolder路径
$filefolder = 'test';
//如果文件夹不存在则创建
if (!is_dir('uploads/'.$filefolder)) {
mkdir('uploads/'.$filefolder, 0755, true);
}
$file = fopen('uploads/'.$filefolder.'/'.$filename.$chunk, 'ab'); //打开文件
//获取分片文件内容
$data = file_get_contents('php://input');
fwrite($file, $data); //将分片文件内容写入
fclose($file); //关闭文件
//如果已经上传完成,则进行文件合并
if (($chunk+1) * $chunkSize >= $totalSize) {
$fp = fopen($filepath, 'ab');
for ($i=0; $i <= $chunk; $i++) {
$handle = fopen('uploads/'.$filefolder.'/'.$filename.$i, 'rb');
fseek($handle, $i * $chunkSize);
fwrite($fp, fread($handle, $chunkSize));
fclose($handle);
}
fclose($fp);
//处理完后删除分片文件
for ($i=0; $i <= $chunk; $i++) {
unlink('uploads/'.$filefolder.'/'.$filename.$i);
}
//显示上传成功的信息
echo json_encode(['status' => 'success', 'msg' => '上传成功']);
} else {
//显示当前分片以及上传进度的信息
echo json_encode(['status' => 'continue', 'msg' => '继续上传', 'chunk' => $chunk+1, 'chunkSize' => $chunkSize, 'totalSize' => $totalSize]);
}
?>
注:以上示例代码只是一个简单的实现,实际应用中需考虑更多的错误处理、文件重命名等问题。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:PHP大文件分割分片上传实现代码 - Python技术站