从0到1学PHP(十):PHP 文件操作:读写与管理文件

目录

  • 一、文件的打开与关闭
    • 1.1 fopen () 函数的使用
    • 1.2 fclose () 函数的使用
    • 1.3 不同打开模式(r、w、a 等)的区别
  • 二、文件的读写操作
    • 2.1 使用 fread ()、fwrite () 等函数读写文件
    • 2.2 读取整个文件的函数(file_get_contents ()、file_put_contents ())
  • 三、目录操作
    • 3.1 目录的创建(mkdir ())、删除(rmdir ())
    • 3.2 目录的遍历
    • 3.3 获取目录信息的函数(scandir () 等)
  • 四、文件上传处理
    • 4.1 HTML 文件上传表单的创建
    • 4.2 PHP 处理文件上传的步骤(临时文件、移动文件等)
    • 4.3 上传文件的验证(类型、大小等)
  • 五、总结

一、文件的打开与关闭

在 PHP 的文件操作中,文件的打开与关闭是最基础的操作,就像我们日常打开和关闭文件一样,是进行文件读写和管理的前提。在 PHP 里,我们主要使用fopen()函数来打开文件,使用fclose()函数来关闭文件。这两个函数为我们后续对文件的各种操作搭建了基础的桥梁。

1.1 fopen () 函数的使用

fopen()函数用于打开文件或者 URL,它的语法如下:

php 复制代码
resource fopen ( string $filename , string $mode [, bool $use_include_path = false [, resource $context ]] )
  • $filename:要打开的文件路径及文件名,它可以是一个本地文件路径,例如"test.txt",也可以是一个 URL,如"http://example.com/data.txt"。
  • $mode:打开文件的模式,这个参数非常关键,它决定了我们以何种方式来操作文件,后面会详细介绍不同模式的区别。
  • $use_include_path:可选参数,设为true时会在include_path中查找文件,默认为false。
  • $context:可选参数,是一个stream_context资源,用于设置一些额外的流上下文选项,比如设置 HTTP 请求头,在处理 URL 时比较常用。

下面是一个简单的示例,使用fopen()函数以只读模式打开一个本地文件:

php 复制代码
<?php
$file = fopen("test.txt", "r");
if ($file) {
    echo "文件打开成功!";
    // 这里可以进行文件读取等操作
    fclose($file);
} else {
    echo "文件打开失败!";
}
?>

在这个示例中,首先尝试使用fopen()函数打开test.txt文件,模式为"r"(只读模式)。如果文件成功打开,fopen()函数会返回一个文件指针资源,赋值给$file变量,然后输出 "文件打开成功!",接着可以在这个条件块内进行文件读取等操作,最后使用fclose()函数关闭文件;如果文件打开失败,fopen()函数返回false,则输出 "文件打开失败!" 。

1.2 fclose () 函数的使用

fclose()函数的作用是关闭一个已打开的文件指针,也就是释放与该文件相关的系统资源。它的语法很简单:

php 复制代码
bool fclose ( resource $handle )
  • $handle:要关闭的文件指针资源,这个资源就是由fopen()函数返回的。
    关闭文件是一个非常重要的操作,如果不关闭文件,可能会导致数据丢失或者系统资源的浪费。例如,当我们向文件中写入数据时,如果没有关闭文件,数据可能只是暂时存储在缓冲区中,并没有真正写入到文件中,一旦程序异常终止,这些数据就会丢失。另外,打开的文件会占用系统资源,如果长时间不关闭,可能会影响系统的性能。

下面是一个结合fopen()和fclose()函数的完整示例:

php 复制代码
<?php
$file = fopen("test.txt", "w");
if ($file) {
    fwrite($file, "这是写入的内容");
    $result = fclose($file);
    if ($result) {
        echo "文件关闭成功,数据已保存。";
    } else {
        echo "文件关闭失败,数据可能未正确保存。";
    }
} else {
    echo "文件打开失败!";
}
?>

在这个示例中,先以写入模式"w"打开test.txt文件,如果打开成功,使用fwrite()函数向文件中写入内容,然后使用fclose()函数关闭文件。fclose()函数执行后会返回一个布尔值,如果关闭成功返回true,否则返回false,根据返回值输出相应的提示信息。

