百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

PHP创建、删除、授权文件夹和读写文件的方法V1.2

bigegpt 2024-10-12 06:58 6 浏览

1. 介绍

1.1 介绍

今天福哥带着大家来学习PHP读写文件的方法,虽然现阶段做项目需要用到文件操作的情况不多了,但是免不了在特殊情况下还是需要用到这个技术的。

今天福哥还会给大家讲解PHP创建、删除、授权文件夹的方法,这个技术在提供用户上传功能的项目当中是非常常见的,通过动态建立文件夹将用户上传的文件分布式地保存在不同文件夹下面,避免单个文件夹下面的文件数量过多造成系统故障。

PHP创建、删除、授权文件夹以及读写文件是依靠内建的函数实现的,PHP提供了多种读写文件的函数,我们来逐个介绍一下。

文件夹操作就是目录操作,在Windows系统里面文件夹叫folder,翻译过来就是文件夹,在Linux系统里面文件夹叫directory,翻译过来就是目录。所以创建、删除、授权文件夹就是创建、删除、授权目录。

2. 基本原则

读写文件有一些常识需要大家先了解一下。

  • 读写文件可以是本地电脑上面的文件,也可以是远程网络上面的文件,只要授权了就可以操作。
  • 文件夹操作可以是本地电脑上面的文件夹,也可以是远程网络上面的文件夹,只要授权了就可以操作。
  • 要创建文件需要对创建文件的文件夹有写权限。
  • 读写已经存在的文件只需要对文件有权限。
  • 文件内容分为普通模式和二进制模式,普通模式通过字符串操作,二进制模式通过字节操作。
  • 写文件分为重置写入和追加写入,前者会清空已有内容,后者不会。
  • 通过文件指针可以精确控制读写文件内容的具体位置,但是写入只会覆盖已有内容而不会像编辑器一样插入内容。
  • 当前文件夹通过“.”表示,上一级文件夹通过“..”表示。
  • 任何文件夹都会有“当前文件夹”和“上一级文件夹”。

3. 文件夹

3.1 遍历

遍历文件夹用到opendir、readdir、closedir几个函数,可以通过is_dir、is_file判断文件夹下面的项目是文件还是文件夹,可以通过is_readable判断文件/文件夹是否可读,可以通过is_writable判断文件/文件夹是否可写。

$currDir = __DIR__;

$do = opendir($currDir);
if($do) {
    while ($fi = readdir($do)) {
        if($fi != "." && $fi != ".."){
            $fullPath = __DIR__. "/". $fi;
            $r = (is_readable($fullPath)) ? "可读" : "不可读";
            $w = (is_writable($fullPath)) ? "可写" : "不可写";
            if(is_file($fullPath)){
                echo "<span style='color: gray;'>文件:[". $r. "][". $w. "]". $fullPath. "</span><br />";
            }
            else if(is_dir($fullPath)){
                echo "<span style='color: blue;'>文件夹:[". $r. "][". $w. "]". $fullPath. "</span><br />";
            }
        }
    }
    closedir($do);
}12345678910111213141516171819

3.2 递归遍历

如果需要遍历一个文件夹下面的所有子级文件和文件夹需要用到递归遍历,就是无论是子级还孙级都要遍历出来。

递归遍历很简单,只要在普通遍历的基础之上稍加改动即可实现,首先需要将普通遍历程序封装为一个方法,然后在普通遍历到的项目为文件夹的时候调用一下方法自己,就可以了。

private function listFolder(string $folder){
    $do = opendir($folder);
    if($do) {
        while ($fi = readdir($do)) {
            if($fi != "." && $fi != ".."){
                $fullPath = $folder. "/". $fi;
                $r = (is_readable($fullPath)) ? "可读" : "不可读";
                $w = (is_writable($fullPath)) ? "可写" : "不可写";
                if(is_file($fullPath)){
                    echo "<span style='color: gray;'>文件:[". $r. "][". $w. "]". $fullPath. "</span><br />";
                }
                else if(is_dir($fullPath)){
                    echo "<span style='color: blue;'>文件夹:[". $r. "][". $w. "]". $fullPath. "</span><br />";
                    // 递归遍历关键,自己调用自己
                    $this->listFolder($fullPath);
                }
            }
        }
        closedir($do);
    }
}

protected function user_process(){
    $currDir = __DIR__;
    
    $this->listFolder($currDir);
}123456789101112131415161718192021222324252627

