Django 在尝试获取静态文件路径时出错

问题描述 投票:0回答:1

我正在尝试实现xhtml2pdf,并且在我的html文件中,它引用了一个静态文件。我的项目中的文件位于

jobs/static/jobs/style.css
。当使用 xhtml2pdf 在他们的文档中提供的代码时,当它到达
result = finders.find(uri)
并说
django.core.exceptions.SuspiciousFileOperation: The joined path (/static/jobs/css/style.css) is located outside of the base path component (/opt/project/myproject/static)
时,我收到错误。如果我导航到该文件应位于的 url,
http://127.0.0.1:8000/static/jobs/css/style.css
,我会看到 css 文件的代码,因此它通常位于正确的位置。

这个项目正在 docker 容器中提供服务,并且 /opt 中没有存储任何内容,所以我不知道为什么它会在 (/opt/project/myproject/static) 中出现这个“基本路径组件”。我不知道这条路是从哪里来的。我搜索了整个项目,在任何文件中都没有包含

opt
project
的路径。该项目存储在容器中的/app中。

这是我从他们的网站获得的代码:

from xhtml2pdf import pisa
from django.template.loader import get_template
from django.http import HttpResponse
from django.conf import settings
import os
import io
from django.contrib.staticfiles import finders


def link_callback(uri, rel):
    """
    Convert HTML URIs to absolute system paths so xhtml2pdf can access those
    resources
    """
    result = finders.find(uri)
    if result:
        if not isinstance(result, (list, tuple)):
            result = [result]
        result = list(os.path.realpath(path) for path in result)
        path = result[0]
    else:
        sUrl = settings.STATIC_URL  # Typically /static/
        sRoot = settings.STATIC_ROOT  # Typically /home/userX/project_static/
        mUrl = settings.MEDIA_URL  # Typically /media/
        mRoot = settings.MEDIA_ROOT  # Typically /home/userX/project_static/media/

        if uri.startswith(mUrl):
            path = os.path.join(mRoot, uri.replace(mUrl, ""))
        elif uri.startswith(sUrl):
            path = os.path.join(sRoot, uri.replace(sUrl, ""))
        else:
            return uri

    # make sure that file exists
    if not os.path.isfile(path):
        raise Exception(
            'media URI must start with %s or %s' % (sUrl, mUrl)
        )
    return path


def render_to_pdf(template_src, context_dict, bytes=False):
    template = get_template(template_src)
    context = context_dict
    if bytes:
        response = io.BytesIO()
    else:
        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = 'attachment; filename="contract.pdf"'
    html = template.render(context)

    pisa_status = pisa.CreatePDF(html, dest=response, link_callback=link_callback)

    # if error then show some funny view
    if pisa_status.err:
        return HttpResponse('We had some errors <pre>' + html + '</pre>')
    return response

这是我的settings.py的相关部分:

ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent  # Resolves to /app
APPS_DIR = ROOT_DIR / "myproject"  # Resolves to /app/myproject

STATIC_ROOT = str(ROOT_DIR / "staticfiles")  # Resolves to /app/staticfiles
STATIC_URL = "/static/"
STATICFILES_DIRS = [
    str(APPS_DIR / "static"),
    str(ROOT_DIR / "node_modules"),
]  # Resolves to ['/app/myproject/static', '/app/node_modules']
STATICFILES_FINDERS = [
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
]

MEDIA_ROOT = str(APPS_DIR / "media")  # Resolves to /app/media/myproject/media
MEDIA_URL = "/media/"

我的 html 模板中存在问题的行是:

<link rel="stylesheet" href="{% static 'jobs/css/style.css' %}">

我有另一个项目,我已经使用它大约一年了,使用相同的代码,并且它一直在运行,没有出现此错误。另一个项目不在 docker 容器内,但我不明白为什么这会产生影响。

我还应该看哪里?

django static-files xhtml2pdf
1个回答
0
投票

xhtml2pdf
与 Django 集成时,尤其是在 Docker 中,由于 Django 处理静态文件的方式,您可能会遇到文件路径问题。当路径被评估为位于预期基目录之外时,通常会出现
SuspiciousFileOperation
错误。

这是一个综合解决方案,分解了可能的原因并提供了建议的修复方法:

  1. 静态文件处理:

    xhtml2pdf
    需要绝对系统路径来访问静态和媒体资源。在 Docker 容器中运行时,容器内的文件路径可能与主机上的文件路径不同。确保正确设置静态设置以反映 Docker 容器内的路径。

  2. 增强

    link_callback
    功能

    修改

    link_callback
    函数以适应绝对路径和相对路径。理想情况下,该功能应该:

    • 将相对 URL 转换为绝对系统路径。
    • 验证构建的路径是否存在于文件系统上。

    这是使用设置方法优化的

    link_callback
    功能:

    import os
    from django.conf import settings
    
    def link_callback(uri, rel):
        if uri.startswith(settings.MEDIA_URL):
            path = os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, ""))
        elif uri.startswith(settings.STATIC_URL):
            path = os.path.join(settings.STATIC_ROOT, uri.replace(settings.STATIC_URL, ""))
        else:
            return uri
    
        # Ensure the file exists
        if not os.path.isfile(path):
            raise Exception(f"Path does not exist: {path}")
    
        return path
    

    如果您使用

    finders
    ,您将尝试根据文件的 URL 获取文件的实际路径。如果由于 Docker 容器内的文件夹结构或卷映射不同而返回的路径不是您期望的路径,则可能会出现问题。

    from django.contrib.staticfiles import finders
    
    path = finders.find(uri)
    

    鉴于您当前的情况以及您面临的

    SuspiciousFileOperation
    错误,我建议坚持使用
    settings
    方法。它更直接,对项目结构的假设更少。

    但是,如果您更喜欢使用

    finders
    ,请确保您了解它如何解析路径,并确保解析的路径与 Docker 容器内的实际文件结构一致。

  3. Docker 卷映射:

    确保您的主机和 Docker 容器之间的卷映射配置正确。仔细检查 Docker Compose 或 Docker 命令以验证应用程序目录(在您的情况下为

    /app
    )是否已准确映射。

  4. 收集静态文件:

    如果 Django 设置中的

    DEBUG
    设置为
    False
    ,请确保运行
    collectstatic
    以收集
    STATIC_ROOT
    位置中的所有静态文件:

    python manage.py collectstatic
    

    此步骤确保所有静态文件在指定的

    STATIC_ROOT
    目录中可用,允许
    xhtml2pdf
    访问它们。

  5. 文件权限

    有时,这可能是权限问题。确保在 Docker 容器内运行 Django 应用程序的用户具有访问和读取静态文件和媒体文件的必要权限。

通过结合这些建议,您应该能够在 Docker 容器内运行的 Django 项目中使用

xhtml2pdf
成功生成 PDF。

© www.soinside.com 2019 - 2024. All rights reserved.