1.3 不同打开模式(r、w、a 等)的区别

fopen()函数的$mode参数有多种取值,每种取值代表一种不同的打开模式,它们在文件操作中有各自不同的表现:

  • r(只读模式):以只读方式打开文件,文件指针会被放置在文件的开头。如果文件不存在,fopen()函数会返回false。例如:
php 复制代码
$file = fopen("test.txt", "r");
  • w(写入模式):以写入方式打开文件,如果文件存在,会将文件长度清为零,即文件原有的内容会被全部删除;如果文件不存在,则尝试创建该文件。文件指针被放置在文件的开头。例如:
php 复制代码
$file = fopen("test.txt", "w");
  • a(追加模式):以写入方式打开文件,并将文件指针指向文件末尾。如果文件不存在,则尝试创建该文件。写入的数据会被追加到文件末尾,文件原有的内容会被保留。例如:
php 复制代码
$file = fopen("test.txt", "a");
  • r+(读写模式):以可读写方式打开文件,文件必须存在,文件指针被放置在文件的开头。既可以读取文件内容,也可以写入内容,写入时会覆盖原有的数据。例如:
php 复制代码
$file = fopen("test.txt", "r+");
  • w+(读写模式,清空文件):以读写方式打开文件,如果文件存在,会将文件长度清为零;如果文件不存在,则尝试创建该文件。文件指针被放置在文件的开头,同样可以进行读写操作 。例如:
php 复制代码
$file = fopen("test.txt", "w+");
  • a+(读写追加模式):以读写追加方式打开文件,如果文件不存在,则尝试创建该文件。文件指针被放置在文件末尾,读取时可以读取整个文件内容,写入时数据会追加到文件末尾 。例如:
php 复制代码
$file = fopen("test.txt", "a+");

为了更直观地理解这些模式的区别,我们来看一个对比示例:

php 复制代码
<?php
// 准备测试文件,先写入一些初始内容
$initFile = fopen("test.txt", "w");
fwrite($initFile, "初始内容");
fclose($initFile);

// 使用r模式读取文件
$rFile = fopen("test.txt", "r");
echo "r模式读取:". fread($rFile, filesize("test.txt"));
fclose($rFile);

// 使用w模式写入文件
$wFile = fopen("test.txt", "w");
fwrite($wFile, "w模式写入的新内容");
fclose($wFile);
$rFile = fopen("test.txt", "r");
echo "w模式写入后读取:". fread($rFile, filesize("test.txt"));
fclose($rFile);

// 使用a模式追加文件
$aFile = fopen("test.txt", "a");
fwrite($aFile, "a模式追加的内容");
fclose($aFile);
$rFile = fopen("test.txt", "r");
echo "a模式追加后读取:". fread($rFile, filesize("test.txt"));
fclose($rFile);

// 使用r+模式读写文件
$rPlusFile = fopen("test.txt", "r+");
fwrite($rPlusFile, "r+模式写入的内容");
fseek($rPlusFile, 0); // 将文件指针移到开头
echo "r+模式写入后读取:". fread($rPlusFile, filesize("test.txt"));
fclose($rPlusFile);

// 使用w+模式读写文件
$wPlusFile = fopen("test.txt", "w+");
fwrite($wPlusFile, "w+模式写入的内容");
fseek($wPlusFile, 0);
echo "w+模式写入后读取:". fread($wPlusFile, filesize("test.txt"));
fclose($wPlusFile);

// 使用a+模式读写追加文件
$aPlusFile = fopen("test.txt", "a+");
fwrite($aPlusFile, "a+模式追加的内容");
fseek($aPlusFile, 0);
echo "a+模式追加后读取:". fread($aPlusFile, filesize("test.txt"));
fclose($aPlusFile);
?>

通过这个示例,可以清晰地看到不同打开模式下对文件读取、写入和追加操作的影响。在实际编程中,我们需要根据具体的需求选择合适的打开模式,以确保文件操作的正确性和高效性。

二、文件的读写操作

文件的读写操作是文件操作中的核心部分,它涉及到如何将数据写入文件以及从文件中读取数据。在 PHP 中,提供了一系列丰富的函数来满足不同场景下的文件读写需求,这些函数就像是我们与文件之间进行数据交互的桥梁,通过它们,我们可以灵活地对文件中的数据进行处理。接下来,我们将深入探讨这些文件读写函数的使用方法和技巧。

