使用 sorl-thumbnail 的可疑操作

Posted

技术标签:

【中文标题】使用 sorl-thumbnail 的可疑操作【英文标题】:SuspiciousOperation using sorl-thumbnail 【发布时间】:2013-01-27 01:48:09 【问题描述】:

我有一个 Django Web 应用程序,它代表用户访问和操作多个服务器文件系统(例如 /fs01、/fs02 等)。我想向用户展示这些文件系统上的图像缩略图,并认为 sorl-thumbnail 将是这样做的方法。

似乎图像必须在 MEDIA_ROOT 下才能 sorl-thumbnail 创建缩略图。我的MEDIA_ROOT/Users/me/Dev/MyProject/myproj/media,所以这行得通:

path = "/Users/me/Dev/MyProject/myproj/media/pipe-img/magritte-pipe-large.jpg"
try:
  im = get_thumbnail(path, '100x100', crop='center', quality=99)
except Exception, e:
  exc_type, exc_obj, exc_tb = sys.exc_info()
  print "Failed getting thumbnail: (%s) %s" % (exc_type, e)
print "im.url = %s" % im.url

正如我所料,它会创建缩略图并打印 im.url。但是当我将path 更改为:

path = "/fs02/dir/ep340102/foo/2048x1024/magritte-pipe-large.jpg"

它失败了:

Failed getting thumbnail: (<class 'django.core.exceptions.SuspiciousOperation'>)
Attempted access to '/fs02/dir/ep340102/foo/2048x1024/magritte-pipe-large.jpg' denied.

有没有办法解决这个问题?我可以使用 sorl-thumbnail 在这些其他文件系统(例如 /fs01、/fs02、/fs03 等)下创建缩略图吗?有更好的方法吗?

更新。这是完整的堆栈跟踪:

Environment:


Request Method: GET
Request URL: http://localhost:8000/pipe/file_selection/

Django Version: 1.4.1
Python Version: 2.7.2
Installed Applications:
('django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.admin',
 'django.contrib.admindocs',
 'django.contrib.humanize',
 'django.contrib.messages',
 'pipeproj.pipe',
 'south',
 'guardian',
 'sorl.thumbnail')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware')


Traceback:
File "/Library/Python/2.7/site-packages/django/core/handlers/base.py" in get_response
  111.                         response = callback(request, *callback_args, **callback_kwargs)
File "/Library/Python/2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  20.                 return view_func(request, *args, **kwargs)
File "/Users/dylan/Dev/Pipe/pipeproj/../pipeproj/pipe/views/data.py" in file_selection
  184.  im = get_thumbnail(path, '100x100', crop='center', quality=99)
File "/Library/Python/2.7/site-packages/sorl_thumbnail-11.12-py2.7.egg/sorl/thumbnail/shortcuts.py" in get_thumbnail
  8.     return default.backend.get_thumbnail(file_, geometry_string, **options)
File "/Library/Python/2.7/site-packages/sorl_thumbnail-11.12-py2.7.egg/sorl/thumbnail/base.py" in get_thumbnail
  56.             source_image = default.engine.get_image(source)
File "/Library/Python/2.7/site-packages/sorl_thumbnail-11.12-py2.7.egg/sorl/thumbnail/engines/pil_engine.py" in get_image
  12.         buf = StringIO(source.read())
File "/Library/Python/2.7/site-packages/sorl_thumbnail-11.12-py2.7.egg/sorl/thumbnail/images.py" in read
  121.         return self.storage.open(self.name).read()
File "/Library/Python/2.7/site-packages/django/core/files/storage.py" in open
  33.         return self._open(name, mode)
File "/Library/Python/2.7/site-packages/django/core/files/storage.py" in _open
  156.         return File(open(self.path(name), mode))
File "/Library/Python/2.7/site-packages/django/core/files/storage.py" in path
  246.             raise SuspiciousOperation("Attempted access to '%s' denied." % name)

Exception Type: SuspiciousOperation at /pipe/file_selection/
Exception Value: Attempted access to '/fs02/dir/ep340102/foo/2048x1024/bettina.jpg' denied.

【问题讨论】:

我认为你应该使用相对于settings.MEDIA_ROOT 的路径而不是绝对路径。但是完整的堆栈跟踪对于做出准确的答案很有用。 我将实际路径包括在内只是为了说明目的。我也可以将path 设置为os.path.join(settings.MEDIA_ROOT, "pipe-img/magritte-pipe-large.jpg")。好主意——我现在将添加完整的堆栈跟踪。 试试"pipe-img/magritte-pipe-large.jpg" 而不是os.path.join(settings.MEDIA_ROOT, "pipe-img/magritte-pipe-large.jpg")。我认为它会自动生成绝对路径。 【参考方案1】:

SuspiciousOperation 来自此处的 FileSystemStorage.path():

def path(self, name):
try:
    path = safe_join(self.location, name)
except ValueError:
    raise SuspiciousFileOperation("Attempted access to '%s' denied." % name)
return os.path.normpath(path)

它起源于有这个测试的safe_join():

if (not normcase(final_path).startswith(normcase(base_path + sep)) and
...

这意味着计算的文件名必须存在于配置的缩略图存储中。默认情况下 settings.THUMBNAIL_STORAGE 是 settings.DEFAULT_FILE_STORAGE 是 FileSystemStorage,它将文件存储在 settings.MEDIA_ROOT 中。

您应该能够通过定义存储类为缩略图使用不同的存储路径:

from django.core.files.storage import FileSystemStorage

class ThumbnailStorage(FileSystemStorage):
    def __init__(self, **kwargs):
        super(ThumbnailStorage, self).__init__(
            location='/fs02', base_url='/fs02')

然后在settings.py中

THUMBNAIL_STORAGE = 'myproj.storage.ThumbnailStorage'

您还需要确保在该 URL 上提供了 /fs02 的内容:

if settings.DEBUG:
    patterns += patterns('',
        url(r'^fs02/(?P<path>.*)$', 'django.views.static.serve',
            'document_root': '/fs02'))

请注意,您的缩略图将根据默认的 THUMBNAIL_PREFIX 创建为 /fs02/cache/...

【讨论】:

我最终选择了与 sorl-thumbnail 不同的方向,但如果我有这个答案,我会使用它。我觉得很合适!【参考方案2】:

使用以下命令你会得到什么?

ls -la /fs02/dir/ep340102/foo/2048x1024/

如果文件所有者不是好人(和/或错误的文件权限),通常会发生访问被拒绝...

【讨论】:

不,我确保文件可以访问...-rw-rw-rw-@ 1 dylan staff 82222 Feb 6 06:28 magritte-pipe-large.jpg。我一直在研究 storage.py 代码,并认为我知道发生了什么。很快就会有更多... ls -la /fs02 怎么样?我曾经遇到过文件可以访问但不是其目录的符号链接的情况...【参考方案3】:

我通过提供一个绝对网址来做到这一点,就像这样:

from sorl.thumbnail import get_thumbnail
from django.contrib.staticfiles.storage import staticfiles_storage

image_url = staticfiles_storage.url('image.jpg')
thumbnail = get_thumbnail(image_url, '100x100')

【讨论】:

以上是关于使用 sorl-thumbnail 的可疑操作的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 sorl-thumbnail? (django)

sorl-thumbnail 不生成占位符图像

django:无法让 sorl-thumbnail 工作

sorl-thumbnail 的模板标签啥都不做

关于使用带有远程存储的 sorl-thumbnails 的 celery 的指针?

我不明白 sorl-thumbnail 如何使用 db 和缓存