Ruby on Rails应用生产环境send_file报错排查求助
Rails Dokku部署下send_file 500错误的解决方案
问题背景
本地环境中,使用以下代码可正常交付文件:
filepath = Rails.root.join("public", @records.document.url) send_file(filepath)
但部署到EC2 Ubuntu的Dokku生产环境时,出现ActionController::MissingFile (Cannot read file *path of the file*)错误。通过主机执行find / -iname "*filename*"发现文件存储在/var/lib/docker/overlay2/*container_name*/merged/app/public/filename和/var/lib/docker/overlay2/*container_name*/diff/app/public/filename,但Rails.root拼接后的路径为app/public/filename。
核心原因排查
- Dokku将Rails应用运行在Docker容器内,主机的
overlay2路径是Docker底层存储路径,容器内进程无法直接访问该路径;Rails.root指向的是容器内的/app目录,因此拼接出的路径是容器内的绝对路径(如/app/public/filename),报错说明容器内要么文件不存在、权限不足,要么Nginx静态托管逻辑与send_file冲突。 - Dokku默认配置Nginx直接托管容器内
public目录的静态文件,若文件在public下,Nginx会直接返回请求,无需经过Rails的send_file,此时强行用send_file可能触发路径或权限问题。
具体解决步骤
1. 确认容器内文件状态
- 进入应用容器:
dokku enter <你的应用名称> - 在容器内验证文件路径与权限:
# 替换成实际的文件路径,可通过Rails控制台打印Rails.root.join("public", @records.document.url)获取 ls -l /app/public/xxx/filename.pdf- 若文件不存在:检查文件生成逻辑是否正确同步到容器内的
public目录,或使用Dokku持久化存储挂载主机目录到容器public子目录:dokku storage:mount <你的应用名称> /主机上的持久化路径:/app/public/documents - 若文件存在但权限不足:调整文件权限为容器进程可读(如
chmod 644 /app/public/xxx/filename.pdf),或修改文件生成逻辑时设置正确权限。
- 若文件不存在:检查文件生成逻辑是否正确同步到容器内的
2. 验证Nginx静态托管能力
- 直接访问文件的静态URL(如
https://你的域名/xxx/filename.pdf):- 若能正常下载:说明Nginx可正确托管该文件,此时无需使用
send_file,直接返回文件的静态URL即可(如url_for(@records.document.url)),效率更高且避免路径问题。 - 若返回404:查看Dokku的Nginx配置,确认静态文件路由规则:
确保配置中包含dokku nginx:show-config <你的应用名称>location / { try_files $uri @proxy; },该规则让Nginx优先查找静态文件,找不到再转发给Rails应用。
- 若能正常下载:说明Nginx可正确托管该文件,此时无需使用
3. 修复send_file的使用逻辑
若必须使用send_file(如需要权限校验、动态生成内容等场景):
- 确保
Rails.root.join生成的是容器内的绝对路径,可在Rails控制台打印确认:puts Rails.root.join("public", @records.document.url).to_s # 应输出类似 /app/public/xxx/filename.pdf 的容器内绝对路径 - 确认容器内的Rails进程有权限读取该文件,可通过修改文件所属用户或调整权限解决。
关于OpenURI.open_uri()的说明
该方法主要用于读取远程URL内容,不适用于本地文件交付场景,因此原开发者的建议不适用当前问题,无需采用。
内容的提问来源于stack exchange,提问作者Rodrigo Moreno
相关产品推荐
相关产品推荐