2.1 使用 fread ()、fwrite () 等函数读写文件

fread()函数用于从文件指针中读取数据,它的语法如下:

php 复制代码
string fread ( resource $handle , int $length )
  • $handle:是通过fopen()函数打开的文件指针,它就像是文件的 "入口钥匙",通过这个指针我们可以找到并操作对应的文件。
  • $length:指定要读取的最大字节数,这个参数决定了我们从文件中读取数据的量。

例如,我们要读取一个文本文件的内容,可以这样实现:

php 复制代码
<?php
$file = fopen("test.txt", "r");
if ($file) {
    $content = fread($file, filesize("test.txt"));
    echo $content;
    fclose($file);
} else {
    echo "文件打开失败!";
}
?>

在这个例子中,首先使用fopen()函数以只读模式打开test.txt文件。如果文件成功打开,通过filesize()函数获取文件的大小,然后将其作为参数传递给fread()函数,这样就能读取整个文件的内容,并将读取到的内容赋值给$content变量,最后输出文件内容并关闭文件。如果文件打开失败,则输出错误提示。

fwrite()函数用于向文件指针写入数据,语法如下:

php 复制代码
int fwrite ( resource $handle , string $string [, int $length ] )
  • $handle:同样是文件指针。
  • $string:是要写入文件的字符串内容。
  • length:可选参数,指定要写入的最大字节数。如果省略该参数,将写入整个string字符串 。

下面是一个使用fwrite()函数向文件写入数据的示例:

php 复制代码
<?php
$file = fopen("test.txt", "w");
if ($file) {
    $str = "这是要写入文件的内容";
    $bytesWritten = fwrite($file, $str);
    if ($bytesWritten!== false) {
        echo "成功写入 $bytesWritten 个字节。";
    } else {
        echo "写入失败!";
    }
    fclose($file);
} else {
    echo "文件打开失败!";
}
?>

在这个示例中,以写入模式"w"打开test.txt文件,如果打开成功,定义要写入的字符串$str,然后使用fwrite()函数将字符串写入文件。fwrite()函数返回写入的字节数,如果返回值不是false,说明写入成功,并输出写入的字节数;如果返回false,则表示写入失败,最后关闭文件。如果文件打开失败,也会输出相应的错误提示。

2.2 读取整个文件的函数(file_get_contents ()、file_put_contents ())

file_get_contents()函数可以一次性读取整个文件的内容,并将其作为字符串返回,使用起来非常简洁方便。它的语法如下:

php 复制代码
string file_get_contents ( string $filename [, bool $use_include_path = false [, resource $context [, int $offset = 0 [, int $maxlen ]]]] )
  • $filename:要读取的文件路径及文件名。
  • $use_include_path:可选参数,设为true时会在include_path中查找文件,默认为false。
  • $context:可选参数,是一个stream_context资源,用于设置一些额外的流上下文选项。
  • $offset:可选参数,指定从文件的哪个偏移位置开始读取,默认为0,即从文件开头开始读取。
  • $maxlen:可选参数,指定读取的最大长度。

例如:

php 复制代码
<?php
$content = file_get_contents("test.txt");
echo $content;
?>

这几行代码就可以轻松读取test.txt文件的全部内容,并将其输出。与fread()函数相比,file_get_contents()函数不需要手动打开和关闭文件,也不需要获取文件大小来确定读取长度,代码更加简洁直观,在读取小文件时优势明显。

file_put_contents()函数则用于将一个字符串写入文件中,如果文件不存在,会自动创建该文件。它的语法如下:

php 复制代码
int|false file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] )
  • $filename:要写入数据的文件路径及文件名。
  • data:要写入文件的数据,可以是字符串、数组(如果flags包含FILE_APPEND且$data是数组时,会将数组元素逐行追加到文件)或数据流。
  • $flags:可选参数,用于指定一些写入选项,常见的值有FILE_USE_INCLUDE_PATH(在include_path中查找文件)、FILE_APPEND(追加数据到文件末尾,而不是覆盖原有内容)、LOCK_EX(独占锁定文件,防止其他进程同时写入 )。
  • $context:可选参数,流上下文资源。

