php文件处理与文件上传

Posted cscsdvdsv

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php文件处理与文件上传相关的知识,希望对你有一定的参考价值。

文件处理

序列化与反序列化

序列化:将一个变量代表的内存数转换为字符串形式,并持久保存到硬盘上
反序列化:将序列化后保存的字符串数据恢复

序列化:形式 $v1=123; $s1=serialize($v1); file_put_contents(‘要保存的目标文件’,$s1);
反序列化: 形式 $s1=file_get_contents(‘保存的文本’); $v1=unserialize($s1);

__sleep():用于对象的序列化,只能将属性保存,方法会忽略,会自动调用这个方法,改方法必须返回一个数组,数组中是计划要进行序列化的属性名,返回的可以改 (要引用文件)
__wake up(): 用于对象的序列化 (要引用文件)

序列化的做法:
$v1 = 123; //这是一个变量,代表任意的内存数据
$s1 = serialize( $v1 ); //将任何类型的变量数据,转换为“字符串”
file_put_contents( ‘要保存的目标文本文件’, $s1);//将该字符串,保存到一个文件里(就是硬盘数据)
反序列化的做法:
$s1=file_get_contents(‘保存序列化的目标文件’);//从一个文件里读出其中的所有字符
$v1 = unserialize( $s1 ); //将该字符串数据,反序列化转换为变量(数据)

__sleep():用于对象的序列化
1,对一个对象进行序列化,只能将其属性数据“保存起来”,而方法被忽略(方法不是数据)
2,对象的序列化的时候,会自动调用该对象所属类的这个魔术方法:__sleep()(前提是有该方法)。且,此时,该方法必须返回一个数组,数组中是“计划”要进行序列化的属性名;

__wakeup():用于对象的反序列化
1,对一个对象进行反序列化,其实是恢复其原来保存起来的属性数据,而且,此时必然需要依赖该对象原本的所属类;
2,对象在反序列化的时候,会自动调用该对象所属类的这个魔术方法:__wakeup()

目录操作

创建目录
bool mkdir(string $pathname [,int $mode = 0777 [,bool $recursive=false] ] )
参数:$pathname 创建的目录名称 $mode指定目录的权限
$mode在windows系统上,权限设置无效
权限是一个八进制的值:0777
第1位表示所有者权限,第2位表示的所在组权限,第3位表示所有的人权限
每一位权限也是由三个值构成:1为可执行、2为可写、4为可读
举例:如“7”=1+2+4
举例:0644、04444、0755等。
提示:在windows下的只读权限为:0444
$recursive递归创建,true表示即使上层目录不存在则会自动创建
返回值:创建成功返回true,失败返回false
提示:文件夹不能更换

判断是不是一个目录
bool is_dir(string $filename); 判断给定文件名是否是一个目录

判断目录或文件是否存在
bool file_exists(string $filename) 检查文件或目录是否存在

删除目录
bool rmdir(string $dirname); 目录必须是空才能删除

更改目录的访问权限
bool chmod (string $filename,int $mode) 改变文件模式
$filename 文件名称 $mode八进制的权限值

取得文件夹访问权限
int fileperms(string $filename);
例如:$dir=”./image”;
$int=fileperms($dir); //这返回的是十进制的权限值
$int= base_convert(int, 10, 8); //将十进制转为8进制
echo substr($int,-4); //8进制只要后四位,只取后四位
//也可以封装成一个函数
function getFileperms($fileName)
return substr(base_convert(fileperms($fileName),10,8),-4);

目录重命名或移动目录
bool rename (string $oldname,string $newname);

打开目录
resource opendir(string $path); 打开目录句柄,成功返回一个句柄(资源),失败false

读取目录中的条目
string readdir([resource $dir_handle]); 从目录句柄中读取条目

显示中文目录或文件
string iconv(string $in_charset, string $out_charset,string $str);
参数:$in_charset,原字符编码;$out_charset,转换后字符编码;$str,要转换的字符串。

关闭目录
void closedir(resource $dir_handle);关闭目录句柄

目录操作封装

<?php
header("content-type:text/html;charset=utf-8");
/**
 *  遍历出一个文件夹下所有的文件和目录
 *封装一个函数,可以统计出一个文件夹下面目录的数量和文件的数量。
 *封装一个函数,可以删除目录
 */

