So liefern Sie statische Dateien in Flask

539

Das ist also peinlich. Ich habe eine Anwendung, die ich zusammengestellt habe, Flaskund im Moment wird nur eine einzige statische HTML-Seite mit einigen Links zu CSS und JS bereitgestellt. Und ich kann nicht finden, wo in der Dokumentation Flaskdie Rückgabe statischer Dateien beschrieben wird. Ja, ich könnte verwenden, render_templateaber ich weiß, dass die Daten nicht mit Vorlagen versehen sind. Ich hätte gedacht send_fileoder url_forwar das Richtige, aber ich konnte diese nicht zum Laufen bringen. In der Zwischenzeit öffne ich die Dateien, lese Inhalte und richte einen Responsemit dem entsprechenden Mimetyp ein:

import os.path

from flask import Flask, Response


app = Flask(__name__)
app.config.from_object(__name__)


def root_dir():  # pragma: no cover
    return os.path.abspath(os.path.dirname(__file__))


def get_file(filename):  # pragma: no cover
    try:
        src = os.path.join(root_dir(), filename)
        # Figure out how flask returns static files
        # Tried:
        # - render_template
        # - send_file
        # This should not be so non-obvious
        return open(src).read()
    except IOError as exc:
        return str(exc)


@app.route('/', methods=['GET'])
def metrics():  # pragma: no cover
    content = get_file('jenkins_analytics.html')
    return Response(content, mimetype="text/html")


@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def get_resource(path):  # pragma: no cover
    mimetypes = {
        ".css": "text/css",
        ".html": "text/html",
        ".js": "application/javascript",
    }
    complete_path = os.path.join(root_dir(), path)
    ext = os.path.splitext(path)[1]
    mimetype = mimetypes.get(ext, "text/html")
    content = get_file(complete_path)
    return Response(content, mimetype=mimetype)


if __name__ == '__main__':  # pragma: no cover
    app.run(port=80)

Möchte jemand ein Codebeispiel oder eine URL dafür angeben? Ich weiß, dass das ganz einfach sein wird.

hughdbrown
quelle
6
Verwenden Sie Nginx oder andere Webserver, um statische Dateien bereitzustellen.
Atupal
8
Beachten Sie bitte, dass sich die Art und Weise, wie Sie die Dateien tatsächlich "bereitstellen", wahrscheinlich zwischen Produktion (auf Ihrem Webserver) und Entwicklung (auf Ihrem lokalen Computer oder einem anderen Testbereich) unterscheidet. Wie einige Antworten gezeigt haben, möchten Sie Ihre statischen Dateien wahrscheinlich NICHT mit flask bereitstellen, sondern sie stattdessen in einem eigenen Verzeichnis haben und diese Dateien dann direkt von Ihrem eigentlichen Webserver (Apache, Nginx usw.) verwalten lassen.
Mark Hildreth
75
"Warum nicht Nginx verwenden ..." Denn wenn ich es im Entwicklermodus auf meinem Laptop ausführe, ist es schön, nur eine Sache und nur eine Sache ausführen zu müssen. Ja, das macht die Dinge ein bisschen anders, aber das ist okay.
Thanatos
1
Selbst in der Produktion ist dies sehr häufig zu sehen, natürlich mit einer Cache-Ebene vor (wie Varnish oder Nginx oder einem CDN).
Thomas Decaux

Antworten:

643

Die bevorzugte Methode besteht darin, nginx oder einen anderen Webserver zum Bereitstellen statischer Dateien zu verwenden. Sie werden es effizienter machen können als Flask.

Sie können send_from_directoryjedoch Dateien aus einem Verzeichnis senden, was in einigen Situationen sehr praktisch sein kann:

from flask import Flask, request, send_from_directory

# set the project root directory as the static folder, you can set others.
app = Flask(__name__, static_url_path='')

@app.route('/js/<path:path>')
def send_js(path):
    return send_from_directory('js', path)

if __name__ == "__main__":
    app.run()

Verwenden Sie keinensend_file oder send_static_fileeinen vom Benutzer angegebenen Pfad.

send_static_file Beispiel:

from flask import Flask, request
# set the project root directory as the static folder, you can set others.
app = Flask(__name__, static_url_path='')