示例如下:

php 复制代码
<?php
$str = "这是通过file_put_contents写入的内容";
$bytesWritten = file_put_contents("test.txt", $str);
if ($bytesWritten!== false) {
    echo "成功写入 $bytesWritten 个字节。";
} else {
    echo "写入失败!";
}
?>

这段代码将字符串$str写入test.txt文件中,如果写入成功,返回写入的字节数并输出提示信息;如果写入失败,返回false并输出错误提示。file_put_contents()函数同样不需要手动打开和关闭文件,而且在写入操作上更加便捷,尤其是在追加数据到文件时,使用FILE_APPEND标志可以很容易实现 。与fwrite()函数相比,它在处理简单的文件写入任务时,代码量更少,逻辑更清晰。不过,在一些需要更精细控制文件写入过程(如分块写入、对文件指针进行复杂操作)的场景下,fwrite()函数会更合适。

三、目录操作

在文件管理中,目录就像是一个大型图书馆的书架分类系统,它能帮助我们更有序地组织和管理文件。通过合理地操作目录,我们可以轻松地创建新的分类区域(创建目录)、清理不再需要的分类(删除目录)、查看某个分类下的所有文件(遍历目录)以及获取目录的详细信息(如包含哪些文件和子目录)。在 PHP 中,提供了一系列函数来实现这些目录操作,让我们能够高效地管理文件系统中的目录结构。

3.1 目录的创建(mkdir ())、删除(rmdir ())

mkdir()函数用于创建目录,它的语法如下:

php 复制代码
bool mkdir ( string $dirname [, int $mode = 0777 [, bool $recursive = false [, resource $context ]]] )
  • $dirname:必需参数,指定要创建的目录名称,可以是相对路径或绝对路径。例如"new_dir"表示在当前目录下创建一个名为new_dir的目录,"/var/www/html/new_dir"则表示在指定的绝对路径下创建目录。
  • $mode:可选参数,用于设置目录的权限,默认为0777,表示任何人都有读、写和执行权限。权限设置使用八进制数表示,每一位分别对应文件所有者、所属组和其他用户的权限 。例如0755表示文件所有者有读、写和执行权限,所属组和其他用户只有读和执行权限。
  • $recursive:可选参数,设为true时,会递归创建目录,即如果上级目录不存在,会自动创建上级目录。默认为false。
  • $context:可选参数,是一个stream_context资源,用于设置一些额外的流上下文选项。

下面是一个创建目录的示例:

php 复制代码
<?php
$newDir = "new_dir";
if (!mkdir($newDir, 0755, true)) {
    die("目录创建失败!");
}
echo "目录 $newDir 创建成功!";
?>

在这个示例中,尝试创建一个名为new_dir的目录,权限设置为0755,并且启用了递归创建功能。如果目录创建失败,mkdir()函数返回false,通过die()函数输出错误信息并终止脚本;如果创建成功,输出成功提示信息。

rmdir()函数用于删除空目录,语法如下:

php 复制代码
bool rmdir ( string $dirname )
  • $dirname:必需参数,指定要删除的目录名称,同样可以是相对路径或绝对路径。

需要注意的是,rmdir()函数只能删除空目录,如果目录不为空,删除操作会失败。例如:

php 复制代码
<?php
$dirToDelete = "new_dir";
if (!rmdir($dirToDelete)) {
    die("目录删除失败,可能目录不为空或没有权限!");
}
echo "目录 $dirToDelete 删除成功!";
?>

在这个示例中,尝试删除名为new_dir的目录,如果删除失败,输出错误提示信息;如果删除成功,输出成功提示 。如果new_dir目录中包含文件或子目录,rmdir()函数将返回false,并提示删除失败 。在实际应用中,在调用rmdir()函数之前,通常需要先检查目录是否为空,可以使用scandir()函数获取目录内容来判断,后面会详细介绍scandir()函数。

3.2 目录的遍历

目录遍历是指访问目录中的所有文件和子目录,这在很多场景下都非常有用,比如统计某个目录下文件的数量、复制整个目录结构等。在 PHP 中,可以使用readdir()函数结合opendir()和closedir()函数来实现目录的遍历。

