PHP模块之文件上传

  • 目录
  • 1.前言
  • 2.上传的实现
  • 2.1客户端配置
  • 2.2服务端配置
  • 2.3报错原因
  • 2.4限制条件
  • 2.4.1客户端限制
  • 2.4.2服务器端限制
  • 2.5.实现单文件上传
  • 2.6实现多文件上传
  • 2.7实现文件的下载
  • 3. 文件上传类封装
  • 3.1析构函数声明基本变量
  • 3.2上传文件调用函数
  • 3.3核心模块之检测上传文件是否出错
  • 3.4核心模块之检测是否是真实图片
  • 3.5核心模块之检测是否通过HTTP POST方式上传
  • 4. 总结

1.前言

将客户端文件上传到服务器端,再将服务器端的文件(临时文件)移动到指定目录即可。通过本章文件上传模块实现,你将透过使用现象看到文件上传的本质!

代码分享:https://github.com/mtdgclub/libraryClass

详见UploadLib.class.php

2.上传的实现

2.1客户端配置

选择文件上传页面(表单页面)

下面两个条件缺一不可:

  • 发送方式为POST
  • 添加enctype="multipart/form-data"属性

简单的前端代码实现如下:

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 
<meta name="format-detection" content="telephone=no" /> 
<title>文件上传</title>
<meta charset="utf-8" />
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
请选择您要上传的文件:<br/>
<input type="file" name="myFile" /><br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>

注:关键就是form的属性;另外就是input 中用到了type="file"这一点

2.2服务端配置

处理上传文件,流程如下

  • 获得上传页面传递的$FILES
  • 将服务器的临时文件移动到指定位置

方法一:move_upload_file($tmp_name,$destination)

方法二:copy($src,$des)

注意:move相当于剪切;而copy则临时文件还在

  • 根据$error的值,提示不同信息,进行相应处理

upload.php代码实现如下:

<?php

//文件上传处理程序
header("Content-type:text/html;charset=utf-8");
//$_FILES:文件上传变量
$filename=$_FILES['myFile']['name'];
$type=$_FILES['myFile']['type'];
$tmp_name=$_FILES['myFile']['tmp_name'];
$size=$_FILES['myFile']['size'];
$error=$_FILES['myFile']['error'];

//将服务器上的临时文件移动到指定位置
//方法一move_upload_file($tmp_name,$destination)
//move_uploaded_file($tmp_name, "uploads/".$filename);//文件夹应提前建立好,不然报错
//方法二copy($src,$des)
//以上两个函数都是成功返回真,否则返回false
//copy($tmp_name, "copies/".$filename);
//注意,不能两个方法都对临时文件进行操作,临时文件操作完就没了,我们试试反过来
copy($tmp_name, "copies/".$filename);
move_uploaded_file($tmp_name, "uploads/".$filename);
//能够实现,说明move那个函数基本上相当于剪切;copy就是copy,临时文件还在
//另外,错误信息也是不一样的,遇到错误可以查看或者直接报告给用户
if ($error===0) {
  echo "上传成功!";
}else{
  switch ($error){
    case 1:
      echo "超过了上传文件的最大值,请上传2M以下文件";
      break;
    case 2:
      echo "上传文件过多,请一次上传20个及以下文件!";
      break;
    case 3:
      echo "文件并未完全上传,请再次尝试!";
      break;
    case 4:
      echo "未选择上传文件!";
      break;
    case 5:
      echo "上传文件为0";
      break;
  }
}

注:需要在upload.php的同级目录里新建一个uploads文件夹用于存放上传的图片,不然会报错;上面的var_dump($_FILES);可以在测试时候对文件上传成功与否进行判断,如果成功上传,返回文件信息数组,如:

array(1){
["myflie"]=>
array(5){
["name"]=>string(37)"xxxxxxxx.jpg"
["type"]=>string(10)"image/jpeg"
["tmp_name"]=>string(44)"C:\Users\win7\xxxxxx\xxxx\xxx\xxx.tmp
["error"]=>int(0)
["size"]=>int(13767)
}
}

得到的是一个二维数组,关键有两个:tmp_name临时文件名;error报错信息(代号,后面可以利用)

2.3报错原因

基本上都是超过或者不符合服务器关于上传文件的配置,那么服务器端配置有哪些呢?先考虑上传我们用了什么?POST、upload,所以在php.ini中找这么几项:

  • file_upload:On
  • upload_tmp_dir=——临时文件保存目录;
  • upload_max_filesize=2M
  • max_file_uploads=20——允许一次上传的最大文件数量
  • post_max_size=8M——post方式发送数据的最大值

其他相关配置

  • max_exectuion_time=-1——最大执行时间,避免程序不好占用服务器资源;
  • max_input_time=60
  • max_input_nesting_level=64——输入嵌套深度;
  • memory_limit=128M——最大单线程的独立内存使用量

