PHP 高级特性三之文件上传和下载 | 珊瑚贝

综述

上一节我们学习了文件的读写操作,这一节我们来看一下文件上传和下载的相关内容。

文件上传

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,让我们来感受一下吧!

FileUpload.class.php
<?php
class FileUpload {

private $filePath; //指定上传文件保存的路径
private $allowType=array('gif', 'jpg', 'png', 'jpeg'); //充许上传文件的类型
private $maxSize=1000000; //允上传文件的最大长度 1M
private $isRandName=true; //是否随机重命名, true false不随机,使用原文件名
private $originName; //源文件名称
private $tmpFileName; //临时文件名
private $fileType; //文件类型
private $fileSize; //文件大小
private $newFileName; //新文件名
private $errorNum=0; //错误号
private $errorMess=""; //用来提供错误报告



//用于对上传文件初使化
//1. 指定上传路径, 2,充许的类型, 3,限制大小, 4,是否使用随机文件名称
//让用户可以不用按位置传参数,后面参数给值不用将前几个参数也提供值
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;
//默认返回值为True
$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)){
//错误代号必须也是Array,因为一个文件对应一个错误代号
$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;
}
}

//设置和$_FILES有关的内容
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;
}

}
 upload.php
<?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>';
1
 form.html
<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.html
<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 方法。 以上便是文件下载所需要的方法。 这样,文件上传和文件下载的方法就全部介绍完啦!

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?