//测试:遍历出一个文件夹下所有的文件和目录,数组
$filename="./xx";
$result=fileDirectory($filename);
dump($result);

//测试:遍历出一个文件夹下所有的文件和目录,输出
$result=readDirs($filename);

//测试:封装一个函数,可以统计出一个文件夹下面目录的数量和文件的数量。
$filename="./xx";
$result=getFileCount($filename);
dump($result);

//测试:封装一个函数,可以删除目录
$filename="./jj";
$result=del($filename);
dump($result);


/**
 * 遍历出一个文件夹下所有的文件和目录
 * @param $filename string 文件夹或文件的路径 $deep int 默认0 文件的递归深度
 * @return $file_list arr 二维数组
 */
function fileDirectory($filename, $deep=0) 
    if(! file_exists($filename)) return false; //如果传递过来的路径不存在直接返回false
    static $file_list = array();   //存储所有的文件信息,二维数组
    $handle = opendir($filename);
    while(false !== ($rec = readdir($handle)) )    
        if($rec == '.' || $rec == '..') continue;  //目录下有.和..跳出循环      
        //当前的文件信息,存储到数组中,这个数组是二维数组中一个
        $fileinfo['filename'] = $rec;
        $fileinfo['deep'] = $deep;
        $file_list[] = $fileinfo;
        //判断当前读取到的是否为目录
        if(is_dir($filename.'/'.$rec))   
            fileDirectory($filename.'/'.$rec,$deep+1);
        
    
    //循环读取完毕后,关闭数据流
    closedir($handle);
    //返回读取的结果
    return $file_list;


/**
 * 遍历出一个文件夹下所有的文件和目录
 * @param $filename string 文件夹或文件的路径 $deep int 默认0 文件的递归深度
 */
function readDirs($filename,$deep=0) 
    if(! file_exists($filename)) return false; //如果传递过来的路径不存在直接返回false
    $handle = opendir($filename);
    while(false !== ($rec = readdir($handle)) )   
        if($rec == '.' || $rec == '..') continue; //目录下有.和..跳出循环
        $rec = iconv("gbk", "utf-8", $rec);     
        echo str_repeat('&nbsp;',$deep*4),$rec,'<br />';
        //判断当前读取到的是否为目录
        if(is_dir($filename.'/'.$rec))   
            readDirs($filename.'/'.$rec,$deep+1);
        
    
    //循环读取完毕后,关闭数据流
    closedir($handle); 



/**
 * 封装一个函数,可以统计出一个文件夹下面目录的数量和文件的数量。
 * @param $filename string 文件夹或文件的路径
 * @return arr 一维数组
 */
    function countDirA($filename,&$dirNum=0,&$fileNum=0)
        // 打开句柄
        $handle = opendir($dir);
        // 当文件名为0的时候,判断是否全等于false,不全等于false继续执行
        while(false !== ($rec = readdir($handle)) )   
            // 过滤.和..
            if($fileName == '.' || $fileName == '..') continue;
            // 拼接完整路径
            $newFile = "$dir/$fileName";
            if(is_dir($newFile))
                $dirNum++;
                countDirA($newFile,$dirNum,$fileNum);
            else
                $fileNum++;
            
        
        // 关闭句柄
        closedir($handle);
        return array($dirNum,$fileNum);
    


/**
 * 封装一个函数,可以删除目录
 * @param $filename string 文件夹或文件的路径
 */
function del($filename)    
    if(! file_exists($filename)) return false; //如果传递过来的路径不存在直接返回false
    $handle = opendir($filename);
    while(false !== ($rec=readdir($handle)))    
        if($rec == '.' || $rec == '..') continue; //目录下有.和..跳出循环
            //开始删除,是目录递归
            if(is_dir($filename.'/'.$rec))   
                del($filename.'/'.$rec);
             else 
                unlink($filename.'/'.$rec);
            
    
    //关闭数据流
    closedir($handle);
    //删除本身的空目录再把自己本身函数结束,返回上一层函数
    return rmdir($filename);



/**
 * @param $n 任意类型
 * @return var_dump(参数)  打印出详细信息
 */
