<?php
// 创建一个上传类
class upload {
// 定义类中使用到的变量
private $error = ""; // 用于存储产生的错误信息
private $uploadFile = ""; // 存储客户端提交的文件信息
private $mimeType = "text/plain"; // 存储文件的mime值,默认为text/plain
private $filterType = []; // 用于存放mime值的数组
private $Filter = []; // 用于存储可上传文件类型的变量
private $fileSize = 1024 * 1024; // 设置可上传文件大小(默认1MB)
private $path = "html"; // 设置上传目录,默认值为html
private $rename = true; // 设置重命名开关
private $ext = ""; // 用于存储文件扩展名
private $stat = false; // 用于存储文件上传状态
// 构造函数,初始化类
public function __construct($files, $path = "html", $rename = true) {
$this->path = $path; // 设置上传路径
$this->filterType(); // 设置$filterType数组
$this->Filter(); // 设置$Filter数组
$this->uploadFile = $files; // 设置上传文件
$this->rename = $rename; // 设置重命名开关
$this->uploadFile(); // 开始上传文件
}
// 上传文件函数
public function uploadFile() {
$file = $this->uploadFile; // 获取类初始化时关于上传文件的数据
if (isset($file["tmp_name"]) && file_exists($file["tmp_name"])) {
// 检测上传文件变量中是否存在上传文件数据
$tmp = $file["tmp_name"]; // 获取上传文件临时文件名
$name = $file["name"]; // 获取上传文件名称
$dir = $this->path; // 设置文件保存目录
// 确保上传目录存在
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
// 检查上传文件是否合法
if (!$this->checkFileType($name, $tmp)) {
return false; // 如果上传文件类型为非法文件类型,返回false值
}
if (!$this->checkFileSize($tmp)) {
return false; // 如果上传文件过大,返回false值
}
// 根据重命名开关,处理新文件名
if ($this->rename) {
$filename = $this->newName() . "." . $this->ext;
} else {
$filename = $file["name"];
}
// 安全处理文件名,防止目录遍历攻击
$safeFilename = basename($filename);
$destination = $dir . "/" . $safeFilename;
// 移动上传的临时文件到新目录
if (move_uploaded_file($tmp, $destination)) {
$this->stat = true; // 设置上传状态为true
return true;
} else {
$this->stat = false; // 设置上传状态为false
$this->error .= "文件上传失败!<br>";
return false;
}
} else {
$this->error .= "上传文件不存在!<br>";
return false;
}
}
// 创建新文件名
private function newName() {
return uniqid() . time(); // 返回唯一ID和时间戳组合作为新文件名
}
// 设置上传文件大小
public function setFileSize($size) {
$this->fileSize = $size; // 设置类的关于文件大小的值
}
// 检查文件大小是否合法
private function checkFileSize($file) {
$filesize = filesize($file); // 获取文件大小
if ($filesize <= $this->fileSize) { // 检测文件大小是否小于等于规定的文件大小
return true;
} else {
$this->error .= "上传文件大于设置文件大小!";
return false; // 当上传文件大于规定的大小时,返回false值
}
}
// 检查文件类型
private function checkFileType($filename, $file) {
// 取得文件的扩展名
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$this->ext = $ext;
// 检查上传文件的扩展名是否在允许的列表中
if (in_array($ext, $this->Filter)) {
// 使用fileinfo扩展获取MIME类型(更安全)
if (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$this->mimeType = finfo_file($finfo, $file);
finfo_close($finfo);
} elseif (function_exists("mime_content_type")) {
// 如果mime_content_type()函数存在,使用其取得文件的mime值
$this->mimeType = mime_content_type($file);
} else {
// 如果上述函数都不存在,使用预定义的MIME类型映射
if (isset($this->filterType[$ext])) {
$this->mimeType = $this->filterType[$ext];
}
}
// 如果获取mime值后,$this->mimeType值依然为空,显示错误信息
if (empty($this->mimeType)) {
$this->error .= "获取文件类型出错";
return false;
} else {
return true;
}
} else {
$this->error .= "此文件类型不允许上传!<br>";
return false;
}
}
// 文件上传统计
public function saveUpload() {
// 这里可以添加文件上传记录到数据库或日志的代码
}
// 取得错误信息
public function getError() {
return $this->error;
}
// 设置允许上传的文件扩展名
private function Filter() {
// 默认允许上传txt和zip文件,用户可以扩展这个列表
$this->Filter = array("txt", "zip", "jpg", "png", "gif");
}
// 用于填充$this->filterType数组
private function filterType() {
$this->filterType = array(
'chm' => 'application/octet-stream',
'ppt' => 'application/vnd.ms-powerpoint',
'xls' => 'application/vnd.ms-excel',
'doc' => 'application/msword',
'exe' => 'application/octet-stream',
'rar' => 'application/octet-stream',
'zip' => 'application/zip',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'txt' => 'text/plain'
);
}
}
// 检查上传请求
if (isset($_GET["action"]) && $_GET["action"] == "upload") {
// 确保上传目录存在
if (!is_dir("html")) {
mkdir("html", 0777, true);
}
// 初始化upload类
$up = new upload($_FILES["file"], "html", true);
// 设置最大上传文件大小为10MB
$up->setFileSize(10 * 1024 * 1024);
// 如果文件上传成功,显示上传成功信息
if ($up->stat) {
echo "文件上传成功!";
exit();
} else {
// 如果上传失败,显示错误信息
echo $up->getError();
exit();
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>上传文件</title>
</head>
<body>
<form action="?action=upload" method="post" enctype="multipart/form-data" name="sendfile">
<label>选择上传文件:
<input type="file" name="file" />
</label>
<label>
<input type="submit" name="Submit" value="上传" />
</label>
</form>
</body>
</html>
<?php
// 创建一个下载类
class download {
// 定义类中使用到的变量
private $error = ""; // 用于存储产生的错误信息
private $downFile = ""; // 存储下载文件名称
private $mimeType = "text/plain"; // 存储下载文件的mime值,默认为text/plain
private $filterType = []; // 用于存放mime值的数组
private $Filter = []; // 用于存储可下载文件类型的变量
// 构造函数,初始化类
public function __construct($filename) {
$this->filterType(); // 设置$filterType数组
$this->Filter(); // 设置$Filter数组
$this->downFile = $filename; // 设置下载文件
$this->downFile(); // 下载文件
}
// 下载文件函数
public function downFile() {
// 使用checkFileType()函数检查下载文件是否合法
if ($this->checkFileType()) {
$filename = basename($this->downFile); // 安全获取文件名
// 输出头信息
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: " . $this->mimeType); // 输出文件类型头信息
header("Content-Length: " . filesize($this->downFile)); // 输出文件大小头信息
header("Content-Disposition: attachment; filename=\"" . $filename . "\""); // 文件名称头信息
header("Content-Transfer-Encoding: binary"); // 以二进制方式编码数据
// 输出文件内容
readfile($this->downFile);
return true;
} else {
return false;
}
}
// 检查文件类型
private function checkFileType() {
// 检查下载的文件是否存在
if (file_exists($this->downFile)) {
// 取得文件的扩展名
$ext = strtolower(pathinfo($this->downFile, PATHINFO_EXTENSION));
// 检查下载文件的扩展名是否在允许的列表中
if (in_array($ext, $this->Filter)) {
// 使用fileinfo扩展获取MIME类型(更安全)
if (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$this->mimeType = finfo_file($finfo, $this->downFile);
finfo_close($finfo);
} elseif (function_exists("mime_content_type")) {
// 如果mime_content_type()函数存在,使用其取得文件的mime值
$this->mimeType = mime_content_type($this->downFile);
} else {
// 如果上述函数都不存在,使用预定义的MIME类型映射
if (isset($this->filterType[$ext])) {
$this->mimeType = $this->filterType[$ext];
}
}
// 如果获取mime值后,$this->mimeType值依然为空,显示错误信息
if (empty($this->mimeType)) {
$this->error .= "获取文件类型出错";
return false;
} else {
return true;
}
} else {
$this->error .= "此文件类型不允许下载!<br>";
return false;
}
} else {
$this->error .= "文件不存在!<br>";
return false;
}
}
// 文件下载统计
public function countDownload() {
// 注意:原代码中的统计逻辑会破坏文件内容
// 这里提供一个安全的统计方式示例
$logFile = __DIR__ . '/download_log.txt';
$count = 1;
// 如果日志文件存在,读取当前计数
if (file_exists($logFile)) {
$count = (int)file_get_contents($logFile) + 1;
}
// 更新计数
file_put_contents($logFile, $count);
}
// 取得错误信息
public function getError() {
return $this->error; // 返回错误信息
}
// 设置允许下载的文件扩展名
private function Filter() {
// 默认允许下载txt文件,用户可以扩展这个列表
$this->Filter = array("txt", "pdf", "jpg", "png", "zip", "rar");
}
// 用于填充$this->filterType数组
private function filterType() {
$this->filterType = array(
'chm' => 'application/octet-stream',
'ppt' => 'application/vnd.ms-powerpoint',
'xls' => 'application/vnd.ms-excel',
'doc' => 'application/msword',
'exe' => 'application/octet-stream',
'rar' => 'application/octet-stream',
'zip' => 'application/zip',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'txt' => 'text/plain',
'pdf' => 'application/pdf'
);
}
}
// 创建一个数组,数组中包含着要下载文件的地址
$files = array(
1 => array("html/1.txt", "示例文本文件"),
2 => array("html/sample.pdf", "示例PDF文件"),
3 => array("html/image.jpg", "示例图片")
);
// 检查下载请求
if (isset($_GET["action"]) && $_GET["action"] == "download") {
$fid = intval($_GET["fid"]); // 取得数组的键值
// 验证文件ID是否存在
if (isset($files[$fid])) {
$filename = $files[$fid][0]; // 根据键值取得下载文件名称
// 安全检查:确保文件在允许的目录内
$safeDir = realpath(__DIR__ . '/html');
$requestedFile = realpath(__DIR__ . '/' . $filename);
if (strpos($requestedFile, $safeDir) === 0) {
$down = new download($filename); // 初始化download类
// 记录下载统计
$down->countDownload();
// 如果下载成功,结束代码运行
if ($down->downFile()) {
exit();
} else {
// 如果下载失败,显示错误信息
echo $down->getError();
exit();
}
} else {
echo "访问被拒绝: 非法文件路径!";
exit();
}
} else {
echo "文件不存在!";
exit();
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>文件下载</title>
</head>
<body>
<h3>直接下载文件的方式</h3>
<?php foreach ($files as $key => $val): ?>
<p><a href="<?php echo htmlspecialchars($val[0]); ?>" download><?php echo htmlspecialchars($val[1]); ?></a></p>
<?php endforeach; ?>
<h3>通过服务器中转下载的方式</h3>
<?php foreach ($files as $key => $val): ?>
<p><a href="?action=download&fid=<?php echo $key; ?>"><?php echo htmlspecialchars($val[1]); ?></a></p>
<?php endforeach; ?>
</body>
</html>
|