各位观众老爷,大家好!今天咱们来聊聊 WordPress 里的一个“幕后英雄”—— wp_upload_dir()
函数。别看它名字平平无奇,它可是掌管着你网站上所有媒体文件的命运,负责规划它们“安家落户”的地点。
咱们今天就来扒一扒它的源码,看看它是如何一步步计算出你的媒体文件该放在哪个文件夹,以及如何生成相应的 URL 的。准备好了吗?发车!
一、wp_upload_dir()
:你的媒体文件“导航员”
简单来说,wp_upload_dir()
函数的作用就是返回一个包含了媒体上传目录信息的数组。这个数组里面有什么呢?大概是这些:
键 (Key) | 值 (Value) |
---|---|
path |
上传目录的完整服务器路径,例如 /var/www/wordpress/wp-content/uploads/2023/10 。注意,这是服务器上的真实路径,不是 URL。 |
url |
上传目录的完整 URL,例如 https://yourwebsite.com/wp-content/uploads/2023/10 。这就是你在浏览器里访问媒体文件的地址。 |
subdir |
上传目录相对于 wp-content/uploads 目录的相对路径,例如 /2023/10 。 如果 wp-content/uploads 是你的上传根目录,那么这就是月份文件夹。 |
basedir |
wp-content/uploads 目录的完整服务器路径。 |
baseurl |
wp-content/uploads 目录的完整 URL。 |
error |
如果发生错误,这里会包含错误信息。如果没有错误,则为空字符串。 |
有了这些信息,WordPress 就能轻松地把你的图片、视频和其他文件存放到正确的位置,并且在你的网站上正确显示它们。
二、源码剖析:一步一步揭秘
好了,废话不多说,直接上源码!我们来看一下 wp-includes/functions.php
文件中 wp_upload_dir()
函数的关键部分(为了方便讲解,我对源码进行了一些简化和注释):
function wp_upload_dir( $time = null, $create_dir = true, $refresh_cache = false ) {
static $cache = array();
if ( null === $time ) {
$time = current_time( 'mysql' );
}
$key = md5( $time . '|' . $create_dir . '|' . $refresh_cache );
if ( isset( $cache[ $key ] ) && ! $refresh_cache ) {
return $cache[ $key ];
}
$upload_path = trim( get_option( 'upload_path' ) );
// 如果用户自定义了 upload_path,则使用自定义路径,否则默认为 'wp-content/uploads'
if ( empty( $upload_path ) ) {
$dir = WP_CONTENT_DIR . '/uploads';
} elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) {
// 如果 upload_path 不是绝对路径,则相对于 ABSPATH
$dir = ABSPATH . $upload_path;
} else {
$dir = $upload_path;
}
$url = get_option( 'upload_url_path' );
if ( empty( $url ) ) {
$url = content_url( 'uploads' );
}
// 获取年月文件夹的格式,默认为 'Y/m'
$uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' );
if ( false !== $uploads_use_yearmonth_folders &&
( ! ( is_multisite() && ! get_site_option( 'ms_upload_use_yearmonth_folders' ) && is_main_site() ) )
) {
// 获取当前的年份和月份
$subdir = '/' . date( 'Y/m', strtotime( $time ) );
} else {
$subdir = '';
}
$dir .= $subdir;
$url .= $subdir;
// 处理多站点的情况(这里简化了部分代码)
if ( is_multisite() ) {
// ... (多站点相关逻辑) ...
}
$basedir = $dir;
$baseurl = $url;
// 创建目录(如果需要并且目录不存在)
if ( $create_dir ) {
$error = false;
if ( ! wp_mkdir_p( $dir ) ) {
if ( is_dir( $dir ) ) {
if ( ! is_writable( $dir ) ) {
$error = sprintf(
/* translators: %s: Directory path. */
__( 'Upload directory %s is not writable.' ),
$dir
);
}
} else {
$error = sprintf(
/* translators: %s: Directory path. */
__( 'Unable to create directory %s. Is its parent directory writable by the server?' ),
$dir
);
}
}
if ( $error ) {
return array(
'path' => false,
'url' => false,
'subdir' => false,
'basedir' => false,
'baseurl' => false,
'error' => $error,
);
}
}
// 返回结果数组
$ret = array(
'path' => $dir,
'url' => $url,
'subdir' => $subdir,
'basedir' => $basedir,
'baseurl' => $baseurl,
'error' => false,
);
$cache[ $key ] = $ret;
return apply_filters( 'upload_dir', $ret );
}
现在,让我们逐行拆解一下:
-
缓存机制:
static $cache = array(); if ( null === $time ) { $time = current_time( 'mysql' ); } $key = md5( $time . '|' . $create_dir . '|' . $refresh_cache ); if ( isset( $cache[ $key ] ) && ! $refresh_cache ) { return $cache[ $key ]; }
这段代码使用了静态变量
$cache
来缓存结果。wp_upload_dir()
函数会根据传入的参数生成一个唯一的 key,然后检查缓存中是否已经存在对应的结果。如果存在并且$refresh_cache
为false
,则直接返回缓存结果,避免重复计算,提高效率。为什么要缓存呢?因为计算上传目录可能涉及到读取数据库配置、判断文件系统状态等操作,比较耗时。缓存可以显著提升性能,尤其是当多次调用
wp_upload_dir()
函数时。 -
获取上传路径和 URL:
$upload_path = trim( get_option( 'upload_path' ) ); // 如果用户自定义了 upload_path,则使用自定义路径,否则默认为 'wp-content/uploads' if ( empty( $upload_path ) ) { $dir = WP_CONTENT_DIR . '/uploads'; } elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) { // 如果 upload_path 不是绝对路径,则相对于 ABSPATH $dir = ABSPATH . $upload_path; } else { $dir = $upload_path; } $url = get_option( 'upload_url_path' ); if ( empty( $url ) ) { $url = content_url( 'uploads' ); }
这里首先从数据库中读取
upload_path
和upload_url_path
选项。这两个选项允许用户自定义上传目录的路径和 URL。- 如果
upload_path
为空,则使用默认路径WP_CONTENT_DIR . '/uploads'
,也就是wp-content/uploads
目录。 - 如果
upload_path
不是绝对路径,则将其视为相对于 WordPress 根目录的路径,并使用ABSPATH
进行拼接。 upload_url_path
的处理方式类似,如果为空,则使用content_url( 'uploads' )
函数生成默认的 URL。
WP_CONTENT_DIR
是wp-config.php
中定义的WP_CONTENT_DIR
常量,表示wp-content
目录的绝对路径。ABSPATH
也是在wp-config.php
中定义的,表示 WordPress 根目录的绝对路径。content_url()
函数会根据你的 WordPress 设置和网站地址生成正确的wp-content
目录的 URL。 - 如果
-
处理年月文件夹:
$uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' ); if ( false !== $uploads_use_yearmonth_folders && ( ! ( is_multisite() && ! get_site_option( 'ms_upload_use_yearmonth_folders' ) && is_main_site() ) ) ) { // 获取当前的年份和月份 $subdir = '/' . date( 'Y/m', strtotime( $time ) ); } else { $subdir = ''; } $dir .= $subdir; $url .= $subdir;
这段代码检查是否启用了按年月组织上传文件的选项。如果启用了(
uploads_use_yearmonth_folders
选项为true
),则根据当前时间生成形如/2023/10
的子目录,并将其拼接到$dir
和$url
后面。date( 'Y/m', strtotime( $time ) )
函数用于格式化时间戳。strtotime( $time )
将$time
转换为时间戳,date( 'Y/m', ...)
则将其格式化为YYYY/MM
的形式。 -
处理多站点:
if ( is_multisite() ) { // ... (多站点相关逻辑) ... }
这部分代码处理多站点的情况。在多站点环境中,每个站点可以有自己的上传目录。这部分代码会根据当前站点的 ID 和设置来调整上传路径和 URL。(为了简化,这里省略了具体的代码)。
-
创建目录:
if ( $create_dir ) { $error = false; if ( ! wp_mkdir_p( $dir ) ) { if ( is_dir( $dir ) ) { if ( ! is_writable( $dir ) ) { $error = sprintf( /* translators: %s: Directory path. */ __( 'Upload directory %s is not writable.' ), $dir ); } } else { $error = sprintf( /* translators: %s: Directory path. */ __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), $dir ); } } if ( $error ) { return array( 'path' => false, 'url' => false, 'subdir' => false, 'basedir' => false, 'baseurl' => false, 'error' => $error, ); } }
如果
$create_dir
为true
(默认情况),则尝试创建上传目录。这里使用了wp_mkdir_p()
函数,它可以递归地创建目录,也就是说,如果父目录不存在,它也会自动创建。如果创建目录失败,则会检查目录是否已经存在,以及是否可写。如果存在问题,则返回一个包含错误信息的数组。
-
返回结果:
$ret = array( 'path' => $dir, 'url' => $url, 'subdir' => $subdir, 'basedir' => $basedir, 'baseurl' => $baseurl, 'error' => false, ); $cache[ $key ] = $ret; return apply_filters( 'upload_dir', $ret );
最后,将计算出的路径、URL 和其他信息封装到一个数组中,并将其存储到缓存中。然后,使用
apply_filters( 'upload_dir', $ret )
应用upload_dir
过滤器。这个过滤器允许其他插件或主题修改上传目录的信息。
三、upload_dir
过滤器:给你的媒体文件“改名换姓”
upload_dir
过滤器是一个非常强大的工具,它允许你自定义上传目录的行为。你可以使用它来:
- 修改上传路径和 URL。
- 更改年月文件夹的格式。
- 将文件上传到不同的存储位置,例如云存储服务。
- 等等。
下面是一个使用 upload_dir
过滤器的例子,它可以将上传目录更改为 wp-content/my-uploads
:
add_filter( 'upload_dir', 'my_custom_upload_dir' );
function my_custom_upload_dir( $dirs ) {
$dirs['basedir'] = WP_CONTENT_DIR . '/my-uploads';
$dirs['baseurl'] = content_url( 'my-uploads' );
$dirs['path'] = $dirs['basedir'] . $dirs['subdir'];
$dirs['url'] = $dirs['baseurl'] . $dirs['subdir'];
return $dirs;
}
在这个例子中,我们定义了一个名为 my_custom_upload_dir()
的函数,它接收一个 $dirs
数组作为参数。这个数组就是 wp_upload_dir()
函数返回的数组。
我们在 my_custom_upload_dir()
函数中修改了 $dirs
数组的 basedir
、baseurl
、path
和 url
键,将上传目录更改为 wp-content/my-uploads
。
最后,我们使用 add_filter( 'upload_dir', 'my_custom_upload_dir' )
将 my_custom_upload_dir()
函数注册为 upload_dir
过滤器的回调函数。
四、实际应用:一些场景示例
- 自定义上传目录: 你可以使用
upload_dir
过滤器将上传目录更改为云存储服务,例如 Amazon S3 或 Google Cloud Storage。这样可以减轻服务器的负担,并提高网站的性能。 - 按用户组织上传文件: 在多用户博客或社区网站中,你可以使用
upload_dir
过滤器按用户 ID 或用户名组织上传文件。 - 创建独特的年月文件夹格式: 如果默认的
YYYY/MM
格式不符合你的需求,你可以使用upload_dir
过滤器自定义年月文件夹的格式。例如,你可以使用YYYY-MM
或MM-YYYY
格式。 - 限制上传文件类型: 虽然这不是
wp_upload_dir()
直接控制的,但你可以结合其他钩子(如upload_mimes
)和upload_dir
过滤器,根据上传目录的不同,允许上传不同的文件类型。
五、总结:wp_upload_dir()
的重要性
wp_upload_dir()
函数是 WordPress 媒体上传的核心。它负责计算上传目录的路径和 URL,并提供了一个灵活的 upload_dir
过滤器,允许你自定义上传目录的行为。
理解 wp_upload_dir()
函数的工作原理,可以帮助你更好地管理你的媒体文件,并构建更强大的 WordPress 插件和主题。
好了,今天的讲座就到这里。希望大家有所收获!下次再见!