总之都是有关资源的配置。

错误号

  • UPLOAD_ERR_OK          值:0; 没有错误发生,文件上传成功。
  • UPLOAD_ERR_INI_SIZE      值:1; 上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值
  • UPLOAD_ERR_FORM_SIZE  值:2; 上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值
  • UPLOAD_ERR_PARTIAL       值:3; 文件只有部分被上传。
  • UPLOAD_ERR_NO_FILE       值:4; 没有文件被上传。

注:这个错误信息是第一步上传的信息,也就是上传到临时文件夹的情况,而不是move或者copy的情况。

2.4限制条件

下面从客户端和服务器端实现文件上传限制

2.4.1客户端限制

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 
<meta name="format-detection" content="telephone=no" /> 
<title>文件上传(客户端限制)</title>
<meta charset="utf-8" />
</head>
<body>
<form action="upload2.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="101321" />
请选择您要上传的文件:
<input type="file" name="myFile" accept="image/jpeg,image/gif,text/html"/><br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>

这里用input的属性对上传文件的大小和类型进行了限制,但是个人感觉,html代码是“可见的”,容易被修改,可能不起作用。

2.4.2服务器端限制

<?php

header('content-type:text/html;charset=utf-8');
//接受文件,临时文件信息
$fileinfo=$_FILES["myFile"];//降维操作
$filename=$fileinfo["name"];
$tmp_name=$fileinfo["tmp_name"];
$size=$fileinfo["size"];
$error=$fileinfo["error"];
$type=$fileinfo["type"];
 
//服务器端设定限制
$maxsize=10485760;//10M,10*1024*1024
$allowExt=array('jpeg','jpg','png','tif');//允许上传的文件类型(拓展名)
$ext=pathinfo($filename,PATHINFO_EXTENSION);//提取上传文件的拓展名
$path="uploads";//目标存放文件夹
if (!file_exists($path)) {  //当目录不存在,就创建目录
  mkdir($path,0777,true);//创建目录
  chmod($path, 0777);//改变文件模式,所有人都有执行权限、写权限、度权限
}

//得到唯一的文件名!防止因为文件名相同而产生覆盖
//md5加密,uniqid产生唯一id,microtime做前缀
//目标存放文件地址
$uniName=md5(uniqid(microtime(true),true)).".$ext";
$destination=$path."/".$uniName;

//当文件上传成功,存入临时文件夹,服务器端开始判断
if ($error===0) {
  if ($size>$maxsize) {
    exit("上传文件过大!");
  }
  if (!in_array($ext, $allowExt)) {
    exit("非法文件类型");
  }
  if (!is_uploaded_file($tmp_name)) {
    exit("上传方式有误,请使用post方式");
  }
  //判断是否为真实图片(防止伪装成图片的病毒一类的
  if (!getimagesize($tmp_name)) {//getimagesize真实返回数组,否则返回false
    exit("不是真正的图片类型");
  }
  //move_uploaded_file($tmp_name, "uploads/".$filename);
  if (@move_uploaded_file($tmp_name, $destination)) {//@错误抑制符,不让用户看到警告
    echo "文件".$filename."上传成功!";
  }else{
    echo "文件".$filename."上传失败!";
  } 
}else{
  switch ($error){
    case 1:
      echo "超过了上传文件的最大值,请上传2M以下文件";
      break;
    case 2:
      echo "上传文件过多,请一次上传20个及以下文件!";
      break;
    case 3:
      echo "文件并未完全上传,请再次尝试!";
      break;
    case 4:
      echo "未选择上传文件!";
      break;
    case 7:
      echo "没有临时文件夹";
      break;
  }
}

2.5.实现单文件上传

实现单文件上传fun_upload.php代码如下:

<?php

