Come servire i file statici in Flask


539

Quindi questo è imbarazzante. Ho un'applicazione che ho lanciato insieme Flaske per ora serve solo una singola pagina HTML statica con alcuni collegamenti a CSS e JS. E non riesco a trovare dove nella documentazione sia Flaskdescritta la restituzione di file statici. Sì, potrei usarlo render_templatema so che i dati non sono templatizzati. Avrei pensato send_fileo url_forfosse la cosa giusta, ma non sono riuscito a farli funzionare. Nel frattempo, sto aprendo i file, leggendo i contenuti e creando un Responsemimetype appropriato:

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)

Qualcuno vuole dare un esempio di codice o un URL per questo? So che sarà semplice.


6
Perché non utilizzare nginx o altri server Web per servire file statici.
Atupal

8
Tieni presente che il modo in cui "stai servendo" i file probabilmente differirà tra produzione (sul tuo server web) e sviluppo (sul tuo computer locale o su qualche altra area di test). Come alcune risposte hanno sottolineato, probabilmente NON vorrete servire i vostri file statici con la beuta, ma invece li avrete nella loro directory e quindi avrete il vostro server web (Apache, nginx, ecc.) Direttamente quei file.
Mark Hildreth,


75
"Perché non usare nginx ..." Perché quando lo eseguo in modalità sviluppatore sul mio laptop, è bello dover solo eseguire una cosa e una sola cosa. Sì, rende le cose un po 'diverse, ma va bene.
Thanatos,

1
Anche in produzione, è molto comune vederlo, con ovviamente uno strato di cache in primo piano (come Varnish o Nginx o un CDN).
Thomas Decaux,

Risposte:


644

Il metodo preferito è usare nginx o un altro server web per servire file statici; saranno in grado di farlo in modo più efficiente di Flask.

Tuttavia, è possibile utilizzare send_from_directoryper inviare file da una directory, che può essere abbastanza conveniente in alcune situazioni:

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()

Evitare Non utilizzare send_fileo send_static_filecon un percorso fornito dall'utente.

send_static_file esempio:

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')

12
per supportare Windows: return app.send_static_file (os.path.join ('js', path) .replace ('\\', '/'))
Tony BenBrahim

9
un utente malintenzionato può sfruttare questo metodo per sfogliare i file sorgente del pallone sfogliando il tipo di / js / <una codifica intelligente di "../ yourflaskapp.py">?
Akiva,

30
@kiwi send_from_directoryè progettato per risolvere quel problema di sicurezza. Esiste per errore se il percorso conduce all'esterno della directory specifica.
jpmc26,

10
"Non utilizzare send_file o send_static_file con un percorso fornito dall'utente." perchè no?
Estratto da Verlee l'