3.3 创建

创建文件夹使用mkdir函数,创建之后可以通过chmod授权,通过chown设置角色。

$currDir = __DIR__;
$newDir = $currDir. "/newFolder";

$ret = mkdir($newDir);
var_dump($ret);
if($ret){
    chmod($newDir, 0755);
    chown($newDir, "tfums", "tfums");
}123456789

3.4 删除

删除文件夹使用rmdir函数。

$currDir = __DIR__;
$newDir = $currDir. "/newFolder";

$ret = rmdir($newDir);
var_dump($ret);12345

3.5 递归删除

如果文件夹含有子级文件/文件夹则需要先删除子级项目才能删除文件夹,这个时候就需要用到递归遍历技术了。

private function removeFolder(string $folder){
    $do = opendir($folder);
    if($do) {
        while ($fi = readdir($do)) {
            if($fi != "." && $fi != ".."){
                $fullPath = $folder. "/". $fi;
                if(is_file($fullPath)){
                    // 删除文件夹下面的文件
                    unlink($fullPath);
                }
                else if(is_dir($fullPath)){
                    $this->removeFolder($fullPath);
                }
            }
        }
        closedir($do);
    }
    // 删除清空后的文件夹
    rmdir($folder);
}

protected function user_process(){
    $currDir = __DIR__;
    $newDir = $currDir. "/newFolder";

    $this->removeFolder($newDir);
}123456789101112131415161718192021222324252627

4. 文件

4.1 创建

创建文件可以有两种方式,最简单的是通过file_put_contents直接创建文件并写入内容,除此之外还有标准方式通过fopen、fclose实现。

4.1.1 file_put_contents

使用file_put_contents创建文件非常简单,返回值为写入文件内容长度。

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

$writeLen = file_put_contents($newFile, "这是福哥新创建的文件");1234

4.1.2 fopen

使用fopen创建文件需要了解更多知识,首先我们需要指定“文件打开方式”这个概念,使用fopen实际上知识打开了一个文件,需要我们指定一个打开文件想要干什么的模式,这个就是文件打开方式。

文件打开方式包括:

  • r,读模式
  • r+,可读可写模式,文件指针在文件开头
  • w,写模式,重置文件内容
  • w+,可读可写模式,重置文件内容
  • a,写模式,文件指针在文件末尾
  • a+,可读可写模式,文件指针在文件末尾
$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

$fo = fopen($newFile, "w");
if($fo){
    $writeLen = fwrite($fo, "这是福哥新创建的文件");
    fclose($fo);   
}12345678

4.2 删除

删除文件使用unlink函数。

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

unlink($newFile);1234

4.3 读文件

读文件有两种方式,简单方式和标准方式。

4.3.1 file_get_contents

使用file_get_contents可以简单的将文件内容读取出来。

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

$fc = file_get_contents($newFile);

var_dump($fc);123456

4.3.2

4.3.3 fopen

使用fopen读文件需要用到while语句配合feof函数通过fgets循环读取。

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

$fc = "";
$fo = fopen($newFile, "r");
if($fo){
    while(!feof($fo)){
        $fc .= fgets($fo);
    }
    fclose($fo);
}

var_dump($fc);12345678910111213

4.3.4

4.4 写文件

写文件也有两种方式,简单方式和标准方式

4.4.1 file_put_contents

使用file_put_contents可以简单的将内容写入文件当中。

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

file_put_contents($newFile, "福哥说这是全部内容了");

$fc = file_get_contents($newFile);

var_dump($fc);12345678

4.4.2 fopen

使用fopen写文件需要用到fwrite函数。

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

$fo = fopen($newFile, "w");
if($fo){
    fwrite($fo, "福哥说这是全部内容了");
    fclose($fo);
}

$fc = "";
$fo = fopen($newFile, "r");
if($fo){
    while(!feof($fo)){
        $fc .= fgets($fo);
    }
    fclose($fo);
}

var_dump($fc);12345678910111213141516171819

4.5 追加写文件

有时候我们不想将文件内容全部重置了,需要在文件现有内容后面追加内容怎么办?

4.5.1 file_put_contents

使用FILE_APPEND标识开启追加模式。

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

file_put_contents($newFile, "福哥又说话了", FILE_APPEND);

$fc = file_get_contents($newFile);

var_dump($fc);12345678

4.5.2 fopen

将文件打开方式设置为追加写模式。

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";