@app.route('/')
def root():
    return app.send_static_file('index.html')
atupal
quelle
12
Um Windows zu unterstützen: Geben Sie app.send_static_file (os.path.join ('js', path) .replace ('\\', '/')) zurück
Tony BenBrahim
9
Kann ein Angreifer diese Methode ausnutzen, um die Flask-Quelldateien zu durchsuchen, indem er eine Art / js / <eine clevere Codierung von "../ yourflaskapp.py"> durchsucht?
Akiva
30
@kiwi wurde send_from_directoryentwickelt, um dieses Sicherheitsproblem zu lösen. Es besteht ein Fehler, wenn der Pfad außerhalb des jeweiligen Verzeichnisses führt.
jpmc26
10
"Verwenden Sie send_file oder send_static_file nicht mit einem vom Benutzer angegebenen Pfad." warum nicht?
Drew Verlee
6
@DenisV hat nichts mit Python an sich zu tun, es ist die Flask-Konvention zum Definieren von URL-Parametern (siehe http://flask.pocoo.org/docs/0.12/api/#url-route-registrations ). Kurz gesagt, <path>entspricht <string:path>und weil Sie möchten, dass Flask einen pfadähnlichen Parameter sicherstellt, nach dem Sie fragen <path:path>.
b4stien
135

Wenn Sie nur den Speicherort Ihrer statischen Dateien verschieben möchten, ist es am einfachsten, die Pfade im Konstruktor zu deklarieren. Im folgenden Beispiel habe ich meine Vorlagen und statischen Dateien in einen Unterordner namens verschoben web.

app = Flask(__name__,
            static_url_path='', 
            static_folder='web/static',
            template_folder='web/templates')
  • static_url_path=''Entfernt alle vorhergehenden Pfade aus der URL (dh die Standardeinstellung /static).
  • static_folder='web/static'um alle im Ordner gefundenen Dateien web/staticals statische Dateien bereitzustellen.
  • template_folder='web/templates' In ähnlicher Weise wird dadurch der Vorlagenordner geändert.

Mit dieser Methode gibt die folgende URL eine CSS-Datei zurück:

<link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css">

Und zum Schluss hier ein Ausschnitt aus der Ordnerstruktur, in der flask_server.pysich die Flask-Instanz befindet:

Verschachtelte statische Flaschenordner

Richard Dunn
quelle
9
Das hat auch bei mir funktioniert. Send_from_directory hat trotz aller Empfehlungen einfach nicht funktioniert.
GA
Funktioniert perfekt. Vielen Dank <3.
Thuat Nguyen
Wie sieht der Weg aus? get_static_file('index.html')?
Batman
Das funktioniert gut, wie GA sagte, nichts anderes hat bei mir funktioniert und das hat alles behoben.
Sehr
81

Sie können auch, und dies ist mein Favorit, einen Ordner als statischen Pfad festlegen, damit die darin enthaltenen Dateien für alle erreichbar sind.

app = Flask(__name__, static_url_path='/static')

Mit diesem Set können Sie das Standard-HTML verwenden:

<link rel="stylesheet" type="text/css" href="/static/style.css">
scharfer Schatten
quelle
4
Funktioniert gut, wenn eine Datei project/static/style.cssverfügbar ist.
Pavel Vlasov
6
Die Zeile "app = Flask (....)" benötigt auch "static_folder", um ein Parameter zu sein
datdinhquoc
Ich habe stundenlang mit diesem Problem zu kämpfen! Mir fehlte nur ein einziges Argument!
LogicalBranch
78

Ich bin sicher, dass Sie dort finden, was Sie brauchen: http://flask.pocoo.org/docs/quickstart/#static-files

Grundsätzlich benötigen Sie nur einen "statischen" Ordner im Stammverzeichnis Ihres Pakets. Anschließend können Sie url_for('static', filename='foo.bar')Ihre Dateien mit http://example.com/static/foo.bar verwenden oder direkt mit ihnen verknüpfen .

BEARBEITEN : Wie in den Kommentaren vorgeschlagen, können Sie den '/static/foo.bar'URL-Pfad direkt verwenden, ABER der url_for() Aufwand (in Bezug auf die Leistung) ist recht gering. Wenn Sie ihn verwenden, können Sie das Verhalten anschließend problemlos anpassen (Ordner ändern, URL-Pfad ändern, Verschieben Sie Ihre statischen Dateien nach S3 usw.).

b4stien
quelle
14
Warum nicht '/static/foo.bar'direkt?
Tyler Long
3
@TylerLong ist richtig - Wenn Sie eine Verknüpfung zu einer Datei herstellen möchten, die bereits in Ihrem statischen Verzeichnis gespeichert ist, können Sie ohne Routencode direkt darauf verlinken.
hamx0r
42

Sie können diese Funktion verwenden:

send_static_file(filename)
Intern verwendete Funktion zum Senden statischer Dateien aus dem statischen Ordner an den Browser.

app = Flask(__name__)
@app.route('/<path:path>')
def static_file(path):
    return app.send_static_file(path)
Schwarze Mamba
quelle
1
Dies war der einzige, der bei mir ohne große Kopfschmerzen funktionierte.
Kenny Powers
Gleich. Wo mir klar wird, dass Flash stark von der Idee abhängt, dass wir ihr Vorlagensystem verwenden, nicht von einer RIA, bei der der HTML-Code an anderer Stelle erstellt wird.
NiKo
15
WARNUNG: Dies ist ein großes Sicherheitsrisiko, das send_static_filebei Benutzereingaben aufgerufen werden muss. Verwenden Sie diese Lösung nicht für wichtige Zwecke.
xApple
41

Was ich benutze (und es hat großartig funktioniert), ist ein "Vorlagen" -Verzeichnis und ein "statisches" Verzeichnis. Ich platziere alle meine .html-Dateien / Flask-Vorlagen im Vorlagenverzeichnis und static enthält CSS / JS. Meines Wissens funktioniert render_template für generische HTML-Dateien einwandfrei, unabhängig davon, in welchem ​​Umfang Sie die Vorlagen-Syntax von Flask verwendet haben. Unten finden Sie einen Beispielaufruf in meiner Datei views.py.

@app.route('/projects')
def projects():
    return render_template("projects.html", title = 'Projects')

Stellen Sie einfach sicher, dass Sie url_for () verwenden, wenn Sie auf eine statische Datei im separaten statischen Verzeichnis verweisen möchten. Sie werden dies wahrscheinlich sowieso in Ihren CSS / JS-Dateilinks in HTML tun. Zum Beispiel...

<script src="{{ url_for('static', filename='styles/dist/js/bootstrap.js') }}"></script>

Hier ist ein Link zum "kanonischen" informellen Flask-Tutorial - viele großartige Tipps, die Ihnen dabei helfen, die ersten Schritte zu unternehmen.

http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

Kyle Sum
quelle
38

Ein einfachstes Arbeitsbeispiel basierend auf den anderen Antworten ist das Folgende:

from flask import Flask, request
app = Flask(__name__, static_url_path='')

@app.route('/index/')
def root():
    return app.send_static_file('index.html')

if __name__ == '__main__':
  app.run(debug=True)

Mit dem HTML- Code index.html :

<!DOCTYPE html>
<html>
<head>
    <title>Hello World!</title>
</head>
<body>
    <div>
         <p>
            This is a test.
         </p>
    </div>
</body>
</html>

WICHTIG: Und index.html befindet sich in einem Ordner namens static , was bedeutet, dass <projectpath>die .pyDatei und <projectpath>\staticdie htmlDatei vorhanden sind.

Wenn der Server im Netzwerk sichtbar sein soll, verwenden Sie app.run(debug=True, host='0.0.0.0')

BEARBEITEN: Verwenden Sie diese Option, um auf Anfrage alle Dateien im Ordner anzuzeigen

@app.route('/<path:path>')
def static_file(path):
    return app.send_static_file(path)

Welches ist im Wesentlichen BlackMambadie Antwort, also geben Sie ihnen eine Gegenstimme.

EpicPandaForce
quelle
Danke für die wichtige Beobachtung!
Gleidson Cardoso da Silva
13

Für Winkel + Boilerplate-Fluss, der den nächsten Ordnerbaum erstellt:

backend/
|
|------ui/
|      |------------------build/          <--'static' folder, constructed by Grunt
|      |--<proj           |----vendors/   <-- angular.js and others here
|      |--     folders>   |----src/       <-- your js
|                         |----index.html <-- your SPA entrypoint 
|------<proj
|------     folders>
|
|------view.py  <-- Flask app here

Ich benutze folgende Lösung:

...
root = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui", "build")

@app.route('/<path:path>', methods=['GET'])
def static_proxy(path):
    return send_from_directory(root, path)


@app.route('/', methods=['GET'])
def redirect_to_index():
    return send_from_directory(root, 'index.html')
...

Es hilft, den 'statischen' Ordner neu zu definieren.

user1671599
quelle
Basierend auf Ihrer Antwort habe ich dies getan: stackoverflow.com/a/29521067/303114 Beachten Sie , dass ich 'add_url_rule' intead 'route' verwendet habe, was im Grunde die gleiche ist
danfromisrael
7

Also habe ich die Dinge zum Laufen gebracht (basierend auf der Antwort von @ user1671599) und wollte sie mit euch teilen.

(Ich hoffe, ich mache es richtig, da es meine erste App in Python ist.)

Ich war das -

Projektstruktur:

Geben Sie hier die Bildbeschreibung ein

server.py:

from server.AppStarter import AppStarter
import os

static_folder_root = os.path.join(os.path.dirname(os.path.abspath(__file__)), "client")

app = AppStarter()
app.register_routes_to_resources(static_folder_root)
app.run(__name__)

AppStarter.py:

from flask import Flask, send_from_directory
from flask_restful import Api, Resource
from server.ApiResources.TodoList import TodoList
from server.ApiResources.Todo import Todo


class AppStarter(Resource):
    def __init__(self):
        self._static_files_root_folder_path = ''  # Default is current folder
        self._app = Flask(__name__)  # , static_folder='client', static_url_path='')
        self._api = Api(self._app)

    def _register_static_server(self, static_files_root_folder_path):
        self._static_files_root_folder_path = static_files_root_folder_path
        self._app.add_url_rule('/<path:file_relative_path_to_root>', 'serve_page', self._serve_page, methods=['GET'])
        self._app.add_url_rule('/', 'index', self._goto_index, methods=['GET'])

    def register_routes_to_resources(self, static_files_root_folder_path):

        self._register_static_server(static_files_root_folder_path)
        self._api.add_resource(TodoList, '/todos')
        self._api.add_resource(Todo, '/todos/<todo_id>')

    def _goto_index(self):
        return self._serve_page("index.html")

    def _serve_page(self, file_relative_path_to_root):
        return send_from_directory(self._static_files_root_folder_path, file_relative_path_to_root)

    def run(self, module_name):
        if module_name == '__main__':
            self._app.run(debug=True)
danfromisrael
quelle
Zum besseren Verständnis können Sie diese Antwort lesen: stackoverflow.com/a/23501776/303114 (was Sie auf die Quelle in Github verweist)
danfromisrael
6

Eine der einfachen Möglichkeiten. Prost!

demo.py.

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/")
def index():
   return render_template("index.html")

if __name__ == '__main__':
   app.run(debug = True)

Erstellen Sie nun den Ordnernamen Vorlagen . Fügen Sie Ihre index.html- Datei im Vorlagenordner hinzu

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Python Web Application</title>
</head>
<body>
    <div>
         <p>
            Welcomes You!!
         </p>
    </div>
</body>
</html>

Projektstruktur

-demo.py
-templates/index.html
Maheshvirus
quelle
Sie haben die Frage nicht gelesen. Ich habe ausdrücklich gesagt, dass mir die render_templateLösung bekannt ist, ich wollte sie aber nicht, da die Datei statisch und ohne Ersatz war: "Ja, ich könnte render_template verwenden, aber ich weiß, dass die Daten nicht mit Vorlagen versehen sind."
Hughdbrown
Die einzige Lösung, die unter Windows problemlos funktioniert hat, danke!
Basj
4

Ich dachte daran zu teilen ... dieses Beispiel.

from flask import Flask
app = Flask(__name__)

@app.route('/loading/')
def hello_world():
    data = open('sample.html').read()    
    return data

if __name__ == '__main__':
    app.run(host='0.0.0.0')

Das funktioniert besser und einfacher.

Jeevan Chaitanya
quelle
Können Sie bitte erläutern, wie dies besser funktionieren wird?
Arsho
1
lmao jede andere Methode gab mir einige nervige Datei nicht gefundene Fehler. nice1 jeevan
Dmitri DB
3

Verwenden redirect undurl_for

from flask import redirect, url_for

@app.route('/', methods=['GET'])
def metrics():
    return redirect(url_for('static', filename='jenkins_analytics.html'))

Dieser Server alle Dateien (CSS & JS ...), auf die in Ihrem HTML verwiesen wird.

forzagreen
quelle
2

Am einfachsten ist es, einen statischen Ordner im Hauptprojektordner zu erstellen. Statischer Ordner mit CSS-Dateien.

Hauptordner

/Main Folder
/Main Folder/templates/foo.html
/Main Folder/static/foo.css
/Main Folder/application.py(flask script)

Bild des Hauptordners mit statischen Ordnern und Vorlagenordnern und Flask-Skript

Flasche

from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def login():
    return render_template("login.html")

HTML (Layout)

<!DOCTYPE html>
<html>
    <head>
        <title>Project(1)</title>
        <link rel="stylesheet" href="/static/styles.css">
     </head>
    <body>
        <header>
            <div class="container">
                <nav>
                    <a class="title" href="">Kamook</a>
                    <a class="text" href="">Sign Up</a>
                    <a class="text" href="">Log In</a>
                </nav>
            </div>
        </header>  
        {% block body %}
        {% endblock %}
    </body>
</html>

html

{% extends "layout.html" %}

{% block body %}
    <div class="col">
        <input type="text" name="username" placeholder="Username" required>
        <input type="password" name="password" placeholder="Password" required>
        <input type="submit" value="Login">
    </div>
{% endblock %}
ESbros
quelle
2
app = Flask(__name__, static_folder="your path to static")

Wenn Sie Vorlagen in Ihrem Stammverzeichnis haben, funktioniert das Platzieren von app = Flask ( Name ), wenn sich die Datei, die diese enthält, ebenfalls am selben Speicherort befindet. Wenn sich diese Datei an einem anderen Speicherort befindet, müssen Sie den zu aktivierenden Vorlagenspeicherort angeben Flasche, um auf den Ort zu zeigen

Novak254
quelle
4
Können Sie erklären, warum dies funktioniert?
Wirtschaft
1
Wie unterscheidet es sich von dieser Antwort, die eine Erklärung liefert?
Gino Mempin
Ich habe eine Erklärung für meine Antwort @economy
Novak254
1

Alle Antworten sind gut, aber was für mich gut funktioniert hat, ist nur die einfache Funktion send_filevon Flask. Dies funktioniert gut, wenn Sie nur eine HTML-Datei als Antwort senden müssen, wenn host: port / ApiName die Ausgabe der Datei im Browser anzeigt


@app.route('/ApiName')
def ApiFunc():
    try:
        return send_file('some-other-directory-than-root/your-file.extension')
    except Exception as e:
        logging.info(e.args[0])```
Binoy S Kumar
quelle
0

   Standardmäßig verwendet flask einen "Vorlagen" -Ordner, der alle Ihre Vorlagendateien enthält (jede .htmlNur -Text-Datei, aber normalerweise oder eine Art Vorlagensprache wie jinja2), und einen "statischen" Ordner, der alle Ihre statischen Dateien enthält (dh .js .cssund Ihre Bilder).
   In Ihrer routeskönnen Sie render_template()eine Vorlagendatei (wie oben erwähnt, standardmäßig im templatesOrdner abgelegt ) als Antwort auf Ihre Anfrage rendern . Und in der Vorlagendatei (normalerweise eine HTML-ähnliche Datei) können Sie einige .jsund / oder CSS-Dateien verwenden. Ich denke, Ihre Frage ist, wie Sie diese statischen Dateien mit der aktuellen Vorlagendatei verknüpfen.

Harvey
quelle
0

Wenn Sie nur versuchen, eine Datei zu öffnen, können Sie verwenden app.open_resource(). Das Lesen einer Datei würde also ungefähr so ​​aussehen

with app.open_resource('/static/path/yourfile'):
      #code to read the file and do something
Chaitanya Shivade
quelle