opendir()函数用于打开一个目录句柄,以便进行目录读取操作,语法如下:

php 复制代码
resource opendir ( string $dirname [, resource $context ] )
  • $dirname:必需参数,指定要打开的目录名称。
  • $context:可选参数,流上下文资源。

readdir()函数用于从目录句柄中读取条目,语法如下:

php 复制代码
string readdir ( resource $dir_handle )
  • $dir_handle:必需参数,是由opendir()函数返回的目录句柄。

closedir()函数用于关闭目录句柄,语法如下:

php 复制代码
void closedir ( resource $dir_handle )
  • $dir_handle:必需参数,要关闭的目录句柄 。

下面是一个遍历目录的示例:

php 复制代码
<?php
$dir = "test_dir";
$handle = opendir($dir);
if ($handle) {
    while (false!== ($entry = readdir($handle))) {
        if ($entry!= "." && $entry!= "..") {
            echo $entry. "<br>";
        }
    }
    closedir($handle);
} else {
    die("无法打开目录 $dir");
}
?>

在这个示例中,首先使用opendir()函数打开名为test_dir的目录,如果打开成功,进入while循环,在循环中使用readdir()函数不断读取目录中的条目。readdir()函数每次读取一个条目,并返回该条目名称,如果读取到目录末尾,返回false。通过判断$entry是否不等于"."(表示当前目录)和"..."(表示上级目录),过滤掉这两个特殊的目录项,然后输出其他文件和子目录的名称。最后,使用closedir()函数关闭目录句柄。如果目录打开失败,通过die()函数输出错误信息并终止脚本。

除了上述方法,PHP 还提供了scandir()函数,它可以更方便地获取目录中的文件和子目录列表,后面我们会详细介绍scandir()函数在获取目录信息方面的应用。

3.3 获取目录信息的函数(scandir () 等)

scandir()函数用于返回指定目录中的文件和目录的数组,它是获取目录信息的常用函数,语法如下:

php 复制代码
array scandir ( string $directory [, int $sorting_order = SCANDIR_SORT_ASCENDING [, resource $context ]] )
  • $directory:必需参数,指定要扫描的目录名称。
  • $sorting_order:可选参数,用于指定排序顺序,SCANDIR_SORT_ASCENDING表示按字母升序排列(默认值),SCANDIR_SORT_DESCENDING表示按字母降序排列,SCANDIR_SORT_NONE表示不排序。
  • $context:可选参数,流上下文资源。

下面是一个使用scandir()函数获取目录信息的示例:

php 复制代码
<?php
$dir = "test_dir";
$files = scandir($dir);
if ($files!== false) {
    foreach ($files as $file) {
        if ($file!= "." && $file!= "..") {
            echo $file. "<br>";
        }
    }
} else {
    die("无法读取目录 $dir");
}
?>

在这个示例中,使用scandir()函数获取test_dir目录中的文件和目录列表,并将结果存储在files数组中。如果获取成功,遍历files数组,同样过滤掉"."和"..."这两个特殊目录项,然后输出其他文件和子目录的名称。如果scandir()函数执行失败,返回false,通过die()函数输出错误信息并终止脚本 。与前面使用opendir()和readdir()函数遍历目录相比,scandir()函数更加简洁,一行代码就能获取目录中的所有条目,在只需要获取目录列表而不需要对每个条目进行复杂操作时,使用scandir()函数会更高效 。此外,scandir()函数返回的数组包含了目录中的所有条目,包括文件和子目录,我们可以进一步结合is_file()和is_dir()函数来判断每个条目是文件还是目录,从而实现更细致的目录信息处理 。例如:

php 复制代码
<?php
$dir = "test_dir";
$files = scandir($dir);
if ($files!== false) {
    foreach ($files as $file) {
        if ($file!= "." && $file!= "..") {
            if (is_file("$dir/$file")) {
                echo "文件: ". $file. "<br>";
            } elseif (is_dir("$dir/$file")) {
                echo "目录: ". $file. "<br>";
            }
        }
    }
} else {
    die("无法读取目录 $dir");
}
?>

