PHP基础三之所有的文件操作

这篇博客详细介绍了PHP中的文件操作,包括写入、读取、打开、关闭文件等,并讲解了换行符的使用。此外,还讨论了GET和POST请求的区别,以及如何进行页面跳转和数据提交。文中提到了文件上传的相关知识,如文件大小限制、文件类型的验证和安全问题。同时,还探讨了表单提交、文件上传的错误处理和优化策略。
摘要由CSDN通过智能技术生成

PHP之所有的文件常见操作

文件操作

将字符串写入文件

$str = "床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。"; # 这里必须要用双引号引起来,只有这样\r\n才会被解析
file_put_contents('./test.txt', $str);
结论与知识点:
1. 所有的写操作都是清空重写
2.  在文件中换行是\r\n
3. \r:回车 光标移动到当前行的最前面
4. \n:换行 将光标向下移动一行
5. 按键盘的回车键做了两步,第一步将光标移动到当前行的最前面,第二步下移一行
6. \r\n都是特殊字符,必须放在双引号内

将整个文件读入一个字符串

//方法一:
echo file_get_contents('./test.txt'), '<br>';//将整个文件读入一个字符串
//方法二:
readfile('./test.txt');
echo '<br>';

打开文件并操作

/*
 * fopen(地址,模式)  打开文件
 * 模式:
 * r:读,w:写,a:追加
 */
 # 打开文件
$fp = fopen('./test.txt', 'w');//打开文件返回文件指针(文件地址)
var_dump($fp);
echo '<br>';
for ($i = 1; $i <= 10; $i++)
    fputs($fp, '关关雎鸠' . "\n"); //写入文件
fclose($fp); //关闭文件

打开文件读取

$fp = fopen('./test.txt', 'r');//打开文件读取
while ($line = fgets($fp)) {
    echo $line . '<br>';
}
fclose($fp);

打开文件追加

# 打开文件追加
$fp = fopen('./test.txt', 'a');
fputs($fp, '在河之洲' . "\n");
fclose($fp);
知识点:
1. 打开文件,返回文件指针,(文件指针就是文件地址),资源类型
2. 打开文件写、追加操作,如果文件不存在,就创建新的文件
3. 打开文件读操作,文件不存在就会报错
4. fputs()写一行,fgets()读一行,fclose()关闭文件

与文件有关的判断

# 是否是文件【is_file()】
echo is_file('./test.txt') ? '是文件' : '不是文件', '<br>';
# 判断文件夹或文件是否存在
echo file_exists('./test.txt') ? '文件存在' : '文件不存在', '<br>';

删除操作

# 删除操作
$path = './test.txt';
if (file_exists($path)):
    if (is_dir($path))
        rmdir($path);
    elseif(is_file($path))
        unlink($path);//unlink是删除文件
endif;

二进制文件操作

知识点:
1. 二进制文件读【fread(文件指针,文件大小)】
2. 文件存储有两种,字符流和二进制流
3. 二进制流的读取按文件大小来读的
$path='./clk.jpg';
$fp=fopen($path,'r');
header('Content-Type:image/jpeg');//告知浏览器下面的代码通过jpg图片方式进行解析
echo fread($fp,filesize($path)); # 二进制读取
# file_get_contents()也可以进行二进制读取
header('Content-Type:image/jpeg');
echo file_get_contents($path);
小结:
1. 文本流有明确的结束符,二进制流没有明确的结束符,通过文件大小判断文件是否读取完毕
2. file_get_contents()既可以进行字符流读取,也可以进行二进制流读取

GET和POST的区别与之对应的请求

知识点:
1. 外观上看:get提交在地址栏上可以看到参数,post提交在地址栏看不到参数
2. 安全性:get不安全,post安全
3. 提交原理:get提交是参数一个一个的提交,post提交是所有参数作为一个整体一起提交
4. 提交数据大小:get提交一般不超过255个字节,post提交的大小取决于服务器
5. 在php.ini中可以设置post提交的大小post_max_size=8M
6. 灵活性:get很灵活,只要有页面的跳转就可以传递参数,post不灵活,post提交需要有表单的参与

跳转方式

html跳转

<a href="index.php?name=tom&age=20">跳转</a>

js跳转

<script type="text/javascript">
location.href='index.php?name=tom&age=20';
location.assign('index.php?name=tom&age=20');
location.replace('index.php?name=tom&age=20');
</script>

php跳转

header('Location:index.php?name=tom&age=20')

post、get、request提交请求的异同

