125. 文件系统基础

Drupal中,文件操作在底层主要以php流的方式实现,本系列早期阶段对php流进行了详解,见:

phpStreams、公共文件、私有文件》

系统提供了一个流包装器管理器:

服务idstream_wrapper_manager

类:Drupal\Core\StreamWrapper\StreamWrapperManager

用于统一管理各种php流包装器,比较简单不多讲,本篇主要讲解文件CURD基本操作

 

文件系统服务:

容器IDfile_system

类:Drupal\Core\File\FileSystem

该服务是系统底层的文件操作类,和php原生文件操作函数相比,支持流操作,可以说是更高一层的封装,因此在drupal中进行文件操作应首先使用此类,由于支持流包装器,因此也将路径统称为URI;该类属于底层操作,因此不涉及钩子执行等

示例:

        $file_system = \Drupal::service('file_system');
        $fileDir = "public://yunke/2019/"; //保存文件到一个公有目录
        $dirOK = $file_system->prepareDirectory($fileDir, 1);//确保目录存在且可写
        if (!$dirOK) {
            echo "不能新建目录,或无写权限";
            die;
        }
        $filePath = rtrim($fileDir, '/\\') . "/test.txt"; //文件路径,
        $filePath = $file_system->saveData("这是一个测试", $filePath);//保存数据到文件
        if ($filePath) {
            echo "已经成功保存数据到文件:" . $filePath;
        }
        die;

在控制器中运行以上代码,将在公有文件目录中写入文件,通常是:

sites/default/files/yunke/2019/test.txt

公有文件目录地址取决于站点配置,在配置文件中以“file_public_path”项指定;可以反复执行保存,每次保存都新建一个文件,文件名以数字后缀递增;接下来我们介绍文件系统服务的方法。

 

方法介绍:

public function moveUploadedFile($filename, $uri)

将上传的临时文件移动到新的位置,相比php原生的move_uploaded_file函数而言,该方法支持流包装器,第一个参数是临时文件的全路径名字,第二个是目标路径,可以是流路径,返回布尔值以标识是否成功

 

public function chmod($uri, $mode = NULL)

设置文件或目录的权限,$mode是可选的,如果没有传递那么将从配置文件的以下项获取:

file_chmod_directory”(目录权限,默认为“0775”)

file_chmod_file”(文件权限,默认为“0664”)

返回布尔值,失败时将记录日志,是否支持流包装器要视流包装器本身是如何实现的

 

public function unlink($uri, $context = NULL)

删除一个文件,支持流包装器,返回布尔值,可删除window系统中的只读文件

 

public function realpath($uri)

得到一个uri规范化的绝对路径名(处理符号链接和相对路径符号),类似php原生函数realpath,但支持流包装器,不支持远程文件,返回的路径中没有符号连接,'/./' '/../' 成分,结尾没有“/”,在window系统中,路径分隔符“\”不会被替换为“/”,用于不存在的文件会出错,但目录不会

 

public function dirname($uri)

返回路径的目录名,类似phpdirname函数,但支持流包装器,结尾没有斜杠

 

public function basename($uri, $suffix = NULL)

得到文件的文件名,可用于不存在的文件,参数$suffix通常应为带点号的扩展名,如果传递了那么文件名将先去除这个后缀再返回,支持流包装器和非US-ASCII字符

 

public function mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL)

创建目录,类似php原生mkdir函数,但支持流包装器,参数含义如下:

$uri:目录路径

$mode:权限标识,如没有传递将从配置文件中获取

$recursive:是否递归创建,也就是父目录尚不存在时,是否自动创建,默认为FALSE

$context:创建上下文,默认为NULL,详见php原生mkdir函数

如果是流包装器路径,那么委派给流包装器创建;返回布尔值,当不能创建或不能设置权限时,都将返回false

 

public function rmdir($uri, $context = NULL)

删除一个目录,非空目录将不能成功删除,支持流包装器,将正确处理window系统只读文件

 

public function tempnam($directory, $prefix)

类似php原生tempnam函数,但支持流包装器,在指定目录中确定一个唯一的文件名并创建文件,然后返回其全路径文件名,可指定文件名前缀(window系统中将仅使用传递前缀的前三个字符),如果传递的目录不存在,将在系统临时目录中生成一个文件并返回,失败时返回false

 

public function uriScheme($uri)

返回uri的协议,没有的话返回false,如“scheme://target”返回“scheme”,有个特殊情况:“data: target”将返回“data”,特殊在协议“data”没有使用“://”做分隔符

 

public function validScheme($scheme)

检查传入的协议是否为有效自定义协议,换句话说是否存在对应的自定义包装器,这意味着“http”虽然是有效协议,但依然返回false,因为它不是自定义的

 

public function copy($source, $destination, $replace = self::EXISTS_RENAME)

复制文件,和php原生copy函数类似,但更为高级,支持流包装器,参数如下:

$source:源文件全路径名,不能是目录,和原生copy函数一样不支持复制整个目录,空目录也不行

$destination:复制目标,可为目录名或文件名,须提供全路径,为目录时,将采用原文件名。

$replace:当目标已有同名文件时的处理行为(详见getDestinationFilename方法):替换、重命名、提示错误。

复制成功时将返回目标文件的全路径名,不成功将记录日志并抛出异常

 

public function delete($path)

用来删除一个文件,成功返回true,不成功将记录日志并抛出异常,不支持删除目录(用deleteRecursive代替),删除不存在的文件将记录日志并返回true

 

public function deleteRecursive($path, callable $callback = NULL)

递归的删除一个目录或文件,可选的传递一个回调函数,将在每一个文件路径和目录路径上调用,回调函数仅一个参数,用于接收文件或目录的全路径,回调通常用于联动改变数据库记录等,运行时机是文件删除前或目录遍历前,该方法忽略回调的返回值,因此回调无法阻止删除操作,如果以修改权限去阻止,那么在后续删除操作上将抛出异常

 