6
@DenisV non ha nulla a che fare con Python di per sé, è la convenzione Flask per la definizione dei parametri URL (vedi http://flask.pocoo.org/docs/0.12/api/#url-route-registrations ). In breve, <path>equivale a <string:path>, e poiché si desidera che Flask assicuri un parametro simile a un percorso richiesto <path:path>.
b4stien,

136

Se vuoi solo spostare la posizione dei tuoi file statici, il metodo più semplice è dichiarare i percorsi nel costruttore. Nell'esempio seguente, ho spostato i miei modelli e file statici in una sottocartella denominata web.

app = Flask(__name__,
            static_url_path='', 
            static_folder='web/static',
            template_folder='web/templates')
  • static_url_path=''rimuove qualsiasi percorso precedente dall'URL (ovvero il valore predefinito /static).
  • static_folder='web/static'per servire tutti i file trovati nella cartella web/staticcome file statici.
  • template_folder='web/templates' allo stesso modo, questo cambia la cartella dei modelli.

Utilizzando questo metodo, il seguente URL restituirà un file CSS:

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

E infine, ecco uno schiocco della struttura delle cartelle, dove si flask_server.pytrova l'istanza Flask:

Cartelle di matracci statici nidificati


9
Questo è ciò che ha funzionato anche per me. Send_from_directory non ha funzionato nonostante tutti i consigli per questo.
GA,

Funziona perfettamente Grazie mille <3.
Thuat Nguyen,

Come appare il percorso? get_static_file('index.html')?
Batman,

funziona bene, come ha detto GA, nient'altro ha funzionato per me e questo ha risolto tutto. Molto apprezzato
Andrey Starenky il

81

Puoi anche, e questo è il mio preferito, impostare una cartella come percorso statico in modo che i file all'interno siano raggiungibili per tutti.

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

Con quel set puoi usare l'HTML standard:

<link rel="stylesheet" type="text/css" href="/static/style.css">

4
Funziona bene se è project/static/style.cssdisponibile un file .
Pavel Vlasov,

6
la riga "app = Flask (....)" richiede che anche "static_folder" sia un parametro
datdinhquoc

Ho lottato con questo problema per ore! Mi mancava solo un singolo argomento!
LogicalBranch,

78

Sono sicuro che troverai quello che ti serve lì: http://flask.pocoo.org/docs/quickstart/#static-files

Fondamentalmente hai solo bisogno di una cartella "statica" alla radice del tuo pacchetto, e quindi puoi usare url_for('static', filename='foo.bar')o collegare direttamente i tuoi file con http://example.com/static/foo.bar .

MODIFICA : Come suggerito nei commenti, è possibile utilizzare direttamente il '/static/foo.bar'percorso URL MA il url_for() sovraccarico (per quanto riguarda le prestazioni) è piuttosto basso e l'utilizzo significa che sarai in grado di personalizzare facilmente il comportamento in seguito (cambia la cartella, cambia il percorso dell'URL, sposta i tuoi file statici su S3, ecc.).


14
Perché non '/static/foo.bar'direttamente?
Tyler Long,

3
@TylerLong ha ragione: se vuoi collegarti a un file che è già stato salvato nella tua directory statica, puoi collegarti direttamente ad esso senza alcun codice di rotta.
hamx0r

42

Puoi usare questa funzione:

send_static_file(filename)
Funzione utilizzata internamente per inviare file statici dalla cartella statica al browser.

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

1
Questo è stato l'unico che ha funzionato per me senza un forte mal di testa.
Kenny Powers il

Stesso. Dove mi rendo conto che Flash si basa fortemente sull'idea che useremo il loro sistema di template, non un RIA in cui l'HTML viene prodotto altrove.
NiKo,

15
ATTENZIONE: questa è una grande preoccupazione per la sicurezza da chiamare send_static_filecon l'input dell'utente. Non utilizzare questa soluzione in qualcosa di importante.
xApple

41

Quello che uso (e ha funzionato alla grande) è una directory "modelli" e una directory "statica". Metto tutti i miei file .html / modelli Flask nella directory dei modelli e static contiene CSS / JS. render_template funziona bene per i file html generici per quanto ne so, indipendentemente dalla misura in cui hai usato la sintassi di template di Flask. Di seguito è una chiamata di esempio nel mio file views.py.

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

Assicurati di usare url_for () quando vuoi fare riferimento a qualche file statico nella directory statica separata. Probabilmente finirai per farlo comunque nei link ai tuoi file CSS / JS in html. Per esempio...

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

Ecco un link al tutorial informale "canonico" di Flask: molti ottimi suggerimenti qui per aiutarti a lanciarti a terra.

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


38

Un esempio di funzionamento più semplice basato sulle altre risposte è il seguente:

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)

Con l'HTML chiamato index.html :

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

IMPORTANTE: e index.html si trova in una cartella chiamata static , che significa che <projectpath>ha il .pyfile e <projectpath>\staticha il htmlfile.

Se si desidera che il server sia visibile sulla rete, utilizzare app.run(debug=True, host='0.0.0.0')

EDIT: per mostrare tutti i file nella cartella, se richiesto, utilizzare questo

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

Che è essenzialmente BlackMambala risposta, quindi dai loro un voto.


