本文目录导读:

这里为您提供一个完整的 PHP 后端处理 WebUploader 上传的解决方案,包含单文件、多文件上传以及分片上传的处理逻辑。
基础配置与公共函数
<?php
/**
* WebUploader PHP 后端处理类
*/
class UploadHandler {
private $config;
public function __construct($config = []) {
// 默认配置
$this->config = array_merge([
'upload_dir' => './uploads/', // 上传保存目录
'max_size' => 20 * 1024 * 1024, // 最大文件大小(20MB)
'allow_types' => ['jpg', 'jpeg', 'png', 'gif', 'zip', 'pdf'], // 允许的文件类型
'is_trim' => true, // 是否去除扩展名中的空格
], $config);
// 检查目录
if (!file_exists($this->config['upload_dir'])) {
@mkdir($this->config['upload_dir'], 0777, true);
}
}
/**
* 统一返回JSON格式
*/
private function jsonResponse($data, $status = true) {
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'success' => $status,
'data' => $data
]);
exit;
}
/**
* 获取文件扩展名
*/
private function getExtension($filename) {
$ext = pathinfo($filename, PATHINFO_EXTENSION);
return strtolower($ext);
}
/**
* 检查文件类型
*/
private function checkType($ext) {
if ($this->config['is_trim']) {
$ext = trim($ext);
}
return in_array($ext, $this->config['allow_types']);
}
/**
* 生成唯一文件名
*/
private function generateUniqueName($ext) {
$name = date('YmdHis') . '_' . uniqid() . '_' . rand(1000, 9999);
return $name . '.' . $ext;
}
/**
* 创建日期目录
*/
private function createDateDir() {
$year = date('Y');
$month = date('m');
$dir = $this->config['upload_dir'] . $year . '/' . $month . '/';
if (!file_exists($dir)) {
@mkdir($dir, 0777, true);
}
return $dir;
}
/**
* 处理上传的主方法
*/
public function handleUpload() {
$action = isset($_POST['action']) ? $_POST['action'] : 'upload';
switch ($action) {
case 'upload': // 普通上传
$result = $this->processUpload();
break;
case 'chunk': // 分片上传
$result = $this->processChunkUpload();
break;
default:
$this->jsonResponse(['msg' => '未知操作'], false);
}
$this->jsonResponse($result);
}
/**
* 普通文件上传
*/
private function processUpload() {
if (!isset($_FILES['file'])) {
return ['msg' => '没有文件上传'];
}
$file = $_FILES['file'];
// 检查错误码
if ($file['error'] !== UPLOAD_ERR_OK) {
$errorMsg = $this->uploadErrorMsg($file['error']);
return ['msg' => $errorMsg];
}
// 检查文件大小
if ($file['size'] > $this->config['max_size']) {
return ['msg' => '文件太大,最大支持 ' . ($this->config['max_size'] / 1024 / 1024) . 'MB'];
}
// 检查类型
$ext = $this->getExtension($file['name']);
if (!$this->checkType($ext)) {
return ['msg' => '不支持的文件类型: ' . $ext];
}
// 生成文件名和路径
$uploadDir = $this->createDateDir();
$fileName = $this->generateUniqueName($ext);
$filePath = $uploadDir . $fileName;
// 移动文件
if (!move_uploaded_file($file['tmp_name'], $filePath)) {
return ['msg' => '文件保存失败'];
}
// 返回文件信息
return [
'originalName' => $file['name'],
'fileName' => $fileName,
'filePath' => $filePath,
'fileSize' => $file['size'],
'fileType' => $ext,
'uploadTime' => date('Y-m-d H:i:s')
];
}
/**
* 分片上传处理
*/
private function processChunkUpload() {
// 分片信息
$chunk = isset($_POST['chunk']) ? intval($_POST['chunk']) : 0; // 当前第几分片
$chunks = isset($_POST['chunks']) ? intval($_POST['chunks']) : 1; // 总分片数
// 文件信息
$originalName = isset($_POST['name']) ? $_POST['name'] : 'unknown';
$file = isset($_FILES['file']) ? $_FILES['file'] : null;
if (!$file) {
return ['msg' => '没有文件数据'];
}
// 检查文件错误
if ($file['error'] !== UPLOAD_ERR_OK) {
return ['msg' => '分片上传失败'];
}
// 检查总大小(可选,需要前端提供)
$totalSize = isset($_POST['size']) ? intval($_POST['size']) : 0;
if ($totalSize > $this->config['max_size']) {
return ['msg' => '文件太大'];
}
// 临时分片保存目录
$tempDir = $this->config['upload_dir'] . 'temp/';
if (!file_exists($tempDir)) {
@mkdir($tempDir, 0777, true);
}
// 生成临时文件夹名(基于文件名和hash,这里简单处理)
$tempFolder = $tempDir . md5($originalName . $totalSize);
if (!file_exists($tempFolder)) {
@mkdir($tempFolder, 0777, true);
}
// 保存分片
$chunkFile = $tempFolder . '/' . $chunk;
if (!move_uploaded_file($file['tmp_name'], $chunkFile)) {
return ['msg' => '分片保存失败'];
}
// 检查是否所有分片都已上传
if ($this->isUploadComplete($tempFolder, $chunks)) {
// 合并分片
$result = $this->mergeChunks($tempFolder, $chunks, $originalName);
return $result;
}
return [
'chunk' => $chunk,
'chunks' => $chunks,
'msg' => '分片上传成功'
];
}
/**
* 检查分片是否全部上传完成
*/
private function isUploadComplete($tempFolder, $chunks) {
for ($i = 0; $i < $chunks; $i++) {
if (!file_exists($tempFolder . '/' . $i)) {
return false;
}
}
return true;
}
/**
* 合并分片
*/
private function mergeChunks($tempFolder, $chunks, $originalName) {
// 获取文件扩展名
$ext = $this->getExtension($originalName);
if (!$this->checkType($ext)) {
return ['msg' => '不支持的文件类型: ' . $ext];
}
// 创建最终上传目录
$uploadDir = $this->createDateDir();
$fileName = $this->generateUniqueName($ext);
$filePath = $uploadDir . $fileName;
// 写入文件
$file = fopen($filePath, 'wb');
if (!$file) {
return ['msg' => '无法创建最终文件'];
}
// 按顺序合并分片
for ($i = 0; $i < $chunks; $i++) {
$chunkFile = $tempFolder . '/' . $i;
if (file_exists($chunkFile)) {
$content = file_get_contents($chunkFile);
fwrite($file, $content);
@unlink($chunkFile); // 删除分片
}
}
fclose($file);
// 删除临时目录
@rmdir($tempFolder);
// 获取文件大小
$fileSize = filesize($filePath);
return [
'originalName' => $originalName,
'fileName' => $fileName,
'filePath' => $filePath,
'fileSize' => $fileSize,
'fileType' => $ext,
'uploadTime' => date('Y-m-d H:i:s'),
'msg' => '上传成功'
];
}
/**
* 获取上传错误信息
*/
private function uploadErrorMsg($errorCode) {
$messages = [
UPLOAD_ERR_INI_SIZE => '文件大小超过服务器限制',
UPLOAD_ERR_FORM_SIZE => '文件大小超过表单限制',
UPLOAD_ERR_PARTIAL => '文件只有部分被上传',
UPLOAD_ERR_NO_FILE => '没有文件被上传',
UPLOAD_ERR_NO_TMP_DIR => '找不到临时文件夹',
UPLOAD_ERR_CANT_WRITE => '文件写入失败',
UPLOAD_ERR_EXTENSION => '文件上传被扩展阻止',
];
return isset($messages[$errorCode]) ? $messages[$errorCode] : '未知错误';
}
}
入口文件 upload.php
<?php
/**
* WebUploader 上传入口文件
* 使用方法: 引入此文件即可
*/
// 设置时区
date_default_timezone_set('Asia/Shanghai');
// 配置
$config = [
'upload_dir' => __DIR__ . '/uploads/', // 上传目录
'max_size' => 100 * 1024 * 1024, // 最大100MB
'allow_types' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'zip', 'rar', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'mp4', 'mp3'],
];
// 实例化并处理
$handler = new UploadHandler($config);
$handler->handleUpload();
?>
前端示例代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">WebUploader 文件上传</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/webuploader/0.1.5/webuploader.min.css">
<style>
.uploader-list {
margin: 20px 0;
}
.file-item {
padding: 10px;
border: 1px solid #ddd;
margin: 5px 0;
background: #f9f9f9;
}
.progress {
height: 10px;
background: #e0e0e0;
margin-top: 5px;
}
.progress-bar {
height: 100%;
background: #4CAF50;
}
#picker {
padding: 10px 20px;
background: #2196F3;
color: white;
border: none;
cursor: pointer;
}
</style>
</head>
<body>
<div id="uploader">
<div id="picker">选择文件</div>
<div class="uploader-list" id="fileList"></div>
<button type="button" class="btn btn-start">开始上传</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/webuploader/0.1.5/webuploader.min.js"></script>
<script>
$(function() {
var uploader = WebUploader.create({
// 上传地址
server: 'upload.php',
// 使用分片上传
chunked: true,
chunkSize: 2 * 1024 * 1024, // 每片2MB
// 并发数
threads: 3,
// 文件类型
accept: {
title: 'Images',
extensions: 'jpg,jpeg,png,gif',
mimeTypes: 'image/jpeg,image/png,image/gif'
},
// 单个文件大小限制
fileSizeLimit: 100 * 1024 * 1024,
fileSingleSizeLimit: 50 * 1024 * 1024,
// 选择文件按钮
pick: '#picker'
});
// 添加到上传队列
uploader.on('fileQueued', function(file) {
var html = '<div class="file-item" id="' + file.id + '">' +
'<span class="file-name">' + file.name + '</span>' +
'<span class="file-size">' + Math.floor(file.size / 1024 / 1024) + 'MB</span>' +
'<div class="progress"><div class="progress-bar" style="width:0%"></div></div>' +
'<span class="file-status"></span>' +
'</div>';
$('#fileList').append(html);
});
// 上传进度
uploader.on('uploadProgress', function(file, percentage) {
var $li = $('#' + file.id);
$li.find('.progress-bar').css('width', percentage * 100 + '%');
});
// 上传成功
uploader.on('uploadSuccess', function(file, response) {
var $li = $('#' + file.id);
$li.find('.file-status').text('上传成功: ' + response.data.filePath);
});
// 上传失败
uploader.on('uploadError', function(file, reason) {
var $li = $('#' + file.id);
$li.find('.file-status').text('上传失败: ' + reason);
});
// 全部上传完成
uploader.on('uploadFinished', function() {
alert('所有文件上传完成');
});
// 开始上传按钮
$('.btn-start').on('click', function() {
uploader.upload();
});
});
</script>
</body>
</html>
高级功能扩展
<?php
/**
* 扩展功能:包括图片压缩、下载、进度查询等
*/
class AdvancedUploadHandler extends UploadHandler {
/**
* 图片压缩处理
*/
public function compressImage($sourcePath, $targetPath, $maxWidth = 1000) {
// 获取图片信息
list($width, $height, $type) = getimagesize($sourcePath);
// 如果图片宽度小于最大宽度,不进行压缩
if ($width <= $maxWidth) {
copy($sourcePath, $targetPath);
return true;
}
// 计算压缩后尺寸
$ratio = $maxWidth / $width;
$newWidth = $maxWidth;
$newHeight = $height * $ratio;
// 创建画布
$thumb = imagecreatetruecolor($newWidth, $newHeight);
// 根据类型加载源图
switch ($type) {
case IMAGETYPE_JPEG:
$source = imagecreatefromjpeg($sourcePath);
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
imagejpeg($thumb, $targetPath, 80);
break;
case IMAGETYPE_PNG:
$source = imagecreatefrompng($sourcePath);
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
imagepng($thumb, $targetPath, 6);
break;
default:
return false;
}
imagedestroy($thumb);
imagedestroy($source);
return true;
}
/**
* 异步下载大文件
*/
public function downloadFile($filePath, $downloadName = '') {
if (!file_exists($filePath)) {
http_response_code(404);
exit('File not found');
}
if (empty($downloadName)) {
$downloadName = basename($filePath);
}
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $downloadName . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
exit;
}
/**
* 获取上传进度(需要配合前端使用)
*/
public function getProgress($tempDir) {
$files = glob($tempDir . '/*');
if ($files) {
return count($files);
}
return 0;
}
}
?>
注意事项
1 安全配置
- 设置 Web 服务器上传大小限制(PHP.ini)
- 验证文件类型(不仅靠扩展名,还要检查 MIME)
- 使用安全的文件命名方式
- 控制上传目录的访问权限
2 PHP 配置建议
; php.ini 配置 upload_max_filesize = 100M post_max_size = 100M max_execution_time = 300 memory_limit = 128M
3 前端注意事项
- 合理设置分片大小(2MB-5MB为宜)
- 设置并发数(2-3个)
- 添加超时处理
- 实现断点续传逻辑
这个方案完整支持了 WebUploader 的普通上传和分片上传功能,可以根据实际需求调整配置和扩展功能。