//定义一个uploadFile函数
function uploadFile($fileInfo,$path,$allowExt,$maxSize){
//取出$_FILES中的数据
$filename=$fileInfo["name"];
$tmp_name=$fileInfo["tmp_name"];
$size=$fileInfo["size"];
$error=$fileInfo["error"];
$type=$fileInfo["type"];
//取出文件路径中文件的类型的部分
$ext=pathinfo($filename,PATHINFO_EXTENSION);
//确定是否存在存放图片的文件夹,没有则新建一个
if (!file_exists($path)) {  //当目录不存在,就创建目录
  mkdir($path,0777,true);//创建目录
  chmod($path, 0777);//改变文件模式,所有人都有执行权限、写权限、度权限
}
//得到唯一的文件名!防止因为文件名相同而产生覆盖
$uniName=md5(uniqid(microtime(true),true)).'.'.$ext;
//目标存放文件地址
$destination=$path."/".$uniName;
//当文件上传成功,存入临时文件夹,服务器端开始判断
if ($error==0) {
  if ($size>$maxSize) {
    exit("上传文件过大!");
  }
  if (!in_array($ext, $allowExt)) {
    exit("非法文件类型");
  }
  if (!is_uploaded_file($tmp_name)) {
    exit("上传方式有误,请使用post方式");
  }

  //判断是否为真实图片(防止伪装成图片的病毒一类的
  if (!getimagesize($tmp_name)) {//getimagesize真实返回数组,否则返回false
    exit("不是真正的图片类型");
  }

  if (@move_uploaded_file($tmp_name, $destination)) {//@错误抑制符,不让用户看到警告
    echo "文件".$filename."上传成功!";
  }else{
    echo "文件".$filename."上传失败!";
  }
}else{

  switch ($error){
    case 1:
      echo "超过了上传文件的最大值,请上传2M以下文件";
      break;
    case 2:
      echo "上传文件过多,请一次上传20个及以下文件!";
      break;
    case 3:
      echo "文件并未完全上传,请再次尝试!";
      break;
    case 4:
      echo "未选择上传文件!";
      break;
    case 7:
      echo "没有临时文件夹";
      break;
  }
}

//返回文件的路径
return $destination;
}

表单采取之前的,然后提交接收文件进行如下处理

<?php

header('content-type:text/html;charset=utf-8');
//初始化相关变量
$fileInfo=$_FILES["myFile"];
$maxSize=10485760;//10M,10*1024*1024
$allowExt=array('jpeg','jpg','png','tif');
$path="uploads";
//引入前面封装了的上传函数fun_upload.php
include_once 'fun_upload.php';
uploadFile($fileInfo, $path, $allowExt, $maxSize);
?>

这样我们就能够通过引用函数文件使用这个函数,这个函数会返回文件的路径。

2.6实现多文件上传

前台基础表单代码如下:

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 
<meta name="format-detection" content="telephone=no" /> 
<meta charset="utf-8" />
<title>多文件上传</title>
</head>
<body>
<form action="upload4.php" method="post" enctype="multipart/form-data">
请选择您要上传的文件:<input type="file" name="myFile1" /><br/>
请选择您要上传的文件:<input type="file" name="myFile2" /><br/>
请选择您要上传的文件:<input type="file" name="myFile3" /><br/>
请选择您要上传的文件:<input type="file" name="myFile4" /><br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>

服务端处理代码如下:

<?php

header('content-type:text/html;charset=utf-8');
include_once 'fun_upload.php';
foreach ($_FILES as $fileInfo){ 
$file[]=uploadFile($fileInfo);
}

//将$_FILES打印出来,打印出来看到是个二维数组,很简单,遍历去用就好了!封装好的上传函数需要修改一下,给一些默认值,具体封装代码如下
function uploadFile( $fileInfo,$path="uploads",$allowExt=array('jpeg','jpg','png','tif'),$maxSize=10485760)
{
$filename=$fileInfo["name"];
$tmp_name=$fileInfo["tmp_name"];
$size=$fileInfo["size"];
$error=$fileInfo["error"];
$type=$fileInfo["type"];
//服务器端设定限制
$ext=pathinfo($filename,PATHINFO_EXTENSION);
//目的信息
if (!file_exists($path)) {  
  mkdir($path,0777,true);
  chmod($path, 0777);
}
$uniName=md5(uniqid(microtime(true),true)).'.'.$ext;
$destination=$path."/".$uniName;
if ($error==0) {
  if ($size>$maxSize) {
    exit("上传文件过大!");
  }
  if (!in_array($ext, $allowExt)) {
    exit("非法文件类型");
  }
  if (!is_uploaded_file($tmp_name)) {
    exit("上传方式有误,请使用post方式");
  }
  //判断是否为真实图片(防止伪装成图片的病毒一类的
  if (!getimagesize($tmp_name)) {//getimagesize真实返回数组,否则返回false
    exit("不是真正的图片类型");
  }
  if (@move_uploaded_file($tmp_name, $destination)) {//@错误抑制符,不让用户看到警告
    echo "文件".$filename."上传成功!";
  }else{
    echo "文件".$filename."上传失败!";
  }
}else{
  switch ($error){
    case 1:
      echo "超过了上传文件的最大值,请上传2M以下文件";
      break;
    case 2:
      echo "上传文件过多,请一次上传20个及以下文件!";
      break;
    case 3:
      echo "文件并未完全上传,请再次尝试!";
      break;
    case 4:
      echo "未选择上传文件!";
      break;
    case 7:
      echo "没有临时文件夹";
      break;
  }
}
return $destination;
}

2.7实现文件的下载

<?php

