Für ein Kundenprojekt habe ich einen Webservice mit dem beliebten Python Framwork Django (https://www.djangoproject.com/) umgesetzt. Der Webservice läuft auf einem nginx Webserver. Für eine Erweiterung sollten PDFs erzeugt und diese auf's Dateisystem ablegt werden. Über einen Endpunkt ruft das Frontend dann den Pfad der PDF ab. Da ich in der Vergangenheit viel mit cakePHP in Verbindung mit dem apache Server gearbeitet habe, kam es hierbei für mich zu einigen konzeptionellen Hürden.  

Die Django Dokumentation beinhaltet im Kapitel zu Static Files (https://docs.djangoproject.com/en/2.2/howto/static-files/) Informationen, wie man statische Dateien mit Django ausliefert. Hierbei ist es wichtig zu verstehen, dass man in der Entwicklung und in der tatsächlichen Veröffentlichung seiner Anwendung unterschiedlich an das Thema herangeht.

In meinem eingangs beschriebenen Szenario, generiere ich mit dem Aufruf eines Endpunkts eine PDF. Wie im folgenden Codeausschnitt zu sehen ist, wird das PDF im default_storage (bei mir ist das Default Storage das FileSystem) abgespeichert und dann der Dateiname zurückgegeben. Mit dem Dateinamen wird dann die URL zur Datei zusammengebaut.

...
pdf = pdfkit.from_string(html, False, configuration=config)
pdfFile = ContentFile(pdf)
file_name = default_storage.save('myfiles/mypdf.pdf', pdfFile)
file_url = request.build_absolute_uri('/') + default_storage.url(file_name)
...

Wenn wir davon ausgehen, dass unsere Anwendung auf django.nettrek.de läuft, hatte ich die Erwartungshaltung, dass ich nun einfach im Webbrowser https://django.nettrek.de/myfiles/mypdf.pdf aufrufe und so die generierte PDF erhalte. Da war ich dann etwas naiv.

Django bietet die Möglichkeit, wie in der Doku beschrieben, die statischen Dateien mit einem Konsolenskript in einem Projekt zu sammeln und dann in einen speziellen Ordner zu kopieren, welcher dann über den Webserver zur Verfügung gestellt wird. Dieser Ordner wird über die Konstante STATIC_FILES in den settings konfiguriert. Hier hinterlegt man den absoluten Pfad zu einem Ordner in dem dann Django unter anderem auch über die default_storage.save() Funktion Dateien schreibt. Mit STATIC_URL gibt man eine Route an, die dann den Django Router dazu veranlasst den in STATIC_FILES angegebenen Pfad für den Webserver zugreifbar zu machen.

Da ich nicht bei jedem Erzeugen einer PDF das Konsolenskript zur Veröffentlichung der statischen Dateien (https://docs.djangoproject.com/en/2.2/howto/static-files/deployment/) ausführen wollte, schreibt mein Skript die PDFs einfach direkt in den STATIC_FILES Ordner.

Um nun die Dateien über eine URL erreichen zu können, muss noch der nginx Server für django.nettrek.de konfiguriert werden. Nehmen wir an, wir schreiben in /home/nettrek/myfiles/ und wollen Dateien in diesem Ordner dann über https://django.nettrek.de/pdfs erreichen.

In unserer nginx Konfiguration (in meinem Fall zu finden unter etc/nginx/conf.d/django.nettrek.de.conf) erweitern wir die Server Direktive nun folgendermaßen:

...
server {
    location /pdfs/ {
        alias /home/nettrek/myfiles/;
    }
}
...

Nachdem wir den Nginx Server Dienst und in meinem Fall den UWSGI Dienst neu gestartet haben, kann man die PDF nun über https://django.nettrek.de/pdfs/mypdf.pdf herunterladen.

Meine anfänglichen Probleme bestanden letzten Endes einfach darin, dass ich die entsprechenden mod_rewrite Regeln aus der Standard webroot/.htaccess in einem cakePHP Projekt so sehr für gegeben hingenommen habe, dass ich zunächst einfach verwundert war, dass ich mich ja noch um die Auslieferung von Dateien auf der Seite des Webservers kümmern musste. Vielleicht hilft das hier dem einen oder anderen weiter.

Geschrieben von Sebastian Köller