在这个改进的示例中,通过is_file()和is_dir()函数判断每个条目是文件还是目录,并分别输出相应的提示信息,这样可以更清晰地展示目录中的内容结构。

四、文件上传处理

在现代 Web 开发中,文件上传是一个极为常见的功能。无论是用户在社交平台上传个人头像、在云盘上传文件,还是在办公系统中提交文档,都离不开文件上传技术。它为用户与服务器之间的数据交互提供了一种便捷的方式,极大地丰富了 Web 应用的功能和用户体验。然而,实现一个安全、高效的文件上传功能并非易事,它涉及到前端表单的创建、后端 PHP 代码的处理以及对上传文件的严格验证等多个环节。接下来,我们将详细探讨 PHP 中文件上传处理的相关知识和技术。

4.1 HTML 文件上传表单的创建

在 HTML 中,创建文件上传表单主要通过<input>标签来实现,关键在于设置type属性为"file",同时<form>标签的enctype属性需设置为"multipart/form-data",这是因为文件上传时需要以二进制形式传输数据,而"multipart/form-data"这种编码类型能够满足这一需求 。以下是一个简单的 HTML 文件上传表单示例:

php 复制代码
<!DOCTYPE html>
<html>

<body>
    <form action="upload.php" method="post" enctype="multipart/form-data">
        <label for="file">选择文件:</label>
        <input type="file" name="file" id="file">
        <br>
        <input type="submit" name="submit" value="上传">
    </form>
</body>

</html>

在这个表单中:

  • action属性指定了表单提交后数据发送到的 PHP 脚本,这里是"upload.php",当用户点击 "上传" 按钮时,表单数据将被发送到该脚本进行处理。
  • method属性设置为"post",表示使用 POST 方法提交表单数据。POST 方法相较于 GET 方法更适合文件上传,因为 GET 方法会将数据附加在 URL 后面,存在长度限制且安全性较低,而 POST 方法将数据包含在 HTTP 请求体中,更安全且对数据长度没有严格限制。
  • <input type="file">创建了文件选择框,用户可以通过点击该框旁边的 "浏览" 按钮,从本地文件系统中选择要上传的文件 。name属性设置为"file",这个名称在后端 PHP 代码中用于获取上传文件的相关信息,保持前后端名称一致是正确处理文件上传的关键。
  • enctype="multipart/form-data"确保表单数据以多部分数据类型进行编码,以便正确传输文件内容 。如果不设置这个属性,文件上传将无法正常进行。

此外,<input>标签还可以添加multiple属性,实现一次上传多个文件,例如:

php 复制代码
<input type="file" name="files[]" id="file" multiple>

这里name属性的值设置为"files[]",使用[]表示这是一个数组,在后端 PHP 代码中可以通过遍历数组来处理多个上传文件 。multiple属性允许用户在文件选择对话框中同时选择多个文件。

4.2 PHP 处理文件上传的步骤(临时文件、移动文件等)

当用户在前端提交文件上传表单后,后端 PHP 脚本需要对上传的文件进行处理。PHP 通过$_FILES全局数组来获取上传文件的相关信息,处理文件上传主要包括以下步骤:

接收上传文件信息

在 PHP 脚本中,通过$_FILES数组获取上传文件的详细信息,例如:

php 复制代码
<?php
if (isset($_FILES['file'])) {
    $file = $_FILES['file'];
    echo "文件名: ". $file['name']. "<br>";
    echo "文件类型: ". $file['type']. "<br>";
    echo "文件大小: ". $file['size']. " 字节<br>";
    echo "临时文件名: ". $file['tmp_name']. "<br>";
    echo "错误代码: ". $file['error']. "<br>";
}
?>

在上述代码中:

  • $_FILES['file']['name']获取上传文件的原始名称,例如"example.jpg"。
  • $_FILES['file']['type']获取文件的 MIME 类型,如"image/jpeg",它表示文件的类型信息,用于判断文件的种类。
  • $_FILES['file']['size']获取文件的大小,单位是字节,通过这个值可以判断文件是否超过服务器设定的大小限制。
  • $_FILES['file']['tmp_name']获取文件上传到服务器后存储的临时文件名,例如"/tmp/php789ABC",这个临时文件在脚本执行结束后会被自动删除,所以需要及时将其移动到指定位置进行保存。
  • $_FILES['file']['error']获取与文件上传相关的错误代码,0表示文件上传成功,其他值表示不同类型的错误,例如1表示上传的文件超过了php.ini中upload_max_filesize选项限制的大小。

