Drupal:Cron调用main()时邮件附件未发送,手动访问URL调用正常
我之前也碰到过一模一样的情况,核心原因是cron运行的环境和Web请求触发的环境完全不一样,下面给你拆解可能的问题点和对应的解决办法:
1. 文件路径问题(最常见)
当你通过Web访问/admin/config/send时,PHP的当前工作目录是Drupal的根目录,所以如果你的附件路径是相对路径(比如sites/default/files/attachments/report.pdf),Web环境下能正常找到。但cron运行时,它的当前工作目录可能是服务器的根目录(/)或者你的用户主目录,这时候相对路径就失效了,找不到附件自然发不出去。
解决办法:
把所有附件路径改成绝对路径,用Drupal内置的函数来生成可靠的绝对路径:
// 代替相对路径,用这个方式获取文件绝对路径 $attachment_path = drupal_realpath('public://attachments/report.pdf'); // 然后在邮件函数里使用这个绝对路径 $params['attachments'] = array( array( 'filepath' => $attachment_path, 'filename' => 'report.pdf', 'filemime' => 'application/pdf', ), );
2. Cron运行的权限问题
Web请求是用Web服务器的用户(比如www-data、apache)运行的,而cron通常是用系统用户(比如你的SSH用户或者root)运行的。如果附件文件的权限只允许Web用户读取,cron的运行用户没权限访问,就无法加载附件。
解决办法:
- 检查附件文件的权限:确保文件的读权限对cron运行用户开放,比如设置权限为
644,目录权限为755。 - 或者让cron用Web服务器用户运行,比如在crontab里指定:
# 用www-data用户运行Drupal cron 0 * * * * sudo -u www-data /usr/bin/php /path/to/drupal/cron.php
3. main()函数依赖Web请求上下文
如果你的main()函数里用到了Web请求相关的变量(比如$_SERVER里的内容,或者依赖当前的用户会话、请求参数),而cron运行时没有这些上下文,可能导致附件生成逻辑失败(比如动态生成的附件没创建成功)。
解决办法:
- 检查
main()里的附件生成/获取逻辑,把依赖Web上下文的部分抽出来,改成独立的逻辑,不依赖请求参数。 - 如果需要动态生成附件,确保在cron环境下能正确生成文件到指定的绝对路径,并且权限正确。
4. 邮件发送函数的差异
有些Drupal邮件模块(或者自定义的邮件发送逻辑)在Web环境下会自动处理附件,但cron环境下可能需要显式传递附件参数,或者某些配置没生效。
解决办法:
- 确认
main()里的邮件发送代码,在cron环境下是否正确传递了attachments参数给drupal_mail()或者你用的邮件函数。比如:
function main() { // 确保attachment参数正确传递,且路径是绝对路径 $attachments = array( array( 'filepath' => drupal_realpath('public://attachments/report.pdf'), 'filename' => 'Monthly Report.pdf', 'filemime' => 'application/pdf', ), ); $message = drupal_mail('mymodule', 'notification', $to, language_default(), array('attachments' => $attachments), $from, TRUE); }
快速调试技巧
可以在hook_cron里加日志,输出当前工作目录和附件路径的存在情况,方便定位问题:
function hook_cron() { // 输出当前工作目录到Drupal日志 watchdog('cron_mail', 'Current working directory: @cwd', array('@cwd' => getcwd())); $attachment_path = drupal_realpath('public://attachments/report.pdf'); // 检查文件是否存在且可读 if (file_exists($attachment_path) && is_readable($attachment_path)) { watchdog('cron_mail', 'Attachment found and readable: @path', array('@path' => $attachment_path)); } else { watchdog('cron_mail', 'Attachment missing or unreadable: @path', array('@path' => $attachment_path), WATCHDOG_ERROR); } main(); }
然后去Drupal的日志页面(/admin/reports/dblog)查看日志,就能快速知道是路径问题还是权限问题了。
内容的提问来源于stack exchange,提问作者Marwen Amri




