使用sorl-thumbnail时的可疑操作
我有一个Django的网页应用,它可以代表用户访问和操作几个服务器上的文件系统(比如说/fs01、/fs02等等)。我想把这些文件系统里的图片做成缩略图展示给用户,所以我觉得可以用sorl-thumbnail来实现。
不过,看起来sorl-thumbnail要生成缩略图,图片必须放在MEDIA_ROOT目录下。我的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.
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')
你执行下面这个命令会得到什么结果呢?
ls -la /fs02/dir/ep340102/foo/2048x1024/
通常情况下,如果文件的拥有者不对或者文件权限设置错误,就会出现“访问被拒绝”的情况...
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,而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/...的格式。