Flask'ta statik dosyalar nasıl sunulur?


539

Yani bu utanç verici. Birlikte attığım bir uygulama var Flaskve şimdilik sadece CSS ve JS'ye bazı bağlantılar içeren tek bir statik HTML sayfası sunuyor. Ve belgelerde Flaskstatik dosyaların geri döndüğünü açıkladığı yeri bulamıyorum . Evet, kullanabilirim render_templateama verilerin geçici olmadığını biliyorum. Düşünürdüm send_fileya url_forda doğru olanıydım, ama bunları çalıştıramadım. Bu arada, dosyaları açıyorum, içerik okuyorum ve Responseuygun bir mimetype ile arma :

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)

Birisi bunun için bir kod örneği veya URL vermek istiyor? Bunun ölü basit olacağını biliyorum.


6
Neden statik dosya sunmak için nginx veya diğer web sunucularını kullanmıyorsunuz?
atupal

8
Lütfen dosyaları nasıl "sunduğunuz" un muhtemelen üretim (web sunucunuzda) ve geliştirme (yerel bilgisayarınızda veya başka bir test alanında) arasında farklılık göstereceğini unutmayın. Bazı cevapların işaret ettiği gibi, muhtemelen statik dosyalarınızı şişeye sunmak istemeyeceksiniz, ancak bunun yerine bunları kendi dizinlerinde bulundurun ve daha sonra gerçek web sunucunuz (Apache, nginx, vb.) Bu dosyaları doğrudan sunucuya sahip olursunuz.
Mark Hildreth


75
"Neden nginx kullanmıyorsunuz?" Evet, işleri biraz farklı kılıyor, ama sorun değil.
Thanatos

1
Üretimde bile, elbette ön tarafta bir önbellek katmanı (Varnish veya Nginx veya CDN gibi) görmek çok yaygındır.
Thomas Decaux

Yanıtlar:


643

Tercih edilen yöntem, statik dosyaları sunmak için nginx veya başka bir web sunucusu kullanmaktır; Flask'tan daha verimli bir şekilde yapabilirler.

Bununla birlikte, send_from_directorybazı durumlarda oldukça uygun olabilecek bir dizinden dosya göndermek için kullanabilirsiniz :

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

Do not kullanmak send_fileveya send_static_filebir kullanıcı tarafından sağlanan yolu ile.

send_static_file misal:

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
Windows'u desteklemek için: app.send_static_file (os.path.join ('js', yol) .replace ('\\', '/'))
döndürün

9
bir saldırgan, / js / <türünde "../ yourflaskapp.py"> 'nin bazı akıllı kodlamalarına göz atarak flask kaynak dosyalarına göz atmak için bu yöntemi kullanabilir mi?
akiva

30
@ kiwi send_from_directorybu güvenlik sorununu çözmek için tasarlanmıştır. Yol belirli bir dizinin dışına çıkarsa hata verir.
jpmc26

10
"Send_file veya send_static_file komutunu kullanıcı tarafından sağlanan bir yolla kullanmayın." neden olmasın?
Drew Verlee