$fo = fopen($newFile, "a");
if($fo){
    fwrite($fo, "福哥又说话了");
    fclose($fo);
}

$fc = "";
$fo = fopen($newFile, "r");
if($fo){
    while(!feof($fo)){
        $fc .= fgets($fo);
    }
    fclose($fo);
}

var_dump($fc);12345678910111213141516171819

4.6 文件指针

我们以可读可写模式打开文件后,通过fseek移动文件指针,在指定位置进行读写操作,这种方式可以避免将文件全部内容加载到内存当中就可以完成很多情况的读写操作,可以解决针对超大文件内容的格式化数据的编辑的难题。

4.6.1 文件数据库

举例:我们制作一个文件数据库,将用户名和密码存储到一个文件里面,用户名和密码格式如下

[用户名(最长50个字符)][密码(取MD5哈希串,所以固定32个字符)][换行]1

然后我们只要保证每一组用户信息都是一样的固定长度就可以通过简单计算知道某一个用户ID对应的用户信息在文件的哪个位置上面,进而通过fseek就可以快速定位并读取用户信息了。

因为我们规定了文件内的用户数据格式,那么通过操控文件指针就可以实现这个文本数据库的增、删、改、查功能了,是不是觉得很神奇?

4.6.2 文本数据库定义

这是文本数据库TFSimpleTextDb的定义。

class TFSimpleTextDb{
    private string $fp;
    private $fo;
    private string $fs;
    private int $lastUserId;
    private int $lastLoginUserId;

    public function conn():bool {
        $this->fp = __DIR__. "/users.tongfu.net.txt";

        // 数据库文件不存在就创建它
        if(!file_exists($this->fp)){
            file_put_contents($this->fp, "");
        }

        // 打开数据库文件
        $this->fo = fopen($this->fp, "r+");

        // 记得当前文件尺寸
        $this->fs = filesize($this->fp);

        return ($this->fo != null);
    }

    public function add(string $user, string $pwd):bool {
        if(strlen($user) > 50){

            return false;
        }

        // 移动文件指针到文件末尾
        fseek($this->fo, $this->fs);

        // 写入一行数据保存用户名和密码
        // 用户名用strpad补位,保证数据结构一致
        $writeLen = fwrite($this->fo, sprintf("%s%s\n",
            str_pad($user, 50, ' ', STR_PAD_RIGHT),
            md5($pwd)
        ));
        $this->fs += $writeLen;

        // 立即写入新内容到磁盘
        fflush($this->fo);

        // 新用户ID就是最新一行的行号
        $this->lastUserId = intval($this->fs/(50+32+1));

        return true;
    }

    public function mod(int $userId, string $pwd):bool {
        // 移动文件指针到指定userId对应的用户信息位置
        fseek($this->fo, (50+32+1)*($userId-1));

        // 按约定数据长度读取数据栏位上的数据
        $userInDb = trim(fread($this->fo, 50));
        $pwdInDb = fread($this->fo, 32);
        if($userInDb == ""){

            return false;
        }

        // 修改密码
        // 后退32个字符,在密码栏位开始处开始写
        fseek($this->fo, ftell($this->fo)-32);
        fwrite($this->fo, md5($pwd));

        // 立即写入新内容到磁盘
        fflush($this->fo);

        return true;
    }

    public function del(int $userId):bool {
        // 移动文件指针到指定userId对应的用户信息位置
        fseek($this->fo, (50+32+1)*($userId-1));

        // 按约定数据长度读取数据栏位上的数据
        $userInDb = trim(fread($this->fo, 50));
        $pwdInDb = fread($this->fo, 32);
        if($userInDb == ""){

            return false;
        }

        // 修改密码
        // 后退82个字符,在用户名栏位开始处开始写
        // 写入82个空字符表示用户已经被删除了
        fseek($this->fo, ftell($this->fo)-82);
        fwrite($this->fo, str_repeat(" ", 82));

        // 立即写入新内容到磁盘
        fflush($this->fo);

        return true;
    }

    public function login(string $user, string $pwd=""):int {
        $fo = fopen($this->fp, "r");
        if($fo){
            while(!feof($fo)){
                // 解析出用户名和密码
                $userInDb = trim(fread($fo, 50));
                $pwdInDb = fread($fo, 32);
                fread($fo, 1);

                // 验证用户名
                if($user == $userInDb){
                    // 验证密码
                    if(md5($pwd) == $pwdInDb){
                        // 计算登录用户ID
                        $this->lastLoginUserId = intval(ftell($fo)/(50+32+1));

                        return 0;
                    }
                    else{

                        return 1; // 密码错误
                    }
                }
            }
            fclose($fo);
        }

        return 2; // 用户名不存在
    }