HTML页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="servers.php" method="post">
    语文:<input type="text" name="ch"><br>
    数学:<input type="text" name="math"><br>
    <input type="submit" value="提交" name="button"><br><br>
</form>
<!--超链接跳转提交数据-->
<a href="servers.php?ch=85&math=99">跳转</a>
<br><br>
<!--js提交数据跳转-->
<input type="submit" name="button" onclick="location.href='servers.php?ch=66&math=22'">
</body>
</html>

PHP页面

服务器提交数据的方式知识点:
1. 通过名字获取名字对应的值
2. $_POST:数组类型,保存的POST提交的值
3. $_GET:数组类型,保存的GET提交的值
4. $_REQUEST:数组类型,保存的GET和POST提交的值
if (!empty($_POST)) {
    echo '这是post提交的数据', '<br>';
    echo '语文:' . $_POST['ch'] . '<br>';
    echo '数学:' . $_POST['math'] . '<br>';
}
echo '<hr>';
if (!empty($_GET)) {
    echo '这是get提交的数据', '<br>';
    echo '语文:' . $_GET['ch'] . '<br>';
    echo '数学:' . $_GET['math'] . '<br>';
}
echo '<hr>';
//既能获取get提交又能获取post提交的数据
echo '这是request' . '<br>';
echo '语文:' . $_REQUEST['ch'] . '<br>';
echo '数学:' . $_REQUEST['math'] . '<br>';

使用request的注意点

知识点:
1. 在一个请求中,既有get又有post,get和post的名字是一样的,这时候通过$_REQUEST获取的数据会覆盖
2. 覆盖取决于配置文件的配置,request_order='GP',先获取get,再获取post
例子
if (!empty($_POST)){
    echo '姓名为:'.$_REQUEST['username'];
}
echo "<form method='post' action='?username=berry'>
姓名:<input type='text' name='username'><br>
<input type='submit' name='button' value='提交'>
</form>";

POST表单练习

复选框值的传递

# 复选框值的传递
if (isset($_POST['button'])) {
    print_r($_POST['hobby']);
    echo $_POST['hobby'][0];
}
echo "<form method='post' action=''>
爱好:
<input type='checkbox' name='hobby[]' value='爬山'>爬山
<input type='checkbox' name='hobby[]' value='跑步'>跑步
<input type='checkbox' name='hobby[]' value='打羽毛球'>打羽毛球
<input type='checkbox' name='hobby[]' value='喝酒'>喝酒
<input type='submit' value='提交' name='button'>
</form>";
知识点:
1. 表单提交到本页面需要判断一下是否有post提交
2. 数组的提交表单元素的名字需要带有[],就是复选框

向服务端提交数据练习

if (isset($_POST['button'])){
    echo '姓名:'.$_POST['username'].'<br>';
    echo '密码:'.$_POST['pwd'].'<br>';
    echo '性别:'.$_POST['sex'].'<br>';
    echo '爱好:'.isset($_POST['username'])?implode(',',$_POST['hobby']):'没有爱好'.'<br>';
    echo '籍贯:'.$_POST['jiguan'].'<br>';
    echo '留言:'.$_POST['words'].'<br>';
}
echo '<form action="" method="post">
    姓名:<input type="text" name="username"><br>
    密码:<input type="password" name="pwd"><br>
    性别:<input type="radio" name="sex" value="1" checked="checked">男
    <input type="radio" name="sex" value="0"> 女<br>
    爱好:
    爱好:
    <input type=\'checkbox\' name=\'hobby[]\' value=\'爬山\'>爬山
    <input type=\'checkbox\' name=\'hobby[]\' value=\'跑步\'>跑步
    <input type=\'checkbox\' name=\'hobby[]\' value=\'打羽毛球\'>打羽毛球
    <input type=\'checkbox\' name=\'hobby[]\' value=\'喝酒\'>喝酒
    <br>
    籍贯:
    <select name="jiguan">
        <option value="021">上海</option>
        <option value="022">湖北</option>
        <option value="033">南京</option>
    </select><br>
    留言:<textarea name="words" rows="10" cols="40"></textarea><br>
    <input type="submit" name="button" value="提交">
</form>';

有关$_FILES的知识点

文件域

# 文件域
echo '<input type="file" name="image">';

表单的enctype属性

知识点:
 默认情况下,表单传递是字符流,不能传递二进制流,通过设置表单的enctype属性传递复合数据。
 enctype属性的值有:
 1. application/x-www-form-urlencoded:默认,表示传递的是带格式的文本数据
 2. multipart/form-data:复合的表单数据(字符串,文件)文件上传必须设置此值
 3. text/plain:用于向服务器传递无格式的文本数据,主要是用户电子邮件