移动临时文件到指定位置

上传的文件首先会被存储在服务器的临时目录中,为了永久保存文件,需要使用move_uploaded_file()函数将其移动到指定的目标目录 。示例如下:

php 复制代码
<?php
if (isset($_FILES['file'])) {
    $file = $_FILES['file'];
    $targetDir = "uploads/";
    $targetFile = $targetDir. basename($file['name']);

    if (move_uploaded_file($file['tmp_name'], $targetFile)) {
        echo "文件上传成功,已保存到 ". $targetFile;
    } else {
        echo "文件上传失败,请重试。";
    }
}
?>

在这段代码中:

  • $targetDir定义了目标目录,这里是"uploads/",确保该目录存在且有可写权限 。如果目录不存在,可以使用mkdir()函数创建。
  • $targetFile通过将目标目录和上传文件的原始文件名拼接而成,basename()函数用于获取文件名部分。
  • move_uploaded_file(file\['tmp_name'\], targetFile)函数尝试将临时文件移动到目标位置,如果移动成功,返回true,并输出成功提示信息;如果移动失败,返回false,输出错误提示 。移动文件时,需要确保目标目录有写入权限,否则移动操作将失败。

4.3 上传文件的验证(类型、大小等)

为了确保上传文件的安全性和符合业务需求,需要对上传文件进行严格的验证,主要包括文件类型验证和文件大小验证。

文件类型验证

文件类型验证可以通过检查文件的 MIME 类型或者文件扩展名来实现。以下是通过 MIME 类型验证的示例:

php 复制代码
<?php
if (isset($_FILES['file'])) {
    $file = $_FILES['file'];
    $allowedTypes = array("image/gif", "image/jpeg", "image/png");

    if (in_array($file['type'], $allowedTypes)) {
        // 允许上传
        echo "文件类型允许上传。";
    } else {
        echo "不允许上传该类型的文件,请上传GIF、JPEG或PNG图片。";
    }
}
?>

在这个示例中,定义了一个允许上传的 MIME 类型数组allowedTypes,通过in_array()函数检查上传文件的file['type']是否在允许的类型数组中,如果在,则表示文件类型合法,允许上传;否则,提示用户不允许上传该类型的文件。需要注意的是,MIME 类型可以被伪造,所以仅依靠 MIME 类型验证是不够安全的,还可以结合文件扩展名验证 。例如:

php 复制代码
<?php
if (isset($_FILES['file'])) {
    $file = $_FILES['file'];
    $fileName = $file['name'];
    $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
    $allowedExtensions = array("gif", "jpeg", "jpg", "png");

    if (in_array(strtolower($fileExtension), $allowedExtensions)) {
        // 允许上传
        echo "文件扩展名允许上传。";
    } else {
        echo "不允许上传该扩展名的文件,请上传GIF、JPEG或PNG图片。";
    }
}
?>

在这段代码中,使用pathinfo()函数获取上传文件的扩展名,PATHINFO_EXTENSION表示返回文件的扩展名部分。然后将获取到的扩展名转换为小写,通过in_array()函数检查是否在允许的扩展名数组$allowedExtensions中,以此来验证文件类型。结合 MIME 类型和文件扩展名验证,可以提高文件类型验证的安全性和准确性。

文件大小验证

文件大小验证可以通过比较上传文件的大小与设定的最大文件大小来实现。在 PHP 中,上传文件的大小可以通过$_FILES['file']['size']获取,单位是字节 。示例如下:

php 复制代码
<?php
if (isset($_FILES['file'])) {
    $file = $_FILES['file'];
    $maxSize = 2 * 1024 * 1024; // 2MB,设置最大文件大小为2MB

    if ($file['size'] <= $maxSize) {
        // 允许上传
        echo "文件大小符合要求,允许上传。";
    } else {
        echo "文件大小超过限制,请上传小于2MB的文件。";
    }
}
?>