public function move($source, $destination, $replace = self::EXISTS_RENAME)

主要流程和复制操作完全一样,只不过该方法是移动操作(剪切),在内部如果原生移动操作调用失败,将采用复制,然后删除原文件,这样将支持跨协议操作;支持移动文件和目录(非空目录也可以),成功时返回目标地址

 

protected function prepareDestination($source, &$destination, $replace)

非公有受保护方法,为移动或复制操作准备目标路径,会检查源路径的存在性、是否和目标路径相同、各种权限问题、替换禁用字符、是否utf8字符集,保存htaccess文件等,依据替换设置,目标路径可能会发生改变,因此以引用接收,该方法如没有抛出异常,即表明目标路径可用(可能是修改过的)

 

public function saveData($data, $destination, $replace = self::EXISTS_RENAME)

保存数据到一个文件,在内部先将数据写入一个临时文件,然后移动到目标地址,第一个参数是文件内容,成功时返回目标地址,失败时抛出异常

 

public function prepareDirectory(&$directory, $options = self::MODIFY_PERMISSIONS)

准备一个目录,支持流包装器,返回布尔值指示是否已经准备好,参数$options是一个掩码,通常可选值如下:

\Drupal\Core\File\FileSystemInterface::MODIFY_PERMISSIONS

(该常数值为2,权限不可写时,可修改为可写,但不存在时不会新建)

\Drupal\Core\File\FileSystemInterface::CREATE_DIRECTORY

(该常数值为1,代表不存在即递归创建,采用配置文件中设置的权限)

在内部:如果和1进行“&”操作结果为假,那么在目录不存在时不会创建,并返回false,否则将递归创建,如果和2进行“&”操作结果为假,那么在目录不可写时,不会进行权限更正操作,否则将改变权限为配置文件指定的默认权限;只在返回true时,调用者才应该继续操作。

 

public function getDestinationFilename($destination, $replace)

依据目标文件的存在性和替换行为配置,返回处理过的目标文件全路径,替换行为有以下三种:

如果存在就替换,常量如下:

\Drupal\Core\File\FileSystemInterface::EXISTS_REPLACE(值为1

如果存在就重命名,直到新的名字不存在为止,重命名格式在文件名后扩展名前加“_整数”,常量如下:

\Drupal\Core\File\FileSystemInterface::EXISTS_RENAME(值为0

如果存在就返回false,调用者可据此抛出错误或做其他处理,常量如下:

\Drupal\Core\File\FileSystemInterface::EXISTS_ERROR(值为2

如果不存在就原样返回,该方法会检查文件名是否为有效的utf8字符,如果不是将抛出错误

 

public function createFilename($basename, $directory)

该方法将确定一个文件全路径,确保是不存在的(不会自动新建),参数$basename是可带扩展名的文件名,不带路径,参数$directory是目录路径(尾斜线可选),如果传入的文件名存在将以增加序号的方式递增,从0开始的整数,假设“yunke.php”存在,将改为“yunke_0.php”,如果还存在将改为“yunke_1.php”,依次递增直到不存在为止,然后返回带路径的完整新文件名;如果是windows操作系统,那么文件名中的:':', '*', '?', '"', '<', '>', '|'将被替换为下划线后再确定文件是否存在

注意:在增加序号的时候以最后一个点号做扩展名分隔,如果扩展名本身带点号可能不是想要的结果,如drupal模块的信息文件:“yunke.info.yml”将可能是“yunke.info_0.yml

 

文件MimeType猜测器

用于给定一个文件名(可带路径),返回其文件MIME类型标识

服务IDfile.mime_type.guesser

类:\Drupal\Core\File\MimeType\MimeTypeGuesser

使用入口:

$mimeType = \Drupal::service('file.mime_type.guesser')->guess($path);

严格说来该服务应该称为“文件MimeType猜测器”的管理器或代理,因为其本身并不执行猜测工作,而是自动收集系统中已定义的猜测器服务,并按优先级执行她们,如果高优先级的猜测器返回NULL将尝试下一个低优先级的猜测器,如果返回其他值(包括false),将终止猜测过程并以此为结果返回,系统默认提供了一个以扩展名进行猜测的猜测器:

扩展名文件MimeType猜测器:

服务IDfile.mime_type.guesser.extension

类:\Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser

该猜测器以扩展名为依据在预定义的列表中查找MIME类型标识,如果查找不到将返回以下默认值:

application/octet-stream

这意味着一定能为文件返回一个MIME类型标识,模块可以执行以下修改钩子添加修改预定义列表:

file_mimetype_mapping

修改钩子接收一个数组,格式如:

\Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser::$defaultMapping

有两个键名:

mimetypes”:保存编号到MIME类型标识的映射

extensions”:保存扩展名到编号的映射

编号随意,但需要一致,扩展名可以是带点号的多级扩展名,必须小写

自定义猜测器

如需自定义猜测器,可以定义一个实现了以下接口的类:

\Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface

将其定义成服务,并给出服务标签:“mime_type_guesser”和优先级priority,重新编译容器将自动被收集

 

 

补充:

1drupal程序文件和页面编码均使用utf-8,在中文操作系统中,保存中文文件名会出现乱码问题(这不是drupal特有的问题),要解决需要先将文件名转化为GBK编码,如下所示:

$fileName=iconv('UTF-8', 'GBK//IGNORE', $fileName);

但通常建议文件名使用或转化为ISO-8859-1字符集,如音译转化、哈希值、日期加序号等

 

 

添加新评论

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。
请输入以上问题的答案,换一个问题请刷新,不区分大小写