$_FILES的相关小知识

超全局变量$_FILES是一个二维数组,用来保存客户端上传到服务器的文件信息,二维数组的行是文件的名称,列有5个
1. $_FILES[]['name']:上传的文件名
2. $_FILES[]['type']:上传的类型,这个类型是MIME类型
3. $_FILES[]['size']:文件的大小,以字节为单位
4. $_FILES[]['tmp_name']:文件上传时的临时文件
5. $_FILES[]['error']:错误编码值,有0,1,2,3,4,6,7;0表示正确

$_FILES[][‘error’]详解

1. 0:正确
2. 1:文件超过了php.ini中允许的最大值
3. 2:文件大小超过了表单允许的最大值
4. 3:只有部分文件上传
5. 4:没有文件上传
6. 6:找不到临时文件
7. 7:文件写入失败

MAX_FILE_SIZE知识点

echo '<form method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="2">
<input type="file" name="face">
<input type="submit" name="button" value="上传">
</form>';
# 设置表单允许的最大值2个字节,注意,MAX_FILE_SIZE必须在文件域的上面
# MAX_FILE_SIZE表示表单允许上传文件的最大值,它也就是$_FILES['error']=2的时候的错误

move_upload_file()的使用

解释:
1. 将上传文件移动到指定位置
2. 函数:move_upload_file(临时地址,目标地址)
if(!empty($_POST)){
    if ($_FILES['face']['error']==0){
        move_uploaded_file($_FILES['face']['tmp_name'],'./'.$_FILES['face']['name']);
    }else{
        echo '上传有误';
        echo ',错误码:'.$_FILES['face']['error'];
    }
}
echo '<form method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="2158556">
<input type="file" name="face">
<input type="submit" name="button" value="上传">
</form>';
注意:上传同名的文件会将原有的文件覆盖

与文件有关的配置

1. post_max_size=8M:表单允许的最大值
2. upload_max_filesize=2M:允许上传文件大小
3. upload_tmp_dir=F:\PS\tmp:指定临时文件地址
4. file_uploads=On:是否允许文件上传
5. max_file_uploads=20:允许同时上传20个文件

优化文件上传

方法:
方法一:通过时间戳做文件名
方法二:通过uniqid()实现,获取一个带前缀、基于当前时间微秒数的唯一ID
# 方法一:
$path='clk.jpg';
echo time().rand(100,9999).strrchr($path,'.');# strrchr()查找指定字符在字符串中最后一次出现的位置
# 方法二:通过uniqid()实现
$path='clk.jpg';
echo uniqid().strrchr($path,'.'),'<br>';//生成唯一的id
echo uniqid('goods_').strrchr($path,'.'),'<br>';//生成带有前缀的唯一的文件名
echo uniqid('goods_',true).strrchr($path,'.').'<br>';//生成带有前缀的唯一的带有随机数的id

验证文件格式

方法:
方法一:判断文件的扩展名(不能识别文件伪装)
方法二:使用mime类型(不能识别文件伪装)
方法三:php_fileinfo扩展(可以防止文件伪装)
# 方法一:判断文件的扩展名(不能识别文件伪装)
# 将文件的后缀和允许的后缀进行对比
if (!empty($_POST)) {
    $allow = array('.jpg', '.png', '.gif');//允许的文件扩展名
    $ext = strrchr($_FILES['face']['name'], '.');//上传文件扩展名
    if (in_array($ext, $allow))
        echo '允许上传' . '<br>';
    else
        echo '不允许上传' . '<br>';
}
# 方法二:使用mime类型(不能识别文件伪装)
if (!empty($_POST)) {
    $allow = array('image/jpeg', 'image/png', 'image/gif');//允许的类别
    $ext = $_FILES['face']['type'];//上传文件类别
    if (in_array($ext, $allow))
        echo '允许上传' . '<br>';
    else
        echo '不允许上传' . '<br>';
}
# 方法三:
if (!empty($_POST)) {
    //第一步:创建finfo资源
    $info = finfo_open(FILEINFO_MIME_TYPE);
    var_dump($info);
    //第二步:将finfo资源和文件作比较
    $mime = finfo_file($info, $_FILES['face']['tmp_name']);
    //第三步比较是否合法
    $allow = array('image/jpeg', 'image/png', 'image/gif');
    echo in_array($mime,$allow)?'合法':'不合法';
}
三个方法所涉及的html代码
echo '<form method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="2158556">
<input type="file" name="face">
<input type="submit" name="button" value="上传">
</form>';
方法三的注意点:
1. 在php.ini中开启php_info扩展extension=php_fileinfo.dll
2. 注意,开启fileinfo扩展后,就可以使用finfo_*的函数了。
验证格式的总结
1. 可以验证扩展名
2. 可以验证文件的类型
3. 通过file_info扩展(可以防止伪装)