function dump($n=null) 
    echo "<hr />";
    echo "<pre>";
    var_dump($n);
    echo "</pre>";
    echo "<hr />";

文件操作

打开文件

resource fopen(string $filename,string $mode);打开文件或URL
参数:$filename 要打开的文件名 $mode文件打开方式
其中$mode文件打开方式:
“r”只读方式打开,指针移到文件开头
“r+”读写方式打开
“w”写入方式打开,指针移到文件开头。如果文件不存在,会自动创建。清空写
“w+”读写方式打开
“a”追加方式打开,指针移到文件结尾。如果文件不存在,会自动创建。追加写
“a+”读写方式打开
“b”以二进制数据打开,为了安全起见,在打开方式后加上”b”参数。文便内容的移植。
返回:打开成功返回一个句柄(资源),打开失败返回FALSE。

打开模式
基本模式:
• R 读
• W 清空写,文件存在直接打开同时清空。
• A 追加写,文件存在直接打开
• X 新建写,只能新建文件进行操作!就是写操作,与w类似
+扩展模式:
扩展的操作,都可以完成读写操作。
差异与基本模式一致!
• R+ 读写,打开任意文件(无论是存在还是不存在),文件内容不会被清空,依据指针位置,完成读,写操作。其中,写,会替换原有指针位置字节。
• W+ 读写,打开时,同时清空内容,之后指针在哪里,就 在哪里完成读写!与R+唯一的差异,就是会清空文件内容!
• A+ 读写,打开任意文件,不会清空内容,指针仅仅影响读操作。不影响写操作,仅仅可以在末尾写!
• X+ 读写,新建才能打开,依据文件指针位置,进行读写操作!

关闭文件

bool fclose(resource $handle); 关闭一个已打开的文件指针

读取文件内容

读取指定大小
string fread(resource $handle,int $length); 读取文件(可安全用于二进制文件)
$handle打开的文件指针; $length读取的字节数,一般1kb=1024b
返回读取的内容

读行
string fgets(resource $handle[,int $length]); 从文件指针中读取一行
参数:$handle 文件指针 $length可以指定读取的字节数.从handle指向的文件中读取一行并返回长度最多为length-1字节的字符串.碰到换行符(包括在返回值中) 或者已经读取了 length - 1 字节后停止(看先碰到那一种情况)。如果没有指定 length,则默认为 1K,或者说 1024 字节。
返回读取的字符串

把文件读入数组
array file(string $filename[,int $flags=0]); 把整个文件读入一个数组中
参数:
$filename文件名,不用打开、关闭文件。
$flags,附加选项。
FILE_USE_INCLUDE_PATH(1),在 include_path 中查找文件。
FILE_IGNORE_NEW_LINES(2),在数组每个元素的末尾不要添加换行符
FILE_SKIP_EMPTY_LINES(4),跳过空行
返回:返回整个文件的一个数组。

把文件内容读入字符串(不用打开)
string file_get_contents(string $filename) 将整个文件读入一个字符串
提示:不用打开文件,也不用关闭文件

写入文件

写入文件内容
int fwrite(resource $handle,string $string [,int $length]); 写入文件(可安全用于二进制文件)
参数:$handle打开的文件指针;$string要写入的字符串;$length表示要写入的字符数

将字符串写入文件,不用打开
int file_put_contents(string $filename,mixed $data) 将一个字符串写入文件

拷贝文件

bool copy(string $source, string $dest);

删除文件

bool unlink(string $filename); 删除文件

文件指针

bool feof ( resource $handle ) 测试文件指针是否到了文件结束的位置
定位指针: fseek(句柄,位置),位置从0开始递增
获取指针位置: ftell(句柄); 获取指针位置

取得文件属性

文件大小:filesize($filename)
创建时间:filectime($filename)
访问时间:fileatime($filename)
修改时间:filemtime($filename) 时间戳
文件权限:fileperms($filename)

文件锁

