PHP实战之文件上传与下载

走远了吗. 提交于 2020-08-18 06:39:58

 

1. 前言

PHP文件上传原理就是客户端文件上传到服务器端,再将服务器端临时文件移动到指定目录,下面我们一起实战一下,加深印象。

2.代码实战

2.1客户端页面配置说明

上传文件必须满足以下三个条件:

  • 表单页面
  • 表单发送方式为psot
  • 必须添加enctype=multipart/form-data

如下代码所示:

<form action="doAction.php" class="file" method="post" enctype="multipart/form-data"> 
<input type="file" name="myFile"><br/> 
<input type="submit" value="上传">
</form>

2.2 $_FILES预定义变量解析

  • name : 上传文件名称
  • type : 上传文件的MIME类型
  • tmp_name : 上传到服务器上的临时文件
  • size : 上传文件的大小
  • error : 上传文件的错误号

2.3文件的移动方式

2.3.1第一种移动形式

//move_uploaded_file($tmp_name,$destination):将服务器上的临时文件移动到指定目录下
//移动成功返回true,否则返回false
move_uploaded_file($tmp_name, "uploads/".$filename);

2.3.2第二种移动形式

//copy($src,$dst):将文件拷贝到指定目录,拷贝成功返回true,否则返回false
copy($tmp_name,"uploads/".$filename);

2.4文件上传配置及解析

服务器端配置:

  • file_uploads=On,支持HTTP上传
  • upload_tmp_dir=,临时文件保存的目录
  • upload_max_filesize=2M,允许上传文件的最大值
  • max_file_uploads=20,允许一次上传的最大文件数
  • post_max_size=8M,POST方式发送数据的最大值

文件上传配置:

  • max_execution_time=-1,设置脚本被解析器终止之前允许的最大执行时间,单位为秒,防止不友好程序占用服务器资源.
  • max_input_time=60,脚本解析输入数据允许的最大时间,单位为秒
  • max_input_nesting_level=64,设置输入变量的嵌套深度
  • max_input_vars=1000,接受多少输入的变量指令的使用减轻以哈希碰撞带来的拒绝服务供给的可能性.
  • memory_limit=128M, 最大单线程的独立内存使用量.

2.5 错误信息说明

  • 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,没有文件被上传。
  • UPLOAD_ERR_NO_TMP_DIR:其值为 6,找不到临时文件夹。
  • UPLOAD_ERR_CANT_WRITE:其值为 7,文件写入失败。
  • UPLOAD_ERR_EXTENSION:其值为8上传的文件被PHP扩展程序中断

3. 文件上传案例实战

3.1客户端限制

<!--    通过表单隐藏与域限制上传文件的最大值-->
<input type="hidden" name="MAX_FILE_SIZE" value="字节数">

<!--    通过accept属性限制上传文件类型-->
<input type="file" name="myFile" accept="文件的MIME类型">

其中MIME类型有:image/jpeg,image/gif,image/png

注意:上面两个限制,对于会程序的用户来说,等同于没有做限制,因为可以在前端修改掉,所以要在服务器做相应限制

3.2服务器端限制

3.2.1限制上传文件大小

//检测上传得到小
if($fileInfo['size']>$maxSize){
   $res['mes']=$fileInfo['name'].'上传文件过大';
}

3.2.2限制上传文件类型

/**
 * 得到文件扩展名
 * @param string $filename
 * @return string
 */
function getExt($filename){
   return strtolower(pathinfo($filename,PATHINFO_EXTENSION));
}


$ext=getExt($fileInfo['name']);
//检测上传文件的文件类型
if(!in_array($ext,$allowExt)){
   $res['mes']=$fileInfo['name'].'非法文件类型';
}

3.2.3检测是非真实图片类型

//检测是否是真实的图片类型
if($flag){
   if(!getimagesize($fileInfo['tmp_name'])){
      $res['mes']=$fileInfo['name'].'不是真实图片类型';
   }
}

3.2.4检测是非为HTTP POST方式上传

//检测文件是否是通过HTTP POST上传上来的
if(!is_uploaded_file($fileInfo['tmp_name'])){
   $res['mes']=$fileInfo['name'].'文件不是通过HTTP POST方式上传上来的';
}

3.2.5确保上传文件名唯一

/**
 * 产生唯一字符串
 * @return string
 */
function getUniName(){
   return md5(uniqid(microtime(true),true));
}

$uniName=getUniName();
$destination=$path.'/'.$uniName.'.'.$ext;

3.2.6 uploads目录不存在时候,自动创建目录

$path='./uploads';