有关文件上传的例题

function check($file){
    if ($file['error']!=0){
        switch ($file['error']){
            case 1:
                return '文件大小超过了php.ini中允许的最大值,最大值是:'.ini_get('upload_max_filesize');
            case 2:
                return '文件大小超过了表单允许的最大值';
            case 3:
                return '只有部分文件上传';
            case 4:
                return '没有文件上传';
            case 6:
                return '找不到临时文件';
            case 7:
                return '文件写入失败';
            default:
                return '未知错误';
        }
    }
    # 2.验证格式
    $info=finfo_open(FILEINFO_MIME_TYPE);
    $mime=finfo_file($info,$file['tmp_name']);
    $allow=array('image/jpeg', 'image/png', 'image/gif');
    if (!in_array($mime,$allow)){
        return '只能上传'.implode(',',$allow).'格式';
    }
    # 3.验证大小
    $size=283558845;
    if ($file['size']>$size){
        return '文件大小不能超过'.number_format($size/1024,1).'K';
    }
    # 4.验证是否是HTTP上传
    if (!is_uploaded_file($file['tmp_name'])){
        return '文件不是HTTP_POST上传的';
    }
    return null;
}
# 修改时区,在php.ini中修改date.timezone=RPC
# 将时间戳转换格式
echo date('Y-m-d H:i:s',time());//将时间戳转换为年月日时分秒的格式
# 如果没有time函数也是表示的是当前时间的时间戳
# 注意:php的执行可以不要appache的参与
if (isset($_POST['button'])){
    if (check($_FILES['face'])){
        echo check($_FILES['face']);
    }else{
        //文件上传,上传的文件保存到当天的文件中
        $foldername=date('Y-m-d');
        $folderpath="./upload/{$foldername}";//文件夹路径
        echo file_exists($folderpath);
        if (!file_exists($folderpath)){
            mkdir($folderpath,0777,true);
        }
        $filename=uniqid('',true).strrchr($_FILES['face']['name'],'.');//文件名
        $filepath="{$folderpath}/{$filename}";//文件路径
        if(move_uploaded_file($_FILES['face']['tmp_name'],$filepath))
            echo '上传成功';
        else
            echo '上传失败'.'<br>';
    }
}
echo '<form method="post" enctype="multipart/form-data" action="">
<input type="hidden" name="MAX_FILE_SIZE" value="2158556">
<input type="file" name="face">
<input type="submit" name="button" value="上传">
</form>';

其实这只是做到了相对安全的文件上传,黑客可以通过多种方式绕过:
1. 文件名大小写绕过(比如:AsP,PhP之类的文件名绕过黑名单检测
2. 名单列表绕过:用黑名单里没有的名单进行攻击,比如黑名单里没有cer或者asa之类的
3. ::$DATA绕过
4. htaccess文件:配合名单列表绕过,上传一个自定义的.htaccess就可以轻松绕过检测
5. 空格绕过
6. 配合解析漏洞
7. 双后缀名绕过
8. mime绕过
9. %00,0x00,0x0a截断
10.特殊文件名test.php.或者test.asp_

解析漏洞:

Apache解析漏洞

test.php.aaa.bbb.ccc任意不属于黑名单且不属于Apache解析白名单之内的后缀名
Apache会从最后的位置开始解析,直到遇到一个能解析的扩展名为止

IIS解析漏洞

  1. IIS6.0在解析asp格式的时候有两个解析漏洞:
    • 如果目录名以.asp、.asa、.cer、.cdx字符串结尾,那么这个目录下所有的文件都会按照asp去解析eg:“test.asp/1.jpg”
    • 只要文件名中含有.asp、.asa、.cer、.cdx会优先按asp解析(/xx.asp;.jpg /xx.asp:.jpg)
  2. IIS7.0/7.5是对php解析时有一个类似于Nginx的解析漏洞对任意文件名只要在URL后面追加上字符串“/任意文件名.php”就会按照php的方式去解析
  3. Nginx<=0.8.37解析漏洞:在Fast-CGI关闭的情况下,在一个文件路径(/xx.jpg)后面加上%00.php会将 /xx.jpg%00.php 解析为 php 文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值