默认的,PHP的文件操作函数,不是阻塞状态。自由操作状态。
当需要,一个脚本操作而需要阻塞另外的脚本操作时,需要用到文件锁。
锁操作流程:
先加锁,检测锁是否加成功,如果成功再使用!
锁定类型:
读锁: s-Lock(share-lock)共享锁,读操作前,期望增加的锁定。导致,允许并发读,阻塞额外的写操作。
写锁: x-lock(exclusive-lock)排他锁,独占锁,写操作前,尝试添加的锁定类型。导致,其他脚本不能读也不能写。
意向锁:所有的操作资源的脚本都遵循一个约定来使用文件锁(约定)。
函数:
Flock(句柄, 类型)
函数用于添加PHP 的文件锁定。(,添加意向锁。)
类型:
LOCK_SH 读锁
LOCK_EX 写锁
语法如下:
1.

2.

2,需要等待(被阻塞)1 执行完才会执行!
强调,一定要判断锁是否可以添加成功! 可以通过LOCK_NB来在锁定失败时,不阻塞,但是在windows下不被支持:

解锁
Flock($handle, LOCK_UN)来强制解锁,
Fclose()自动解锁!

文件上传

上传文件是通过表单来上传的,表单需添加enctype属性 enctype=”multipart/form-data”
将接收到的文件,存储在 上传临时目录中。默认为 服务器所在操作系统的临时目录。
该临时文件有效期为脚本周期!(时间短)在C/windows/Temp
在临时文件未消失前,将其持久性存储!Move_uploaded_file(临时文件地址,目标文件地址);
通过预定义变量$_FILES获得所有的上传文件的信息。也可以在php.ini中修改上传文件的临时目录;upload_tmp_dir = 绝对路径

$_FILFS

上传文件的信息和数据,都保存在$_FILFS全局数组中,不在$_POST中,$_POST是普通表单信息

name:原文件名称;
type:上传文件的后缀名;
tmp_name:上传文件的二进制数据,在服务器端的保存位置。
error:上传文件的错误编号。
size:上传文件大小

上传文件错误代码
第5个错误为上传文件大小为0
1为文件超过了php对上传文件大小的设置 upload_max_filesize
2为文件过大,超过表单中元素 MAX_FILE_SIZE
3文件没有上传完
4没有上传文件
6临时上传目录没找到 可以在ini中改;upload_tmp_dir=
7临时文件写入失败 磁盘空间不足,权限不允许

判断文件是否通过HTTP POST上传的
语法:bool is_upload_file(string $filename) 参数:$filename是上传的临时文件
$filename就用的是$_FILES[‘uploadFile’][‘tmp_name’]

将上传文件移动到新位置
语法:bool move_upload_file(string $filename,string $destination);
参数: $filename临时文件 $destination目标文件,一般将目标文件复制到网站空间

生成唯一ID文件名
语法:string uniqid([string $prefix=””[,bool $more_entropy = false] ])
参数:如果省略2个参数则生成13位的随机数. $prefix给随机数添加前缀
$more_entropy后面添加一个子随机数

验证上传文件是否有错误
if($_FILES[‘uploadFile’][‘error’] != 0)exit(‘上传文件发生错误’);

上传文件大小判断
(1) 修改php.ini中的upload_max_filesize配置项(单个文件大小)系统默认上传文件大小为2M
(2) 在表单中使用隐藏域来存储文件的最大允许上传的值
提示:要写在<input type=”file”>之前才有效
如果超过php.ini中的max_file_size指定的最大值,则$_FILES[‘upload’][‘error’]=2
(3) 在服务器端也要进行大小判断

使用php.fileinfo对MIME类型进行检查
在php.ini中开启extension=php_fileinfo.dll

finfo_file()
返回指定文件的信息(字符集,MIME类型)
语法:string finfo_file(resource $finfo,string $file_name=NULL);
参数:$finfo资源 $file_name指定取出信息的文件名

finfo_open()
创建一个检查MIME类型的资源
语法:resource finfo_open([int $options=FILEINFO_NONE]);
参数:如果是默认参数,则返回含有字符集的MIME类型,如果使用FILEINFO_MIME参数,只返回MIME类型。

多文件上传处理

情况一:表单中name值不同

例如:商品图片:<input type=”file” name=”goods_image”><br />
用户头像: <input type=”file” name=”user_logo”><br />
品牌图片:<input type=”file” name=”brand_image”><br />
print_r($_FILES);
$_FILES中,每个元素,对应一个5个元素的数组:
此时,需要处理那个,就使用哪个数组即可!