if(!file_exists($path)){
   mkdir($path,0777,true);
   chmod($path,0777);
}

3.3单文件上传函数封装

$uniName=md5(uniqid(microtime(true),true)).".".$ext;
$destination=$path.'/'.$uniName.'.'.$ext;
if(!move_uploaded_file($fileInfo['tmp_name'],$destination)){
   $res['mes']=$fileInfo['name'].'文件移动失败';
}
$res['mes']=$fileInfo['name'].'上传成功';

3.4多文件上传案例实战 

<body>
<form action="doAction4.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>

上传返回的是二维数组,通过循环上传

include_once 'upload.func.php';
//print_r($_FILES);
foreach($_FILES as $fileInfo){
   $files[]=uploadFile($fileInfo);
}
print_r($files);

能上传的文件就要让它上传,而不是因为一个而否定全部文件

所以我们要对前端进行修改:

<body>
<form action="doAction5.php" method="post" enctype="multipart/form-data">
请选择您要上传的文件:<input type="file" name='myFile[]' /><br/>
请选择您要上传的文件:<input type="file" name='myFile[]' /><br/>
请选择您要上传的文件:<input type="file" name='myFile[]' /><br/>
请选择您要上传的文件:<input type="file" name='myFile[]' /><br/>
请选择您要上传的文件:<input type="file" name='myFile[]' /><br/>
<input type="submit" value="上传文件" />
</form>
</body>

那么这样我们得到的是三维数组,我们要将三维数组变为我们需要的二维数组,构建上传文件信息如下:

/**
 * 构建上传文件信息
 * @return unknown
 */
function getFiles(){
   $i=0;
   foreach($_FILES as $file){
      if(is_string($file['name'])){
         $files[$i]=$file;
         $i++;
      }elseif(is_array($file['name'])){
         foreach($file['name'] as $key=>$val){
            $files[$i]['name']=$file['name'][$key];
            $files[$i]['type']=$file['type'][$key];
            $files[$i]['tmp_name']=$file['tmp_name'][$key];
            $files[$i]['error']=$file['error'][$key];
            $files[$i]['size']=$file['size'][$key];
            $i++;
         }
      }
   }
   return $files;
   
}

输出的数组需要过滤掉空数组,并重置为新数组(键从0开始)

$uploadFiles=array_values(array_filter($uploadFiles));
print_r($uploadFiles);

 3.5封装上传文件类

<?php 
class upload{
   protected $fileName;
   protected $maxSize;
   protected $allowMime;
   protected $allowExt;
   protected $uploadPath;
   protected $imgFlag;
   protected $fileInfo;
   protected $error;
   protected $ext;
   /**
    * @param string $fileName
    * @param string $uploadPath
    * @param string $imgFlag
    * @param number $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];
   }


/**
 * 检测上传文件是否出错
 * @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;
   }
}


/**
 * 检测上传文件的大小
 * @return boolean
 */
protected function checkSize(){
   if($this->fileInfo['size']>$this->maxSize){
      $this->error='上传文件过大';
      return false;
   }
   return true;
}


/**
 * 检测扩展名
 * @return boolean
 */
protected function checkExt(){
   $this->ext=strtolower(pathinfo($this->fileInfo['name'],PATHINFO_EXTENSION));
   if(!in_array($this->ext,$this->allowExt)){
      $this->error='不允许的扩展名';
      return false;
   }
   return true;
}


/**
 * 检测文件的类型
 * @return boolean
 */
protected function checkMime(){
   if(!in_array($this->fileInfo['type'],$this->allowMime)){
      $this->error='不允许的文件类型';
      return false;
   }
   return true;
}


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

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


/**
 * 上传文件
 * @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;
      }else{
         $this->error='文件移动失败';
         $this->showError();
      }
   }else{
      $this->showError();
   }
}

3.6 文件下载 

<body>
<a href="1.rar">下载1.rar</a>
<br />
<a href="1.jpg">下载1.jpg</a>
<br />
<a href="doDownload.php?filename=1.jpg">通过程序下载1.jpg</a>
<br />
<a href="doDownload.php?filename=../upload/nv.jpg">下载nv.jpg</a>
</body>

注意如果超链接的是图片,浏览器默认会打开,那么我们需要设置,才能通过程序让浏览器下载图片,设置代码如下:

<?php 
$filename=$_GET['filename'];
header('content-disposition:attachment;filename='.basename($filename));
header('content-length:'.filesize($filename));
readfile($filename);

4.总结

文件上传和下载没有我们想的那么难,在多次接触之后反而觉得我们更需要的是对底层的认知,这些都有待能力的提高才能去了解。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!