新手问题 [已解决] 图片上传遭拒,是 capistrano3 + carrierwave 问题,还是权限问题?

chairy11 · 2015年01月23日 · 最后由 chairy11 回复于 2015年01月26日 · 4644 次阅读

更新问题

项目部署到服务器(DigitalOcean), 其它正常,但上传图片会 500。 而我本机操作时,上传图片是正常的。 本来猜想是 capistrano 的问题,想到在不同版本应共享 public/uploads 文件夹下的所有上传文件。所以,我在config/deploy.rb中设置了

set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads}

所以目前,public/uploads 在服务器上是应该是 shared 目录下的。

# 看到当前deploy版本的确链接过去了
root@a_project:/home/a_project/current/public# ls -l
total 3340
-rw-rw-r-- 1 root root    1507 Jan 26 03:11 404.html
-rw-rw-r-- 1 root root    1508 Jan 26 03:11 422.html
-rw-rw-r-- 1 root root    1502 Jan 26 03:11 500.html
drwxrwxr-x 5 root root    4096 Jan 26 03:14 assets
-rw-rw-r-- 1 root root    2550 Jan 26 03:11 favicon.ico
drwxrwxr-x 2 root root    4096 Jan 26 03:11 images
-rw-rw-r-- 1 root root     202 Jan 26 03:11 robots.txt
lrwxrwxrwx 1 root root      33 Jan 26 03:13 system -> /home/a_project/shared/public/system
lrwxrwxrwx 1 root root      34 Jan 26 03:14 uploads -> /home/a_project/shared/public/uploads

# 看到这个目录的权限应该还是root
root@a_project:/home/a_project/shared/public# ls -l
total 8
drwxr-xr-x 2 root root 4096 Jan 21 22:48 system
drwxr-xr-x 2 root root 4096 Jan 26 03:12 uploads

可是,当我上传图片,还是会出错。查看 nginx 出错日志,还是说::Errno::EACCES (Permission denied @ dir_s_mkdir - /home/a_project/releases/20150126081307/public/uploads/tmp):