情况二:表单中name值相同

例如:商品图片一:<input type=”file” name=”goods_image[]”><br>
商品图片二:<input type=”file” name=”goods_image[]”><br>
商品图片三:<input type=”file” name=”goods_image[]”><br>
或者:<input type=”file” name=”img[]” multiple=”multiple”>和上面也一样
print_r($_FILES);打印出结果
把相关的整理到一个元素中,该元素对应5个元素(name, type, tmp_name, error, size)的数组。
每个name对应3个值,其他也是一样
下标一致的属于一个文件,只要获得每个下标,就可以通过下标获得对应的5个元素值!

name值相同的文件处理代码

<?php
/**
 * 上传工具类,单文件直接实列化请求UploadFile,多文件请求uploadMulti
 * 同时提供了一些对外界修改参数的函数
 */
class Upload 
    private $_error;                   // 存储当前错误信息
    private $_upload_path;             // 上传目录
    private $_prefix;                  // 前缀
    private $_max_size;                    // 最大size
    private $_ext_list;                    // 允许的后缀列表
    private $_mime_list;               // 允许的MIME列表
    private $_ext_mime = array(            // 图片对应的MIME表
        '.jpeg' => 'image/jpeg',
        '.jpg' => 'image/jpeg',
        '.png' => 'image/png',
        '.gif' => 'image/gif'
    );

    public function __construct() 
        // 为选项指定默认值
        $this->_error = array();
        $this->_upload_path = './';
        $this->_prefix = '';
        $this->_max_size = 5 * 1024 * 1024;
        $this->_ext_list = array('.jpg', '.gif', '.png', '.jpeg');
        $this->_setMIMEList($this->_ext_list);
    

    /**
    * 通过允许的后缀列表获得相对应允许的MIME列表
    * @param string $ext_list 允许的后缀列表
    * @return void 赋值到 MIME列表属性上
    */
    private function _setMIMEList($ext_list) 
        $mime_list = array();
        // 遍历获得每一个后缀名
        foreach($ext_list as $ext) 
            // 利用当前后缀名,获取对应的MIME,存储到MIME列表中!
            $mime_list[] = $this->_ext_mime[$ext];
        
        // 赋值到 MIME列表属性上!
        $this->_mime_list = $mime_list;
    

    //下面的提供修改这些配置属性的方法:setUploadPath  setPrefix  setMaxSize setExtList

    /**
    * 提供修改路径的方法
    * @access public
    * @param string $upload 目录字符串 默认'./'
    * @return void 目录不存在采用默认,存在采用传入的参数
    */
    public function setUploadPath($upload_path) 
        // 不允许将不存在的目录设置为上传目录
        if (is_dir($upload_path)) 
            $this->_upload_path = $upload_path;
         else 
            trigger_error('上传目录设置失败,采用默认');
        
    

    /**
    * 提供修改前缀的方法
    * @access public
    * @param string $prefix 默认''
    * @return void 把传入的前缀保存到_prefix
    */
    public function setPrefix($prefix) 
        $this->_prefix = $prefix;
    

    /**
    * 提供修改最大大小的方法
    * @access public
    * @param int $max_size 最大大小 默认1*1024*1024
    * @return void 将传入的尺寸保存到_max_size
    */
    public function setMaxSize($max_size) 
        $this->_max_size = $max_size;
    

    /**
    * 提供修改允许的后缀名列表
    * @param array $ext_list 后缀名组成的数组,默认array('.jpg', '.gif', '.png', '.jpeg');
    * @return void 将传入的数组保存到_ext_list,并调用_setMIMEList方法生成对应的MIME类型
    */
    public function setExtList($ext_list) 
        $this->_ext_list = $ext_list;
        $this->_setMIMEList($ext_list);
    

    /**
     * 文件上传(业务逻辑判断)函数
     * 一次上传(判断)一个文件
     * @param array $file_info 某个临时上传文件的5个信息,由$_FILES中获得!
     * @return string:成功,目标文件名;false: 失败
     */
    public function uploadFile($file_info) 
        // 判断是否有错误
        if ($file_info['error'] != 0) 
            $this->_error[] = '上传文件存在错误';
            return false;
        

        // 判断文件类型
        // 后缀名
        $ext_list = $this->_ext_list;              // 允许的后缀名列表
        $ext = strrchr($file_info['name'], '.');
        if (! in_array($ext, $ext_list)) 
            $this->_error[] = '类型,后缀不合法';
            return false;
        

        // MIME
        $mime_list = $this->_mime_list;             // 允许的mime列表!
        if (! in_array($file_info['type'], $mime_list)) 
            $this->_error[] = '类型,MIME不合法';
            return false;
        

        // PHP检测MIME
        $finfo = new Finfo(FILEINFO_MIME_TYPE);
        $mime_type = $finfo->file($file_info['tmp_name']);
        if (! in_array($mime_type, $mime_list)) 
            $this->_error[] = '类型,PHP检测MIME不合法';
            return false;
        

        // 判断大小
        $max_size = $this->_max_size;              // 允许的最大尺寸
        if ($file_info['size'] > $max_size) 
            $this->_error[] = '文件过大';
            return false;
        

        // 设置目标文件地址
        // 上传目录
        $upload_path = $this->_upload_path;
        // 采用子目录存储
        // 获取当前需要的子目录名(目录/小时)
        $sub_dir = date('YmdH') . '/';             // 当前的年月日时
        // 是否存在
        if (! is_dir($upload_path . $sub_dir)) 
            // 不存在,创建
            mkdir($upload_path . $sub_dir);
        
        // 目标文件名
        $prefix = $this->_prefix;// 前缀
        $dst_name = uniqid($prefix, true) . $ext;

        // 是否为HTTP上传文件的检测
        if (! is_uploaded_file($file_info['tmp_name'])) 
            $this->_error = '不是HTTP上传的临时文件';
            return false;
        

        // 移动!
        if (move_uploaded_file($file_info['tmp_name'], $upload_path . $sub_dir . $dst_name)) 
            // 移动成功
            return $sub_dir . $dst_name;          // 仅仅返回 上传目录之后的地址即可!
         else 
            $this->_error = '移动失败';
            return false;
        
    

    /**
     * 获取错误信息
     * @access public
     * @return string 错误信息
     */
    public function getError() 
        return $this->_error;
    

    //示范多文件上传处理,name相同的情况
    //$arrFile = array();
    //$i=0;
    //foreach($file as $key=>$value)  二维数组
    //  foreach($value as $val)     一维数组
    //      $arrFile[$i++][$key] = $val;
    //  
    //  $i = 0;
    //
    //name      取出每个名字          $arr[0~3][name]
    //type      取出每个类型      $arr[0~3][type]
    //tmp_name  取出每个上传的位置   $arr[0~3][tmp_name]
    //error     取出每个错误信息    $arr[0~3][error]
    //size      取出每个大小      $arr[0~3][size]

    /**
     * 上传文件判断函数,多文件上传,name值不同
     * @param array $file_list   比如:$_FILES['image'] 二维数组
     * @return 返回上传目录之后的地址结果
     */
    function uploadMulti($file_list) 
        // 遍历,其中的name元素,得到下标,下面看不懂看177-189
        foreach($file_list['name'] as $key=>$v) 	//传进来的$file_list是二维数组
            // 利用下标 获得对应的5个元素值
            // $file_info每个文件的信息,$key是0开始的整数
            $file_info['name'] = $file_list['name'][$key];
            $file_info['type'] = $file_list['type'][$key];
            $file_info['tmp_name'] = $file_list['tmp_name'][$key];
            $file_info['error'] = $file_list['error'][$key];
            $file_info['size'] = $file_list['size'][$key];

            // 上传该文件即可!
            // 并存储每个文件的上传结果,与$key对应!
            $result_list[$key] = $this->uploadFile($file_info);
        
        // 返回上传结果
        return $result_list;
    

以上是关于php文件处理与文件上传的主要内容,如果未能解决你的问题,请参考以下文章

PHP极速入门

php文件处理与文件上传

php文件处理与文件上传

php 文件上传后缀名与文件类型对照表(几乎涵盖所有文件)

文件上传漏洞学习 6-10

文件上传漏洞与利用