在这个示例中,定义了maxSize变量,表示允许上传的最大文件大小为 2MB(2 \* 1024 \* 1024 字节)。通过比较file['size']和$maxSize,如果上传文件的大小小于或等于最大限制,则允许上传;否则,提示用户文件大小超过限制。需要注意的是,PHP 的php.ini配置文件中也有upload_max_filesize和post_max_size等配置项来限制上传文件的大小和 POST 数据的大小,实际应用中需要确保 PHP 脚本中的限制与php.ini中的配置相匹配,以避免出现不一致的情况。同时,还可以在前端使用 JavaScript 进行文件大小的初步验证,提前提示用户,减轻服务器的压力。例如:

php 复制代码
<!DOCTYPE html>
<html>

<head>
    <script>
        function checkFileSize() {
            var fileInput = document.getElementById('file');
            var file = fileInput.files[0];
            var maxSize = 2 * 1024 * 1024; // 2MB

            if (file && file.size > maxSize) {
                alert('文件大小超过限制,请上传小于2MB的文件。');
                fileInput.value = ''; // 清空文件选择
                return false;
            }
            return true;
        }
    </script>
</head>

<body>
    <form action="upload.php" method="post" enctype="multipart/form-data" onsubmit="return checkFileSize()">
        <label for="file">选择文件:</label>
        <input type="file" name="file" id="file">
        <br>
        <input type="submit" name="submit" value="上传">
    </form>
</body>

</html>

在这个 HTML 页面中,通过 JavaScript 的checkFileSize()函数在表单提交前检查文件大小。获取用户选择的文件file,并与设定的最大文件大小maxSize进行比较 。如果文件大小超过限制,弹出提示框,清空文件选择框,并返回false阻止表单提交;否则,返回true允许表单提交。这样在前端进行初步验证,可以提供更好的用户体验,减少不必要的服务器请求。

五、总结

在 PHP 开发中,文件操作是一项不可或缺的技能,它贯穿于各种项目场景。从简单的文本文件读写,到复杂的文件上传处理以及目录结构管理,这些操作构成了 PHP 与文件系统交互的基础。通过学习fopen()、fwrite()、mkdir()、move_uploaded_file()等一系列函数,我们掌握了打开与关闭文件、读取与写入文件内容、创建与删除目录以及处理文件上传的方法。

在实际应用中,文件操作有着广泛的用途。例如,在日志记录系统中,通过文件写入操作将系统运行的关键信息记录下来,方便后续的问题排查和系统分析;在内容管理系统中,利用文件读取功能展示存储在文件中的文章、图片等内容;文件上传功能则在用户头像上传、文档提交等场景中发挥着重要作用。

对于读者而言,实践是掌握 PHP 文件操作的关键。建议大家多动手编写代码,尝试在不同的场景下运用所学的文件操作知识。可以从简单的文件读写练习开始,逐步过渡到复杂的文件上传和目录管理项目。在实践过程中,不断总结经验,遇到问题时深入思考、查阅资料,这样才能真正提升自己的编程能力,灵活运用 PHP 文件操作技术解决实际问题。

相关推荐
霜羽68927 分钟前
【C++篇】模版进阶
开发语言·c++
下页、再停留11 分钟前
【PHP】接入百度AI开放平台人脸识别API,实现人脸对比
人工智能·百度·php
给老吕螺丝22 分钟前
C 语言作用域与存储期深度解析:空间与时间的双重维度
c语言·开发语言·经验分享·笔记
CHEN5_0230 分钟前
【Java面试题】缓存穿透
java·开发语言·数据库·redis·缓存
UQWRJ1 小时前
R语言基础图像及部分调用函数
开发语言·r语言
苏琢玉1 小时前
如何优雅地处理多种电商优惠规则?我用 PHP 封装了一个 Promotion Engine
后端·php·composer
搜狐技术产品小编20231 小时前
浅析责任链模式在视频审核场景中的应用
java·开发语言·责任链模式
玉树临风江流儿2 小时前
QT收费情况
开发语言·qt
ankleless2 小时前
C语言(02)——标准库函数大全(持续更新)
c语言·开发语言·算法·标准库函数·零基础自学
七七软件开发2 小时前
团购商城 app 系统架构分析
java·python·小程序·eclipse·系统架构·php