App 23466 stderr: Started PATCH "/admin/projects/1" for 54.64.229.171 at 2015-01-26 03:36:42 -0500
App 23466 stderr: Processing by Admin::ProjectsController#update as HTML
App 23466 stderr:   Parameters: {"utf8"=>"✓", "authenticity_token"=>"xxx", "project"=>{"img_on_homepage"=>#<ActionDispatch::Http::UploadedFile:0x007f7fa0d1d630 @tempfile=#<Tempfile:/tmp/RackMultipart20150126-23483-1nxkjq0.jpg>, @original_filename="1-中国旅游信息化“十二五”发展规划项目.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"project[img_on_homepage]\"; filename=\"1-\xE4\xE5\x88\x92\xE9\xA1\xB9\xE7\x9B\xAE.jpg\"\r\nContent-Type: image/jpeg\r\n">, "img_on_homepage_cache"=>""}, "commit"=>"更新", "id"=>"1"}
App 23466 stderr:   ^[[1m^[[35mUser Load (0.7ms)^[[0m  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1  ORDER BY `users`.`id` ASC LIMIT 1
App 23466 stderr:   ^[[1m^[[36mProject Load (0.4ms)^[[0m  ^[[1mSELECT  `projects`.* FROM `projects` WHERE `projects`.`id` = 1 LIMIT 1^[[0m
App 23466 stderr:   ^[[1m^[[35m (0.2ms)^[[0m  BEGIN
App 23466 stderr:   ^[[1m^[[36m (0.2ms)^[[0m  ^[[1mROLLBACK^[[0m
App 23466 stderr: Completed 500 Internal Server Error in 11ms
App 23466 stderr:
App 23466 stderr: Errno::EACCES (Permission denied @ dir_s_mkdir - /home/a_project/releases/20150126081307/public/uploads/tmp):
App 23466 stderr:   app/controllers/admin/projects_controller.rb:29:in `update'

难道意思是,我要把/tmp 目录也放在共享下?可是不对吧?我 public/uploads 都 shared 了,那 public/uploads/tmp 应该就没问题了啊?

如果是权限问题,又在哪部分控制的权限?

原问题

在家,小区所谓时代宽带,完全翻不了墙,用不了 google,百度出来的答案全是广告…… 又来求助万能的 ruby-china 啦……

用了 carriewave,在我本机 localhost 上传照片完全没有问题,可是部署完,在正式启用的网上后台操作,却会直接跳转到 500 页面。 我的 Debug 思路是:

  • 看网页端编辑该表单其它参数是否正常。 结论:update 其它参数正常。仅上传图片处会出错跳转到 500.
  • log/production.rb生产日志,一无所获。
  • 看 passenger 日志
/tmp# ls                    
 passenger-error-71NFJU.html  RackMultipart20150123-26977-1hjx223     RackMultipart20150123-26977-l5zd44.jpg
passenger.1.0.12087          passenger-error-9xT843.html  RackMultipart20150123-26977-1mgii56     RackMultipart20150123-26977-nxrc87
passenger-error-2VjmRw.html  passenger-error-CzlBbt.html  RackMultipart20150123-26977-37vw3t      RackMultipart20150123-26977-yhek8w
passenger-error-3Y9ACE.html  passenger-error-FCXM6h.html  RackMultipart20150123-26977-45020n
passenger-error-68ouMr.html  passenger-error-yCMDWp.html  RackMultipart20150123-26977-89vtvd.jpg

额,这次出错页面没有提示,我根本不知道是哪一个文件啊!为什么没有时间标记?难道要全部复制到本机才能看?因为 html 文件很长很长,开头是页内 css,结尾是很多参数,只用复制过来打开才能较好地看到……

  • 看 nginx 出错日志 ``shell $ vi + /usr/local/logs/error.log App 26960 stderr: User Load (0.9ms) SELECTusers.* FROMusersWHEREusers.id= 1 ORDER BYusers.idASC LIMIT 1 App 26960 stderr: Project Load (0.4ms) SELECTprojects.* FROMprojectsWHEREprojects.id= 1 LIMIT 1 App 26960 stderr: (0.3ms) BEGIN App 26960 stderr: (0.4ms) ROLLBACK App 26960 stderr: Completed 500 Internal Server Error in 17ms App 26960 stderr: App 26960 stderr: Errno::EACCES (Permission denied @ dir_s_mkdir - /home/a_project/releases/20150123105103/public/uploads): App 26960 stderr: app/controllers/admin/projects_controller.rb:29:inupdate' App 26960 stderr: App 26960 stderr: App 26960 stderr: Started GET "/favicon.ico" for xxx.xxx.xxx.xx at 2015-01-23 09:27:33 -0500 App 26960 stderr: App 26960 stderr: ActionController::RoutingError (No route matches [GET] "/favicon.ico"):
不知道怎么解读……
疑问:
 为什么会`Permission denied @ dir_s_mkdir - /home/a_project/releases/20150123105103/public/uploads)`? 
我用的是管理员帐号,而且能编辑其它参数,所以不是权限问题。` @ dir_s_mkdir`是什么呢?难道是存储地址有问题?
我是用capistrano一键部署的,那每次部署之后current/public/应该是会变的,这个怎么处理?我是不是应该指明在哪里的public文件夹?
另外,出错了,它为什么要`Started GET "/favicon.ico"`?我平时没用到这个默认的图标,我删了啊!有必要加回来的么?我的500页面是自己写的啊!没用到图标啊!

# 解决方案
- 一方面,需要把uploads目录放在capistrano设置的共享文件夹下

```shell
# config/deploy.rb

set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads}
  • 另一方面,权限问题,需要修改一下权限(17 楼@flowerwrong 提供) shell chmod 777 -R /home/a_project/shared/public/uploads

没权限吧

Errno::EACCES (Permission denied @ dir_s_mkdir - /home/a_project/releases/20150123105103/public/uploads):

我也曾遇到这个问题,不一定是权限的问题。有几个小建议,1、先去 current 目录先删除你所上传文件 2、carr 的 AvatarUploader(你的 uploader 类),逐一注释掉无用的方法。

我是用capistrano一键部署的,那每次部署之后current/public/应该是会变的,这个怎么处理?我是不是应该指明在哪里的public文件夹?

http://flowerwrong.iteye.com/blog/2150191

我感觉是权限问题,看 passenger 是什么用户运行的,是否有权限

之前没用过 passenger,刚查了下,passenger 是集成在 nginx 中的,那就要看 nginx 进程的用户对/home/a_project/releases/20150123105103/public/uploads 目录有没有写权限了

你 ssh 登录上去的账户不一定就是服务器启动时所用的账户啊,比如我的登录账户是 deploy,但是用的是 appuser 账户去启动了 nginx。另外 /favicon.ico 这个图标还真的是需要的,因为每个网站都会有一个 favicon,用于显示在浏览器的 tab 标签页的标题处,比如 ruby-china 的这个: 不信?你访问 https://ruby-china.org/favicon.ico 下边就是 ruby-china 的 favicon 了,直接给你看吧:

#6 楼 @Martin91 不对啊,我已经在 layout 的 head 提供了一个做这个小图标的 ico 了。是不是 rails 的 505 没有用到 layout,所以自动去取 favicon.ico? 那我在 500 页面 html 直接加上这个 ico 句子好了……

#6 楼 @Martin91 账户影响什么?没懂。我是用 root 部署的。但我上传照片是在网页端的表单……

#5 楼 @liujianhei 不对啊,我是在网页端的表单上传的……如果用户上传不了东西,那应用还怎么叫应用呢?

#7 楼 @chairy11 favicon 好像都是浏览器自己去取的吧,你只要放到 public 目录下就能自动生效了。

#8 楼 @chairy11 账户影响你的服务器的执行权限。用户肯定能上传,但是如果你的服务器权限不足,就不能将用户上传的图片进行处理保存,这样一个流程就会被中断并且出现异常。

看 public 下有否 uploads 目录

写一个 cap 脚本,先用rm -rf删除原先的 upload 目录,再用 ln -s 命令把 upload 文件夹链接到服务器一个固定的位置就好了

不管你 HEAD 里有没有指定 favicon.ico 路径,有些浏览器还是会自作聪明地从跟目录下 GET 一下 favicon.ico

解决方法就是在 public 目录下放一个 favicon.ico

#1 楼 @windwiny #2 楼 @awking #3 楼 @flowerwrong #5 楼 @liujianhei #11 楼 @Martin91 #12 楼 @googya #13 楼 @MrPasserby #14 楼 @libuchao

本来我猜是 capistrano 的问题,想到在不同版本应该共享 public/uploads 文件夹下的所有上传文件。所以,我在config/deploy.rb中设置

set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads}

所以目前,public/uploads 在服务器上是应该是 shared 目录下的。

# 看到当前deploy版本的确链接过去了
root@a_project:/home/a_project/current/public# ls -l
total 3340
-rw-rw-r-- 1 root root    1507 Jan 26 03:11 404.html
-rw-rw-r-- 1 root root    1508 Jan 26 03:11 422.html
-rw-rw-r-- 1 root root    1502 Jan 26 03:11 500.html
drwxrwxr-x 5 root root    4096 Jan 26 03:14 assets
-rw-rw-r-- 1 root root    2550 Jan 26 03:11 favicon.ico
drwxrwxr-x 2 root root    4096 Jan 26 03:11 images
-rw-rw-r-- 1 root root     202 Jan 26 03:11 robots.txt
lrwxrwxrwx 1 root root      33 Jan 26 03:13 system -> /home/a_project/shared/public/system
lrwxrwxrwx 1 root root      34 Jan 26 03:14 uploads -> /home/a_project/shared/public/uploads

# 看到这个目录的权限应该还是root
root@a_project:/home/a_project/shared/public# ls -l
total 8
drwxr-xr-x 2 root root 4096 Jan 21 22:48 system
drwxr-xr-x 2 root root 4096 Jan 26 03:12 uploads

可是,当我上传图片,还是会出错。查看 nginx 出错日志,还是说::Errno::EACCES (Permission denied @ dir_s_mkdir - /home/a_project/releases/20150126081307/public/uploads/tmp):

App 23466 stderr: Started PATCH "/admin/projects/1" for 54.64.229.171 at 2015-01-26 03:36:42 -0500
App 23466 stderr: Processing by Admin::ProjectsController#update as HTML
App 23466 stderr:   Parameters: {"utf8"=>"✓", "authenticity_token"=>"xxx", "project"=>{"img_on_homepage"=>#<ActionDispatch::Http::UploadedFile:0x007f7fa0d1d630 @tempfile=#<Tempfile:/tmp/RackMultipart20150126-23483-1nxkjq0.jpg>, @original_filename="1-中国旅游信息化“十二五”发展规划项目.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"project[img_on_homepage]\"; filename=\"1-\xE4\xB8\xAD\xE5\x9B\xBD\xE6\x97\x85\xE6\xB8\xB8\xE4\xBF\xA1\xE6\x81\xAF\xE5\x8C\x96\xE2\x80\x9C\xE5\x8D\x81\xE4\xBA\x8C\xE4\xBA\x94\xE2\x80\x9D\xE5\x8F\x91\xE5\xB1\x95\xE8\xA7\x84\xE5\x88\x92\xE9\xA1\xB9\xE7\x9B\xAE.jpg\"\r\nContent-Type: image/jpeg\r\n">, "img_on_homepage_cache"=>""}, "commit"=>"更新", "id"=>"1"}
App 23466 stderr:   ^[[1m^[[35mUser Load (0.7ms)^[[0m  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1  ORDER BY `users`.`id` ASC LIMIT 1
App 23466 stderr:   ^[[1m^[[36mProject Load (0.4ms)^[[0m  ^[[1mSELECT  `projects`.* FROM `projects` WHERE `projects`.`id` = 1 LIMIT 1^[[0m
App 23466 stderr:   ^[[1m^[[35m (0.2ms)^[[0m  BEGIN
App 23466 stderr:   ^[[1m^[[36m (0.2ms)^[[0m  ^[[1mROLLBACK^[[0m
App 23466 stderr: Completed 500 Internal Server Error in 11ms
App 23466 stderr:
App 23466 stderr: Errno::EACCES (Permission denied @ dir_s_mkdir - /home/a_project/releases/20150126081307/public/uploads/tmp):
App 23466 stderr:   app/controllers/admin/projects_controller.rb:29:in `update'

难道意思是,我要把/tmp 目录也放在共享下?可是不对吧?我 public/uploads 都 shared 了,那 public/uploads/tmp 应该就没问题了啊?

可否別用 root ... 用 root 經常會問題一堆......

我认为第一是 capistrano 的 shared/public/uploads 没有创建对

project/current/public$ ls -al | grep uploads
# current下面的uploads是链接到shared下面的
lrwxrwxrwx 52 Jan 16 15:49 uploads -> /home/project/shared/public/uploads

# deploy.rb
set :linked_dirs, fetch(:linked_dirs) + %w{public/system public/uploads} # 注意 fetch(:linked_dirs) + []

# 检查并创建
cap production deploy:check:linked_dirs
cap production deploy:symlink:shared

如果还不行,那么你看我的 uploads 文机夹的权限 chmod 777 -R project/shared/public/uploads

#16 楼 @blacktulip 是嘛?我就是觉得用 root 最省事啊,什么权限都有了……以前创建用户,动不动就说我没权限,我一偷懒,就什么都用 root 了……

#17 楼 @flowerwrong 哭…没看懂… 我用的是 capistrano3,它本身就有一句

set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

感觉更短更直接,我就没用你的fetch(:linked_dirs),但与你这句应该是等同的吧?

执行你说的命令,结果是

root@a_project:/home/a_project/current/public# ls -al | grep uploads
lrwxrwxrwx  1 root root      34 Jan 26 03:14 uploads -> /home/a_project/shared/public/uploads

因为我还没有上传任何图片,所以它现在目录是空的。但 carrierwave 应该会在上传时自动创建文件夹啊。因为在 photo_uploader.rb中设置了

def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

执行chmod 777 -R /home/a_project/shared/public/uploads没输出任何结果,话说这句干什么的?

我记得我在楼主的帖子里说过的呢,Passenger 在发现文件拥有者是 root 的时候为了安全会切换成 nobody

https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html#user_switching

#18 楼 @chairy11 不行的... 偷懶必遭反噬,建議你還是換回一個普通用戶。

#20 楼 @Rei 555,看不懂……我怎么感觉它说提有 nobody 权限的人想看 root 权限的文件,所以才访问不到。可是我用的是有 root 权限的人去看 root 权限下的文件……

#21 楼 @blacktulip 呜呜呜,好吧,回去先补补 linux 权限这一课,搞不懂哪……

fetch(:linked_dirs)就是取得那个数组 chmod 777 -R /home/a_project/shared/public/uploads 改变文件夹权限,读写执行

#20 楼 @Rei 感觉这个比较靠谱。你还是 useradd 吧,不用 root

#24 楼 @flowerwrong 现在改 user 要改好多地方吧?会崩溃的啊……

#24 楼 @flowerwrong 咦,好像可以了!估计是你那句改变权限发生作用了!

需要 登录 后方可回复, 如果你还没有账号请 注册新账号