    public function close(){
        fclose($this->fo);
    }

    public function getLastUserId():int {

        return $this->lastUserId;
    }

    public function getLastLoginUserId():int {

        return $this->lastLoginUserId;
    }
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141

4.6.3 文本数据库测试

下面是对这个TFSimpleTextDb的测试代码。

$myTFSimpleTextDb = new TFSimpleTextDb();

$myTFSimpleTextDb->conn();

if($myTFSimpleTextDb->login("福哥") == 2){
    echo ("<h3>创建用户福哥</h3>");
    $myTFSimpleTextDb->add("福哥", "123456");
    var_dump($myTFSimpleTextDb->getLastUserId());
}
if($myTFSimpleTextDb->login("鬼谷子叔叔") == 2) {
    echo ("<h3>创建用户鬼谷子叔叔</h3>");
    $myTFSimpleTextDb->add("鬼谷子叔叔", "abcdef");
    var_dump($myTFSimpleTextDb->getLastUserId());
}
if($myTFSimpleTextDb->login("同福大哥") == 2) {
    echo ("<h3>创建用户同福大哥</h3>");
    $myTFSimpleTextDb->add("同福大哥", "123456");
    var_dump($myTFSimpleTextDb->getLastUserId());
}
echo ("<h3>修改鬼谷子叔叔的密码</h3>");
var_dump($myTFSimpleTextDb->mod(2, "123456"));
echo ("<h3>使用新密码登录鬼谷子叔叔</h3>");
var_dump($myTFSimpleTextDb->login("鬼谷子叔叔", "123456"));
var_dump($myTFSimpleTextDb->getLastLoginUserId());
echo ("<h3>删除鬼谷子叔叔</h3>");
var_dump($myTFSimpleTextDb->del(2));
echo ("<h3>再次登录鬼谷子叔叔</h3>");
var_dump($myTFSimpleTextDb->login("鬼谷子叔叔", "123456"));

$myTFSimpleTextDb->close();123456789101112131415161718192021222324252627282930

5. 标准方式的意义

学完前面的知识后会有一个疑问,既然file_get_contents和file_put_contents那么简单又可以达到目的,为什么我们还要学习fopen呢?

这是因为fopen的文件指针功能是file_get_contents/file_put_contents无法实现的,而在一些特殊情况下必须通过文件指针来解决读文件、写文件的需求。

5. 将一个10G文件里的“福哥”换成“鬼谷子叔叔”

这个巨大的文件如果我们要通过file_get_contents和file_put_contents操作,系统就直接瘫痪了。。。

怎么办?

这个时候我们可以通过开启两个文件指针,一个文件指针负责读现有文件,一个文件指针负责写新的临时文件,完成后删除现有文件,再将临时文件重命名为原来的文件,就搞定了~~

$currDir = __DIR__;
$newFile = $currDir. "/newFile.php";
$tmpFile = $currDir. "/newFile.php.tmp";

$foNew = fopen($newFile, "r");
$foTmp = fopen($tmpFile, "w");
if($foNew && $foTmp){
    while(!feof($foNew)){
        $tmpLine = fgets($foNew);
        $tmpLine = str_replace("福哥", "鬼谷子叔叔", $tmpLine);
        fwrite($foTmp, $tmpLine);
    }
    fclose($foNew);
    fclose($foTmp);
    unlink($foNew);
    rename($tmpFile, $newFile);
}

var_dump(file_get_contents($newFile));12345678910111213141516171819

友情提示:福哥的例子是一个小文件所以最后用file_get_contents打印文件内容给大家看了,如果真的是巨大的文件千千万万不要用file_get_contents函数去读取内容哦~~

6. 总结

好了,今天童鞋们跟着福哥系统地将PHP语言操作文件夹、操作文件的方法学习了一遍,有了这些技术之后,今后在项目当中处理各种有关文件夹/文件的问题就不会发怵了!

要注意一点哦,文件夹操作、文件操作属于IO操作,是有一定风险的,一定不要把文件夹/文件的路径搞错了,要不把系统文件或者程序文件写坏了,系统有可能就完蛋了~~


https://m.tongfu.net/home/35/blog/513231.html

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...