//获取传递过来的路径信息
$filename=$_GET['filename'];
//判断是否有值,没有则不执行下面的php语句
if($filename){
header("Content-Disposition:attachment;filename=download_$filename");
//Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
//格式:content-disposition = "Content-Disposition" ":" disposition-type *( ";" disposition-parm 
//Content-Disposition为属性名
//disposition-type是以什么方式下载,如attachment为以附件方式下载
//disposition-parm为默认保存时的文件名 
readfile($filename);
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 
<meta name="format-detection" content="telephone=no" /> 
<title>文件下载</title>
<meta charset="utf-8" />
</head>
<body>
<a href="1.rar">下载1.rar</a>
<br />
<a href="1.jpg">下载1.jpg</a>
<br />
<a href="download.php?filename=1.jpg">通过程序下载1.jpg</a>
</body>
</html>

注:测试的时候,文件的同级目录下,必须存在1.rar、1.jpg,也可以通过修改路径,改变到自己的文件想要的文件夹下面

3.文件上传类封装

基于上述代码对文件上传类进行封装、功能函数拆分和流程优化。

3.1析构函数声明基本变量

/**
 * @param string $fileName 文件名
 * @param string $uploadPath 上传路径
 * @param bool $imgFlag 图片真实性
 * @param int $maxSize 图片最大大小
 * @param array $allowExt 允许上传图片后缀名
 * @param array $allowMime 允许浏览器读取图片格式
 */
public function __construct($fileName='myFile',$uploadPath='./uploads',$imgFlag=true,$maxSize=5242880,$allowExt=array('jpeg','jpg','png','gif'),$allowMime=array('image/jpeg','image/png','image/gif')){
    $this->fileName=$fileName;
    $this->maxSize=$maxSize;
    $this->allowMime=$allowMime;
    $this->allowExt=$allowExt;
    $this->uploadPath=$uploadPath;
    $this->imgFlag=$imgFlag;
    $this->fileInfo=$_FILES[$this->fileName];
}

3.2上传文件调用函数

  /**
     * 上传文件
     * @return string
     */
    public function uploadFile(){
        if($this->checkError()&&$this->checkSize()&&$this->checkExt()&&$this->checkMime()&&$this->checkTrueImg()&&$this->checkHTTPPost()){
            $this->checkUploadPath();
            $this->uniName=$this->getUniName();
            $this->destination=$this->uploadPath.'/'.$this->uniName.'.'.$this->ext;
            if(@move_uploaded_file($this->fileInfo['tmp_name'], $this->destination)){
//                return  $this->destination;
                $data = ['code'=>'ok','message'=>'上传成功','data'=>$this->destination];
                return json_encode($data);
            }else{
                $this->error='文件移动失败';
                $this->showError();
            }
        }else{
            $this->showError();
        }
    }

3.3核心模块之检测上传文件是否出错

/**
 * 检测上传文件是否出错
 * @return boolean
 */
protected function checkError(){
    if(!is_null($this->fileInfo)){
        if($this->fileInfo['error']>0){
            switch($this->fileInfo['error']){
                case 1:
                    $this->error='超过了PHP配置文件中upload_max_filesize选项的值';
                    break;
                case 2:
                    $this->error='超过了表单中MAX_FILE_SIZE设置的值';
                    break;
                case 3:
                    $this->error='文件部分被上传';
                    break;
                case 4:
                    $this->error='没有选择上传文件';
                    break;
                case 6:
                    $this->error='没有找到临时目录';
                    break;
                case 7:
                    $this->error='文件不可写';
                    break;
                case 8:
                    $this->error='由于PHP的扩展程序中断文件上传';
                    break;

            }
            return false;
        }else{
            return true;
        }
    }else{
        $this->error='文件上传出错';
        return false;
    }
}

3.4核心模块之检测是否是真实图片

/**
 * 检测是否是真实图片
 * @return boolean
 */
protected function checkTrueImg(){
    if($this->imgFlag){
        if(!@getimagesize($this->fileInfo['tmp_name'])){
            $this->error='不是真实图片';
            return false;
        }
        return true;
    }
}

3.5核心模块之检测是否通过HTTP POST方式上传

/**
 * 检测是否通过HTTP POST方式上传
 * @return boolean
 */
protected function checkHTTPPost(){
    if(!is_uploaded_file($this->fileInfo['tmp_name'])){
        $this->error='文件不是通过HTTP POST方式上传上来的';
        return false;
    }
    return true;
}

4.总结

通过上述代码学习,可以了解到文件上传功能其实不难实现,难的是思想和流程的规范,要对文件进行多重的检测处理,保证文件的安全性;其次,对于多文件传输、传输速度优化等方面都是可以考虑的地方,涉及到文件分块传输技术、多进程处理方案,这些有兴趣的可以提前了解,我在以后也会做单章说明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值