Grazie per l'osservazione importante!
Gleidson Cardoso da Silva,

13

Per il flusso angolare + boilerplate che crea il prossimo albero delle cartelle:

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

Uso la seguente soluzione:

...
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')
...

Aiuta a ridefinire la cartella 'statica' in modo personalizzato.


in base alla tua risposta ho fatto questo: stackoverflow.com/a/29521067/303114 avviso ho usato 'add_url_rule' intead 'route' che è sostanzialmente lo stesso
danfromisrael

7

Quindi ho fatto funzionare le cose (basato sulla risposta @ user1671599) e volevo condividerle con voi ragazzi.

(Spero di farlo bene dato che è la mia prima app in Python)

L'ho fatto -

Struttura del progetto:

inserisci qui la descrizione dell'immagine

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)

per una migliore comprensione puoi leggere questa risposta: stackoverflow.com/a/23501776/303114 (che ti indica la fonte in github)
danfromisrael

6

Uno dei modi più semplici per farlo. Saluti!

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)

Ora crea il nome della cartella chiamato template . Aggiungi il tuo file index.html all'interno della cartella dei modelli

index.html

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

Struttura del progetto

-demo.py
-templates/index.html

non hai letto la domanda. Ho espressamente detto di essere a conoscenza della render_templatesoluzione ma non volevo farlo perché il file era statico senza sostituzioni: "Sì, potrei usare render_template ma so che i dati non sono templatizzati."
hughdbrown,

L'unica soluzione che ha funzionato facilmente in Windows, grazie!
Basj

4

Pensato di condividere .... questo esempio.

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')

Funziona meglio e semplicemente.


Potete per favore elaborare come funzionerà meglio?
arsho,

1
Ogni altro metodo mi ha dato alcuni fastidiosi file non trovati errori. nice1 jeevan
Dmitri DB

3

Usa redirecteurl_for

from flask import redirect, url_for

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

Questo server tutti i file (css & js ...) a cui si fa riferimento nel tuo HTML.


2

Il modo più semplice è creare una cartella statica all'interno della cartella principale del progetto. Cartella statica contenente file .css.

cartella principale

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

Immagine della cartella principale contenente cartelle statiche e di modelli e script di matracci

borraccia

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 %}

2
app = Flask(__name__, static_folder="your path to static")

Se nella directory principale sono presenti modelli, posizionare l'app = Flask ( nome ) funzionerà se anche il file che lo contiene si trova nella stessa posizione, se questo file si trova in un'altra posizione, sarà necessario specificare la posizione del modello per abilitare Boccetta per indicare la posizione


4
Potete fornire qualche spiegazione sul perché questo funziona?
economia il

1
In cosa differisce da questa risposta che fornisce spiegazioni?
Gino Mempin,

Ho offerto una spiegazione per la mia risposta @economy
Novak254 il

1

Tutte le risposte sono buone, ma ciò che ha funzionato bene per me è solo usando la semplice funzione send_filedi Flask. Funziona bene quando devi solo inviare un file html come risposta quando host: port / ApiName mostrerà l'output del file nel browser


@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])```

0

   Per impostazione predefinita, flask utilizza una cartella "modelli" per contenere tutti i file modello (qualsiasi file di testo normale, ma in genere .htmlo un tipo di linguaggio modello come jinja2) e una cartella "statica" per contenere tutti i file statici (ad es. .js .cssE le tue immagini).
   Nel tuo routes, puoi usare render_template()per eseguire il rendering di un file modello (come ho detto sopra, per impostazione predefinita è collocato nella templatescartella) come risposta per la tua richiesta. E nel file modello (di solito è un file simile a .html), puoi utilizzare alcuni file .jse / o `.css ', quindi immagino che la tua domanda sia come collegare questi file statici al file modello corrente.


0

Se stai solo cercando di aprire un file, potresti usarlo app.open_resource(). Quindi la lettura di un file sarebbe simile

with app.open_resource('/static/path/yourfile'):
      #code to read the file and do something
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.