Weasyprint не удалось загрузить изображение по URL-адресу: имя или служба неизвестны

#python #django #django-templates #weasyprint

Вопрос:

Я работаю с weasyprint после переноса с xhtml2pdf и нахожу некоторые проблемы с получением статических файлов. Я получаю следующую ошибку:

 2021-12-03 14:45:50,198 [ERROR] Failed to load image at "http://api.dashboard.localhost:8000/static/logos/logo.png" (URLError: lt;urlopen error [Errno -2] Name or service not knowngt;)  

но когда я получаю доступ к тому же URL-адресу, который weasyprint не смог ни в моем браузере, ни в curl, я могу просматривать/ получать доступ к файлу.

Вот мой код:

 from io import BytesIO import mimetypes from pathlib import Path from urllib.parse import urlparse import logging  from django.conf import settings from django.contrib.staticfiles.finders import find from django.core.files.storage import default_storage from django.urls import get_script_prefix from django.template.loader import render_to_string import weasyprint from weasyprint import HTML   logging.basicConfig(  level=logging.INFO,  format="%(asctime)s [%(levelname)s] %(message)s",  handlers=[  logging.FileHandler("debug.log"),  logging.StreamHandler()  ] )  # https://github.com/fdemmer/django-weasyprint/blob/main/django_weasyprint/utils.py def url_fetcher(url, *args, **kwargs):  # load file:// paths directly from disk  if url.startswith('file:'):  mime_type, encoding = mimetypes.guess_type(url)  url_path = urlparse(url).path  data = {  'mime_type': mime_type,  'encoding': encoding,  'filename': Path(url_path).name,  }   default_media_url = settings.MEDIA_URL in ('', get_script_prefix())  if not default_media_url and url_path.startswith(settings.MEDIA_URL):  media_root = settings.MEDIA_ROOT  if isinstance(settings.MEDIA_ROOT, Path):  media_root = f'{settings.MEDIA_ROOT}/'  path = url_path.replace(settings.MEDIA_URL, media_root, 1)  data['file_obj'] = default_storage.open(path)  return data   elif settings.STATIC_URL and url_path.startswith(settings.STATIC_URL):  path = url_path.replace(settings.STATIC_URL, '', 1)  data['file_obj'] = open(find(path), 'rb')  return data   # fall back to weasyprint default fetcher  return weasyprint.default_url_fetcher(url, *args, **kwargs)   def render_template_to_pdf(template_path, request, context):  results = BytesIO()  template_string = render_to_string(  template_name=template_path,  context=context,  )  # create the pdf report  HTML(string=template_string, base_url=request.build_absolute_uri("/"), url_fetcher=url_fetcher).write_pdf(results)  return results.getbuffer()  

Приведенный выше код генерирует pdf-файл, но без изображений, так как вышеупомянутая ошибка продолжает отображаться в моих журналах.

Мои настройки для мультимедийных/ статических файлов:

 DEFAULT_FILE_STORAGE = "utils.storages.CustomFileSystemStorage" STATIC_URL = "/static/" STATIC_ROOT = os.path.realpath(env.str("STATIC_FILES_ROOT", default=os.path.join(BASE_DIR, "staticfiles")   "/")) MEDIA_URL = "/media/" MEDIA_ROOT = os.path.realpath(env.str("MEDIA_FILES_ROOT", default=os.path.join(BASE_DIR, "mediafiles")   "/")) STATICFILES_DIRS = [  os.path.join(BASE_DIR, "static"), ]   

В моем шаблоне:

 {% load static %}   lt;div style="float: right;"gt;  lt;img src="{% static 'logos/logo.jpg' %}" alt="logo" width="140" height="40"/gt; lt;/divgt;  

Я запускаю это в docker, но я думаю, что это может быть неуместно, так как я мог бы получить доступ к файлам вне приложения (браузер/ curl), но не с помощью weasyprint.

Я проверял ответы на stackoverflow/github/etc, Но, к сожалению, не смог найти ничего, объясняющего, почему это происходит или как это обойти. Любые идеи о том, почему это происходит, очень ценятся!

Ответ №1:

Докер-это не проблема, потому что я также получаю аналогичную ошибку при использовании статики так же, как вы делали без докера. Я получаю ошибку, показанную ниже:

[weasyprint:137] ОШИБКА: Относительная ссылка URI без базового URI:

Итак, что я сделал, так это использовал urlsplit для получения URL-адреса моего приложения и передал его в шаблон, чтобы я мог использовать полный URL-адрес.

 from django.utils.six.moves.urllib.parse import urlsplit  def test(request):  scheme = urlsplit(request.build_absolute_uri(None))  context = {   'host_url': f"{scheme.scheme}://{scheme.netloc}"  }  return render(request, 'pdf.html', context)  

шаблон

 lt;img src="{{ host_url }}/logos/logo.jpg" alt="" /gt;  

Вместо передачи host_url вы можете напрямую использовать свой домен, но использование host_url делает приложение динамичным, и вам не нужно менять домен, находясь в другом домене или используя локальный сервер.

Комментарии:

1. Эй, спасибо тебе за твой ответ. Однако это не решает проблему. У меня есть базовый URI, и окончательный URL-адрес, созданный host_url таким же, как и тот, который используется static . Также просто хочу отметить, что модуль django.utils.six был удален из django-3.0. Я ценю вашу помощь.

Ответ №2:

Я не уверен, почему, но установка base_url на "." решение проблемы, и weasyprint теперь может разрешать как локальные, так и внешние статические файлы.

Изменения вступают в силу в:

 HTML(string=template_string, base_url=".", url_fetcher=url_fetcher).write_pdf(results)  

Это заняло у меня целый день, и я изучил исходный код как weasyprint, так и django-weasyprint, прежде чем пытаться "." . Я надеюсь, что это спасет кого-нибудь с такой же проблемой в будущем.