可疑操作 Django

28 投票
11 回答
38640 浏览
提问于 2025-04-15 17:18

我在尝试删除上传的图片时遇到了一个问题。

错误信息大致是这样的:

SuspiciousOperation: Attempted access to '/media/artists/12-stones/154339.jpg' denied.

我查了一下,发现这个错误是因为系统在错误的地方找图片(注意第一个斜杠,/media/在文件系统中并不存在)。

我的 MEDIA_ROOT 和 MEDIA_URL 是:

MEDIA_ROOT = '/home/tsoporan/site/media/'
MEDIA_URL = "/media/

我的模型的 upload_to 参数传入了这个函数:

def get_artist_path(instance, filename):
  return os.path.join('artists', slugify(instance.name), filename)

我有几个问题:

1) 我该如何解决这个问题,以便未来的上传不再出错?

2) 有没有办法在不重新上传的情况下修复我当前图片的路径?

祝好,
Titus

11 个回答

13

给其他人提个醒,这个问题可能是因为你要找的静态文件资源里有两个连续的'//'符号。

{{ STATIC_URL }}/style.css # Causes the issue it should be
{{ STATIC_URL }}style.css
64

我在设置上传路径的时候,如果前面加了一个斜杠,就出现了这个错误。

错误的写法

pic = models.ImageField(upload_to="/uploads/product_images/")

正确的写法

pic = models.ImageField(upload_to="uploads/product_images/")
31

好吧,稍微在代码里查找一下,发现可能有一个更深层次的错误信息在这个过程中被简化了。

在 django/core/files/storage.py 的第 210 行(这是在 1.1.1 版本中),我们看到:

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

所以这个错误应该是来自 safe_join() 函数。

在 django/utils/_os.py 中,我们有以下内容。注意倒数第三行抛出的 ValueError 错误:

===========================

def safe_join(base, *paths):
    """
    Joins one or more path components to the base path component intelligently.
    Returns a normalized, absolute version of the final path.

    The final path must be located inside of the base path component (otherwise
    a ValueError is raised).
    """
    # We need to use normcase to ensure we don't false-negative on case
    # insensitive operating systems (like Windows).
    base = force_unicode(base)
    paths = [force_unicode(p) for p in paths]
    final_path = normcase(abspathu(join(base, *paths)))
    base_path = normcase(abspathu(base))
    base_path_len = len(base_path)
    # Ensure final_path starts with base_path and that the next character after
    # the final path is os.sep (or nothing, in which case final_path must be
    # equal to base_path).
    if not final_path.startswith(base_path) \
       or final_path[base_path_len:base_path_len+1] not in ('', sep):
        raise ValueError('the joined path is located outside of the base path'
                         ' component')
    return final_path

==================

嗯,“连接的路径位于基本路径组件之外”。这里面有几个 abspathu() 的调用(这个函数在这个例程的上面定义,对于 NT 系统和其他操作系统是不同的)。abspathu() 会把所有非绝对路径转换为绝对路径,方法是加上 os.cwdu(),也就是当前工作目录。

问题:你是否有一个指向媒体目录的符号链接(symlink)?换句话说,它不是项目目录的直接子目录吗?我不知道这个问题是否有效,这只是我脑子里冒出来的。

问题:传递给 safe_join() 的 self.locationname 的值是什么?

随便猜测一下:self.location 是不是为空?

再随便猜测一下:MEDIA_ROOT 是不是被改成了 /media/

如果你有自己安装的 Django(其实不难),可以在这些函数里加一些打印语句,然后作为开发服务器运行。打印输出会显示在控制台上。

更新:嗯。你说“2) self.location 和 name 的值是:/home/tsoporan/site/media 和 /media/albums/anthem-for-the-underdog/30103635.jpg”

下面这个路径有没有什么意义?

"/home/tsoporan/site/media/media/albums/anthem-for-the-underdog"

注意里面的 .../media/media/...。

另外,这是什么操作系统?Django 的版本是多少?

撰写回答