工具介绍
文件上传功能基本每个网站都会应用到,每种类型的文件都是一种应用场景,自己在以往的工作中遇到上传开发总是重复造轮子,这次通过对上传类的封装做到一种通用、方便扩展的处理方式。
代码示例
上传功能的应用场景不光有图片,还有文档、音视频、压缩包等类型,不同的文件上传拥有较多相同的行为,这时可以把文件上传进行抽象,不同文件类型作为子类继承实现各自的上传方法。当有新的文件类型需要扩展时,可以在不修改原有代码的情况下进行扩展,可以支持多文件同时上传。
<?php
abstract class FileUpload
{
//文件上传大小限制
protected $maxsize = 3 * 1024 * 1024;
//媒体类型
protected $allowMime = [];
//文件扩展名
protected $allowFileExt = [];
//文件保存路径
protected $savePath;
//文件上传后名称
protected $saveFilename;
//文件上传后字节长度
protected $filenameLength = 10;
//是否开启文件重命名
protected $isRename = true;
//上传文件错误
protected $error;
//上传失败文件
protected $failedFile = [];
/**
* 设置文件上传最大值设置
* @param $maxsize int 文件大小(单位 MB)
* @return $this
*/
public function setMaxsize(int $maxsize)
{
if($maxsize > 0){
$this->maxsize = $maxsize * 1024 * 1024;
}
return $this;
}
/**
* 文件上传时是否重命名
* @param $isRename bool 是否重命名
* @param $filenameLength int 字符长度
*/
public function isRename(bool $isRename, int $filenameLength = 10)
{
$this->isRename = $isRename;
$this->filenameLength = $filenameLength;
return $this;
}
/**
* 重新设置上传文件名称
* @param $filename string 文件名
* @return string
* @throws \Exception
*/
protected function setFilename(string $filename)
{
if($this->isRename === false){
$this->saveFilename = $filename;
return $this;
}
$fileExt = pathinfo($filename, PATHINFO_EXTENSION);
$randomFilename = $this->generateRandomString($this->filenameLength);
if(!$randomFilename){
$this->error = '文件名生成失败';
return false;
}
$this->saveFilename = $randomFilename . '.' . $fileExt;
return $this->saveFilename;
}
/**
* 生成随机字符串
* @param $length int 字符串长度
* @return boolean|string
* @throws \Exception
*/
protected function generateRandomString(int $length)
{
if(!is_int($length)){
return false;
}
if($length < 1){
return false;
}
try{
$bytes = random_bytes($length);
}catch(\Exception $e){
return false;
}
return substr(strtr(base64_encode($bytes), '+/', '-_'), 0, $length);
}
/**
* 上传文件
* @param $files array 文件,支持多个文件
*/
abstract public function upload($files);
/**
* 验证文件合法性
* @param $file array 文件
* @return boolean
*/
protected function validateFile($file)
{
if(!is_file($file['tmp_name'])){
$this->error = '文件不合法';
return false;
}
if($file['error'] > 0){
$this->error = '文件上传错误';
return false;
}
if($file['size'] > $this->maxsize){
$this->error = '文件不能超过' . $this->maxsize / 1024 / 1024 . 'MB';
return false;
}
if(!in_array(pathinfo($file['name'],PATHINFO_EXTENSION),$this->allowFileExt,true)){
$this->error = '文件扩展名错误';
return false;
}
if(!in_array($file['type'],$this->allowMime)){
$this->error = '文件类型错误';
return false;
}
//获取文件真实类型,防止MIME伪装
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$real_ext = finfo_file($finfo, $file['tmp_name']);
if($real_ext != $file['type']){
$this->error = '文件类型错误';
return false;
}
return true;
}
/**
* 设置允许的文件类型
* @param $mime array 文件类型
* @return $this
*/
public function setAllowMime(array $mime)
{
if(is_array($mime)){
$this->allowMime = $mime;
}
return $this;
}
/**
* 设置允许的文件扩展名
* @param $ext array 文件扩展名
* @return $this
*/
public function setAllowFileExt(array $ext)
{
if(is_array($ext)){
$this->allowFileExt = $ext;
}
return $this;
}
/**
* 设置文件保存路径
* @param $savePath string 文件保存路径
* @return $this
*/
public function setSavePath(string $savePath)
{
if(is_dir($savePath)){
$this->savePath = rtrim($savePath,'/') . '/';
}
return $this;
}
/**
* 获取错误信息
*/
public function getError()
{
return $this->error;
}
}
class ImageUpload extends FileUpload
{
public function __construct()
{
$this->setAllowMime(['image/gif','image/jpeg','image/png']);
$this->setAllowFileExt(['png','jpg','jpeg','gif']);
}
/**
* 上传文件
* @param $files array 文件
* @return bool
* @throws \Exception
*/
public function upload($files)
{
if(empty($files) || !is_array($files)){
$this->error = '文件不能为空';
return false;
}
foreach($files as $key => $file){
if(!$this->validateFile($file)){
continue;
}
$saveName = $this->setFilename($file['name']);
if(!$saveName){
continue;
}
$move_filename = $this->savePath . $saveName;
$move_result = move_uploaded_file($file['tmp_name'], $move_filename);
if(!$move_result){
$this->failedFile[] = $file['name'];
continue;
}
}
return true;
}
}
网友评论