6
@DenisV, Python per-se ile ilgisi yoktur, URL parametrelerini tanımlamak için Flask kuralıdır (bkz. Http://flask.pocoo.org/docs/0.12/api/#url-route-registrations ). Özetle <path>, eşdeğerdir <string:path>ve Flask'ın istediğiniz bir yol benzeri parametre sağlamasını istediğiniz için <path:path>.
b4stien

135

Statik dosyalarınızın konumunu taşımak istiyorsanız, en basit yöntem yapıcıdaki yolları bildirmektir. Aşağıdaki örnekte, şablonlarımı ve statik dosyalarımı adlı bir alt klasöre taşıdım web.

app = Flask(__name__,
            static_url_path='', 
            static_folder='web/static',
            template_folder='web/templates')
  • static_url_path=''URL'den önceki tüm yolları kaldırır (yani varsayılan /static).
  • static_folder='web/static'klasörde bulunan web/staticdosyaları statik dosyalar olarak sunmak için .
  • template_folder='web/templates' benzer şekilde, bu şablonlar klasörünü değiştirir.

Bu yöntemi kullanarak, aşağıdaki URL bir CSS dosyası döndürür:

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

Ve son olarak, flask_server.pyFlask örneğinin bulunduğu klasör yapısının bir eki:

İç İçe Statik Şişe Klasörleri


9
Benim için de işe yaradı. Send_from_directory, tüm önerilerine rağmen işe yaramadı.
GA

Mükemmel çalışıyor. Çok teşekkür ederim <3.
Thuat Nguyen

Yol neye benziyor? get_static_file('index.html')?
Batman

GA'nın dediği gibi, bu benim için iyi sonuç verdi ve bu benim için hiçbir şey yapmadı. Çok takdir
Andrey Starenky

81

Ayrıca, bu benim favorim, bir klasörü statik yol olarak ayarlayabilirsiniz, böylece içindeki dosyalar herkes için erişilebilir olur.

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

Bu setle standart HTML'yi kullanabilirsiniz:

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

4
project/static/style.cssKullanılabilir dosya varsa iyi çalışır .
Pavel Vlasov

6
"app = Flask (....)" satırının da bir parametre olması için "static_folder" gerekiyor
datdinhquoc

Saatlerce bu konuyla uğraşıyorum! Sadece tek bir argümanı kaçırıyordum!
LogicalBranch

78

İhtiyacınız olanı orada bulacağınızdan eminim: http://flask.pocoo.org/docs/quickstart/#static-files

Temel olarak paketinizin kökünde bir "statik" klasöre ihtiyacınız vardır ve daha sonra http://example.com/static/foo.barurl_for('static', filename='foo.bar') ile dosyalarınızı kullanabilir veya doğrudan dosyalarınıza bağlayabilirsiniz .

DÜZENLEME : Yorumlarda önerildiği gibi, doğrudan '/static/foo.bar'URL yolunu kullanabilirsiniz AMA url_for() ek yükü (performans açısından) oldukça düşüktür ve bunu kullanmak, daha sonra davranışı kolayca özelleştirebileceğiniz (klasörü değiştirme, URL yolunu değiştirme, statik dosyalarınızı S3'e vb. taşıyın).


14
Neden '/static/foo.bar'doğrudan değil ?
Tyler Long

3
@TylerLong haklı - statik dizininize önceden kaydedilmiş bir dosyaya bağlanmak istiyorsanız, herhangi bir yol kodu olmadan doğrudan dosyaya bağlayabilirsiniz.
hamx0r

42

Bu işlevi kullanabilirsiniz:

send_static_file(filename)
Statik dosyaları statik klasörden tarayıcıya göndermek için dahili olarak kullanılan işlev.

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

1
Büyük bir baş ağrısı olmadan benim için çalışan tek kişi buydu.
Kenny Powers

Aynı. Flash'ın büyük ölçüde HTML'nin başka yerlerde üretildiği bazı RIA'lara değil, şablon sistemlerini kullanacağımız fikrine büyük ölçüde güveniyor.
NiKo

15
UYARI: Bu, send_static_filekullanıcı girişiyle aramak büyük bir güvenlik sorunudur . Bu çözümü önemli hiçbir şeyde kullanmayın.
xApple

41

Ne kullanıyorum (ve harika çalışıyor) bir "şablonlar" dizini ve bir "statik" dizin. Tüm .html dosyalarımı / Flask şablonlarını şablonlar dizinine yerleştiriyorum ve statik CSS / JS içeriyor. render_template, Flask'in şablon sözdizimini ne ölçüde kullandığınıza bakılmaksızın, genel html dosyaları için bilgime göre iyi çalışır. Aşağıda benim views.py dosyamda örnek bir çağrı var.

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

Ayrı statik dizindeki bazı statik dosyalara başvurmak istediğinizde url_for () kullandığınızdan emin olun. Muhtemelen bunu html'deki CSS / JS dosya bağlantılarınızda yine de yapacaksınız. Örneğin...

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

İşte "kanonik" gayri resmi Flask öğretici bir bağlantı - burada yere isabet yardımcı olmak için harika ipuçları bir sürü.

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


38

Diğer cevaplara dayanan en basit çalışma örneği şudur:

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)

İndex.html adlı HTML ile :

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

ÖNEMLİ: Ve index.html bir klasör denilen olduğu statik , anlam <projectpath>vardır .pydosyası ve <projectpath>\staticsahip htmldosyayı.

Sunucunun ağda görünür olmasını istiyorsanız, app.run(debug=True, host='0.0.0.0')

EDIT: İstenirse klasördeki tüm dosyaları göstermek için bunu kullanın

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

Bu aslında BlackMambacevaptır, bu yüzden onlara bir oy verin.


Önemli gözlem için teşekkürler!
Gleidson Cardoso da Silva 22'18

13

Sonraki klasörler ağacını oluşturan açısal + demirbaş akışı için:

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

Aşağıdaki çözümü kullanıyorum:

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

'Statik' klasörü özel olarak yeniden tanımlamaya yardımcı olur.


Cevabınıza göre bunu yaptım: stackoverflow.com/a/29521067/303114 uyarı temelde aynı olan 'add_url_rule' intead 'yol' kullandığımı fark
danfromisrael

7

Bu yüzden işlerim var (@ user1671599 cevabına dayanarak) ve sizlerle paylaşmak istedim.

(Umarım Python'daki ilk uygulamam olduğu için doğru yapıyorum)

Bunu ben yaptım -

Proje yapısı:

resim açıklamasını buraya girin

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)

daha iyi anlamak için bu cevabı okuyabilirsiniz: stackoverflow.com/a/23501776/303114 (
github'daki

6

Bunu yapmanın basit yollarından biri. Şerefe!

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)

Şimdi şablon adı verilen klasör adı oluşturun . İndex.html dosyanızı şablonlar klasörüne ekleyin

index.html

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

Proje Yapısı

-demo.py
-templates/index.html

soruyu okumadınız. Ben açıkça render_templateçözümün farkında olduğunu söyledi ama dosya hiçbir değişiklik ile statik çünkü bunu yapmak istemiyordu: "Evet, render_template kullanabilirsiniz ama veri templatized biliyorum."
hughdbrown

Windows'da kolayca çalışan tek çözüm, teşekkürler!
Basj

4

Bu örneği paylaşmayı düşündüm.

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

Bu daha iyi ve basit çalışır.


Bunun nasıl daha iyi çalışacağını biraz açıklayabilir misiniz?
arsho

1
lmao diğer her yöntem bana bazı can sıkıcı dosya bulunamadı hataları verdi. nice1 jeevan
Dmitri DB

3

Kullan redirectveurl_for

from flask import redirect, url_for

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

Bu sunucular html'nizde başvurulan tüm dosyaları (css & js ...).


2

En basit yol, ana proje klasörü içinde statik bir klasör oluşturmaktır. .Css dosyalarını içeren statik klasör.

ana klasör

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

Statik ve şablonlar klasörleri ve balon komut dosyası içeren ana klasörün görüntüsü

şişe

from flask import Flask, render_template

app = Flask(__name__)

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

html (düzen)

<!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")

Kök dizininizde şablonlar varsa, bunu içeren dosya da aynı konumda ise app = Flask ( name ) yerleştirilirse, bu dosya başka bir konumdaysa, etkinleştirilecek şablon konumunu belirtmeniz gerekir Konumu işaret edecek bir şişe


4
Bunun neden işe yaradığına dair biraz açıklama yapabilir misiniz?
ekonomi

1
Açıklama sağlayan bu cevaptan farkı nedir?
Gino Mempin

Cevabım için bir açıklama önerdim @economy
Novak254

1

Tüm cevaplar iyi ama benim için iyi çalışan şey sadece send_fileFlask'ın basit işlevini kullanmak . Ana bilgisayar: port / ApiName dosyanın çıktısını tarayıcıda gösterdiğinde yanıt olarak bir html dosyası göndermeniz gerektiğinde bu iyi çalışır


@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

   Varsayılan olarak, balon tüm şablon dosyalarınızı (herhangi bir düz metin dosyası, ancak genellikle .htmlveya jinja2 gibi bir şablon şablonu dili) içermek için bir "şablonlar" klasörü ve tüm statik dosyalarınızı (yani .js .cssve resimleriniz).
   Gözlerinde farklı routesu kullanabilirsiniz render_template()(ben yerleştirilir varsayılan olarak, yukarıda dediğimiz gibi bir şablon dosyasını işlemek için templatesisteğiniz için yanıt olarak klasöre). Ve şablon dosyasında (genellikle .html benzeri bir dosyadır), bazı .jsve / veya `` .css '' dosyalarını kullanabilirsiniz, bu yüzden sorunuzun bu statik dosyaları geçerli şablon dosyasına nasıl bağladığınızı tahmin ediyorum.


0

Sadece bir dosya açmaya çalışıyorsanız, kullanabilirsiniz app.open_resource(). Yani bir dosyayı okumak şöyle bir şey olurdu

with app.open_resource('/static/path/yourfile'):
      #code to read the file and do something
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.