综述
上一节我们学习了文件的读写操作,这一节我们来看一下文件上传和下载的相关内容。
文件上传
1.PHP 配置文件
首先,我们文件上传需要设定一下 php.ini 的配置文件。这是最基本的设置,如果这里设置不成功,那么代码写得再正确也没有用。基本的配置项目如下
file_uploads = on #文件上传开启 upload_max_filesize= 200M #文件上传的最大尺寸 upload_tmp_dir = c:/uploads/ #临时文件目录 post_max_size = 250M #POST 时最大尺寸,必须要大于 upload_max_filesize
2. 上传时注意事项
1) 文件上传操作表单提交方法必须为 post 2)文件上传时,input type 必须为 file 类型 3)文件上传的表单中,需要增加一个隐含内容,代码如下,value 的单位是 B
<input type="hidden" name="MAX_FILE_SIZE" value="100000000">
|
4)enctype=”multipart/form-data” 只有文件上传时才使用这个值,用来指定表单编码的数据方式,让服务器知道我们要传递一个文件并带有一些常规的表单信息。如下
<form action="upload.php" method="post" enctype="multipart/form-data"> </form>
|
例如
<html> <head> <title> File Uploads </title> </head> <body> <form action="b.php" method="post" enctype="multipart/form-data"> shopname: <input type="text" name="shopname" > <br> shopprice: <input type="text" name="price"> <br> shopnum : <input type="text" name="num"> <br> shoppic: <input type="file" name="pic"> <br> <input type="submit" name="sub" value="添加商品"> </form> </body> </html>
|
文件 a.php 表单提交到了 b.php 文件,在文件 b.php 中如下
<?php echo "<pre>"; print_r($_POST); print_r($_FILES); echo "</pre>"; ?>
|
一个是输出 POST 得到的数据内容,另一个是输出获取到的文件信息。 运行结果如下
Array ( [shopname] => abc [price] => abc [num] => add [sub] => 添加商品 ) Array ( [pic] => Array ( [name] => QPGF.dll [type] => application/qscall-plugin [tmp_name] => D:\wamp\tmp\phpC2C7.tmp [error] => 0 [size] => 199224 )
)
|
如果不加 enctype=”multipart/form-data” 那么 print_r ($_FILES) 不会有任何输出 又比如多文件上传
<html> <head> <title> File Uploads </title> </head> <body> <form action="b.php" method="post" enctype="multipart/form-data"> shopname: <input type="text" name="shopname" > <br> shopprice: <input type="text" name="price"> <br> shopnum : <input type="text" name="num"> <br> shoppic: <input type="file" name="pic1"> <br> shoppic: <input type="file" name="pic2"> <br> shoppic: <input type="file" name="pic3"> <br> <input type="submit" name="sub" value="添加商品"> </form> </body> </html>
|
file 的 name 需要不同的名字,那么上面的代码输出结果为
Array ( [pic1] => Array ( [name] => libtcmalloc.dll [type] => application/qscall-plugin [tmp_name] => D:\wamp\tmp\phpE51E.tmp [error] => 0 [size] => 178232 )
[pic2] => Array ( [name] => libexpatw.dll [type] => application/qscall-plugin [tmp_name] => D:\wamp\tmp\phpE52E.tmp [error] => 0 [size] => 130104 )
[pic3] => Array ( [name] => AsyncTask.dll [type] => application/qscall-plugin [tmp_name] => D:\wamp\tmp\phpE52F.tmp [error] => 0 [size] => 84536 )
)
|
还可以将 name 设定为一个数组,如
shoppic: <input type="file" name="pic[]"> <br> shoppic: <input type="file" name="pic[]"> <br> shoppic: <input type="file" name="pic[]"> <br>
|
则输出会是一个三维数组
Array ( [pic] => Array ( [name] => Array ( [0] => libtcmalloc.dll [1] => libexpatw.dll [2] => QQProtect.dll )
[type] => Array ( [0] => application/qscall-plugin [1] => application/qscall-plugin [2] => application/qscall-plugin )
[tmp_name] => Array ( [0] => D:\wamp\tmp\phpA17D.tmp [1] => D:\wamp\tmp\phpA17E.tmp [2] => D:\wamp\tmp\phpA17F.tmp )
[error] => Array ( [0] => 0 [1] => 0 [2] => 0 )
[size] => Array ( [0] => 178232 [1] => 130104 [2] => 387128 )
)
)
|
3. 文件上传后的检查
加入上传的表单中文件的 name 是 pic,那么检查的四个方法如下: 1)使用 $_FILES [‘file’][‘error’] 检查错误 2)使用 $_FILES [‘file’][‘size’] 限制大小,单位是字节 3)使用 $_FILES [‘pic’][‘type’] 获取文件或站名,限制文件的类型 4)将上传后的文件名改名
<?php //step 1 使用$_FILES['pic']["error"] 检查错误
if($_FILES["pic"]["error"] > 0){ switch($_FILES["pic"]["error"]) { case 1: echo "上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值<br>"; break; case 2: echo "上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值"; break;
case 3: echo "文件只有部分被上传"; break;
case 4: echo "没有文件被上传"; break;
default:
echo "末知错误"; } exit; } $maxsize=5000000; //50k
//step 2 使用$_FILES["pic"]["size"] 限制大小 单位字节 2M=2000000 if($_FILES["pic"]["size"] > $maxsize ) { echo "上传的文件太大,不能超过{$maxsize}字节"; exit; }
//step 3 使用$_FILES["pic"]["type"]或是文件的扩展名 限制类型 MIME image/gif image/png gif png jpg
/* list($dl, $xl) = explode("/", $_FILES["pic"]["type"]);
if($dl!="image"){ echo "请上传一个图片,不充许其它类型文件"; exit; } */ $allowtype=array("png", "gif", "jpg", "jpeg"); $arr=explode(".", $_FILES["pic"]["name"]); $hz=$arr[count($arr)-1]; if(!in_array($hz, $allowtype)){ echo "这是不充许的类型"; exit; } //step 4 将让传后的文件名改名
$filepath="./uploads/"; $randname=date("Y").date("m").date("d").date("H").date("i").date("s").rand(100, 999).".".$hz; //将临时位置的文件移动到指定的目录上即可 if(is_uploaded_file($_FILES["pic"]["tmp_name"])){ if(move_uploaded_file($_FILES["pic"]["tmp_name"], $filepath.$randname)){ echo "上传成功"; }else{ echo "上传失败"; } }else{ echo "不是一个上传文件"; }
|
以上便实现了文件上传的检测,包括错误检测,文件大小检测,文件类型检测以及文件更名等等。
文件上传类
在上面的介绍中,我们没有将文件的上传做一个封装,不过,将文件上传个功能封装成一个类的确是一个不错的选择。下面便是一个实例 DEMO,让我们来感受一下吧!
<?php class FileUpload { private $filePath; private $allowType=array('gif', 'jpg', 'png', 'jpeg'); private $maxSize=1000000; private $isRandName=true; private $originName; private $tmpFileName; private $fileType; private $fileSize; private $newFileName; private $errorNum=0; private $errorMess="";
function __construct($options=array()){ foreach($options as $key=>$val){ if(!in_array($key,get_class_vars(get_class($this)))){ continue; } $this->setOption($key, $val); } }
private function getError(){ $str="上传文件<font color='red'>{$this->originName}</font>时出错:"; switch($this->errorNum){ case 4: $str .= "没有文件被上传"; break; case 3: $str .= "文件只被部分上传"; break; case 2: $str .= "上传文件超过了HTML表单中MAX_FILE_SIZE选项指定的值"; break; case 1: $str .= "上传文件超过了php.ini 中upload_max_filesize选项的值"; break; case -1: $str .= "不被充许的类型"; break; case -2: $str .= "文件过大,上传文件不能超过{$this->maxSize}个字节"; break; case -3: $str .= "上传失败"; break; case -4: $str .= "建立存放上传文件目录失败,请重新指定上传目录"; break; case -5: $str .= "必须指定上传文件的路径"; break; default: $str .= "末知错误"; } return $str.'<br>'; } private function checkFilePath(){ if(empty($this->filePath)) { $this->setOption('errorNum', -5); return false; } if(!file_exists($this->filePath) || !is_writable($this->filePath)){ if(!@mkdir($this->filePath, 0755)){ $this->setOption('errorNum', -4); return false; } } return true; } private function checkFileSize() { if($this->fileSize > $this->maxSize){ $this->setOPtion('errorNum', '-2'); return false; }else{ return true; } }
private function checkFileType() { if(in_array(strtolower($this->fileType), $this->allowType)) { return true; }else{ $this->setOption('errorNum', -1); return false; } } private function setNewFileName(){ if($this->isRandName){ $this->setOption('newFileName', $this->proRandName()); } else { $this->setOption('newFileName', $this->originName); } }
private function proRandName(){ $fileName=date("YmdHis").rand(100,999); return $fileName.'.'.$this->fileType; } private function setOption($key, $val){ $this->$key=$val; } function uploadFile($fileField){ echo $fileField; $return=true; if(!$this->checkFilePath()){ $this->errorMess=$this->getError(); return false; }
$name=$_FILES[$fileField]['name']; $tmp_name=$_FILES[$fileField]['tmp_name']; $size=$_FILES[$fileField]['size']; $error=$_FILES[$fileField]['error']; if(is_Array($name)){ $errors=array(); for($i=0; $i<count($name); $i++){ if($this->setFiles($name[$i], $tmp_name[$i], $size[$i], $error[$i])){ if(!$this->checkFileSize() || !$this->checkFileType()){ $errors[]=$this->getError(); $return=false; } }else{ $error[]=$this->getError(); $return=false; } if(!$return) $this->setFiles(); } if($return){ $fileNames=array(); for($i=0; $i<count($name); $i++){ if($this->setFiles($name[$i], $tmp_name[$i], $size[$i], $error[$i])){ $this->setNewFileName(); if(!$this->copyFile()){ $errors=$this->getError(); $return=false; }else{ $fileNames[]=$this->newFileName; } } } $this->newFileName=$fileNames; } $this->errorMess=$errors; return $return; } else { if($this->setFiles($name, $tmp_name, $size, $error)){ if($this->checkFileSize() && $this->checkFileType()){ $this->setNewFileName(); if($this->copyFile()){ return true; }else{ $return=false; } }else{ $return=false; } }else{ $return=false; } if(!$return) $this->errorMess=$this->getError();
return $return; } } private function copyFile(){ if(!$this->errorNum){ $filePath=rtrim($this->filePath, '/').'/'; $filePath.=$this->newFileName;
if(@move_uploaded_file($this->tmpFileName, $filePath)) { return true; }else{ $this->setOption('errorNum', -3); return false; } }else{ return false; } }
private function setFiles($name="", $tmp_name='', $size=0, $error=0){ $this->setOption('errorNum', $error); if($error){ return false; } $this->setOption('originName', $name); $this->setOption('tmpFileName', $tmp_name); $arrStr=explode('.', $name); $this->setOption('fileType', strtolower($arrStr[count($arrStr)-1])); $this->setOption('fileSize', $size); return true; }
function getNewFileName(){ return $this->newFileName; } function getErrorMsg() { return $this->errorMess; } }
|
<?php require "FileUpload.class.php"; $up=new FileUpload(array('isRandName'=>true,'allowType'=>array('txt', 'doc', 'php', 'gif'),'filePath'=>'./uploads/', 'maxSize'=>200000)); echo '<pre>'; if($up->uploadFile('upload')){ print_r($up->getNewFileName()); }else{ print_r($up->getErrorMsg()); } echo '</pre>';
|
<form action="upload.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="100000000"> <input type="file" name="upload"> <br> <input type="submit" name="sub" value="upload file"><br> </form>
|
<form action="upload.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="100000000"> <input type="file" name="upload[]"> <br> <input type="file" name="upload[]"> <br> <input type="file" name="upload[]"> <br> <input type="file" name="upload[]"> <br> <input type="submit" name="sub" value="upload file"><br> </form>
|
利用上面的这个文件上传类,我们便可以轻松地实现文件上传,非常之便捷。
文件下载
对于浏览器无法直接打开的文件,我们一般只需要设置一下超链接就好了。比如
<a href="a.rar">a.rar</a>
|
点击超链接之后,便会弹出下载的提示框。 可是对于浏览器可以直接打开的文件,例如 1.html,2.php,3.gif 等等文件,如果仍然用这种超链接形式,那就行不通了,浏览器会直接跳转到这个页面。 我们怎样解决这个问题呢?很简单 我们首先要将超链接的文件名改为一个 php 文件,比如上面的链接就可以改为
<a href="a.php">logo.gif</a>
|
这样浏览器会去访问 a.php 文件,那么我们只需要在 a.php 文件中作相应处理即可,例如我们要下载 logo.gif 文件 我们就需要在 a.php 文件最开始设定头部信息,如下
<?php header("Content-Type:image/gif"); header('Content-Disposition: attachment; filename="logo.gif"'); header('Content-Length:300'); readfile("logo.gif"); ?>
|
一般设置三个头部信息就好了 第一个是设置文件传输的类型,第二个是设置传送的内容为附件形式,文件名是 logo.gif,这里的 filename 即为我们下载文件时命名的名字,而不是文件名本身。第三个是设置文件传输大小。 最后设置一下下载的是哪个文件就好了。利用 readfile 方法。 以上便是文件下载所需要的方法。 这样,文件上传和文件下载的方法就全部介绍完啦!