Cara menyajikan file statis di Flask

539

Jadi ini memalukan. Saya punya aplikasi yang saya lempar bersama Flaskdan untuk saat ini hanya melayani satu halaman HTML statis dengan beberapa tautan ke CSS dan JS. Dan saya tidak dapat menemukan di mana dalam dokumentasi Flaskmenjelaskan pengembalian file statis. Ya, saya bisa menggunakan render_templatetetapi saya tahu datanya tidak templatized. Saya telah memikirkan send_fileatau url_forhal yang benar, tetapi saya tidak bisa membuatnya bekerja. Sementara itu, saya membuka file, membaca konten, dan memasang tipe Responsemimet yang sesuai:

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)

Seseorang ingin memberikan contoh kode atau url untuk ini? Saya tahu ini akan sangat sederhana.

hughdbrown
sumber
6
Mengapa tidak menggunakan nginx atau server web lain untuk menyajikan file statis.
atupal
8
Harap diingat bahwa bagaimana Anda sebenarnya "melayani" file-file tersebut mungkin akan berbeda antara produksi (di server web Anda) dan pengembangan (pada komputer lokal Anda, atau beberapa area pengujian lainnya). Seperti yang telah ditunjukkan beberapa jawaban, Anda mungkin TIDAK ingin melayani file statis Anda dengan termos, tetapi memilikinya di direktori mereka sendiri dan kemudian memiliki server web Anda yang sebenarnya (Apache, nginx, dll.) Server file-file tersebut secara langsung.
Mark Hildreth
75
"Mengapa tidak menggunakan nginx…" Karena ketika saya menjalankannya dalam mode pengembang di laptop saya, senang hanya perlu menjalankan satu hal, dan satu hal saja. Ya, itu membuat beberapa hal sedikit berbeda, tapi tidak apa-apa.
Thanatos
1
Bahkan dalam produksi, sangat umum untuk melihat ini, dengan tentu saja lapisan cache di depan (seperti Varnish atau Nginx atau CDN).
Thomas Decaux

Jawaban:

644

Metode yang disukai adalah menggunakan nginx atau server web lain untuk menyajikan file statis; mereka akan dapat melakukannya dengan lebih efisien daripada Flask.

Namun, Anda dapat menggunakan send_from_directoryuntuk mengirim file dari direktori, yang bisa sangat nyaman dalam beberapa situasi:

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

Jangan tidak menggunakan send_fileatau send_static_filedengan jalur yang disediakan pengguna.

send_static_file contoh:

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
sumber
12
untuk mendukung Windows: return app.send_static_file (os.path.join ('js', path) .replace ('\\', '/'))
Tony BenBrahim
9
dapatkah penyerang mengeksploitasi metode ini untuk meramban file sumber flask dengan meramban jenis / js / <beberapa penyandian cerdas "../ yourflaskapp.py">?
akiva
30
@kiwi send_from_directorydirancang untuk mengatasi masalah keamanan itu. Itu ada untuk kesalahan jika jalan mengarah ke luar direktori tertentu.
jpmc26
10
"Jangan gunakan send_file atau send_static_file dengan jalur yang disediakan pengguna." kenapa tidak?
Drew Verlee
6
@DenisV itu tidak ada hubungannya dengan Python per-se, itu adalah Konvensi Flask untuk mendefinisikan parameter URL (lihat http://flask.pocoo.org/docs/0.12/api/#url-route-registrations ). Singkatnya <path>setara dengan <string:path>, dan karena Anda ingin Flask memastikan parameter seperti jalur yang Anda minta <path:path>.
b4stien
136

Jika Anda hanya ingin memindahkan lokasi file statis Anda, maka metode paling sederhana adalah dengan mendeklarasikan path di konstruktor. Pada contoh di bawah, saya telah pindah template saya dan file statis menjadi sub-folder bernama web.

app = Flask(__name__,
            static_url_path='', 
            static_folder='web/static',
            template_folder='web/templates')
  • static_url_path=''menghapus semua jalur sebelumnya dari URL (yaitu default /static).
  • static_folder='web/static'untuk melayani file yang ditemukan dalam folder web/staticsebagai file statis.
  • template_folder='web/templates' sama halnya, ini mengubah folder templat.

Dengan menggunakan metode ini, URL berikut akan mengembalikan file CSS:

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

Dan akhirnya, inilah snap dari struktur folder, di mana flask_server.pyinstance Flask:

Folder Flask Statis Bersarang

Richard Dunn
sumber
9
Inilah yang bekerja untuk saya juga. Send_from_directory tidak berfungsi meskipun semua rekomendasi untuk itu.
GA
Bekerja dengan sempurna. Terima kasih banyak <3.
Thuat Nguyen
Seperti apa jalan itu? get_static_file('index.html')?
Batman
ini bekerja dengan baik, seperti yang dikatakan GA, tidak ada yang bekerja untuk saya dan ini memperbaiki semuanya. Sangat dihargai
Andrey Starenky
81

Anda juga dapat, dan ini adalah favorit saya, mengatur folder sebagai jalur statis sehingga file di dalamnya dapat dijangkau oleh semua orang.

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

Dengan set itu Anda dapat menggunakan HTML standar:

<link rel="stylesheet" type="text/css" href="/static/style.css">
rumput tajam
sumber
4
Bekerja dengan baik jika ada file yang project/static/style.csstersedia.
Pavel Vlasov
6
baris "app = Flask (....)" membutuhkan "static_folder" untuk menjadi parameter juga
datdinhquoc
Saya telah berjuang dengan masalah ini selama berjam-jam! Saya hanya melewatkan satu argumen!
LogicalBranch
78

Saya yakin Anda akan menemukan yang Anda butuhkan di sana: http://flask.pocoo.org/docs/quickstart/#static-files

Pada dasarnya Anda hanya perlu folder "statis" di root paket Anda, dan kemudian Anda dapat menggunakan url_for('static', filename='foo.bar')atau langsung menautkan ke file Anda dengan http://example.com/static/foo.bar .

EDIT : Seperti yang disarankan dalam komentar Anda dapat langsung menggunakan '/static/foo.bar'jalur URL TETAPI url_for() overhead (kinerja bijaksana) cukup rendah, dan menggunakannya berarti Anda akan dapat dengan mudah menyesuaikan perilaku setelahnya (mengubah folder, mengubah jalur URL, pindahkan file statis Anda ke S3, dll).

b4stien
sumber
14
Kenapa tidak '/static/foo.bar'langsung?
Tyler Long
3
@TylerLong benar - jika Anda ingin menautkan ke file yang sudah disimpan di direktori statis Anda, Anda dapat menautkan langsung ke sana tanpa kode rute.
hamx0r
42

Anda dapat menggunakan fungsi ini:

send_static_file(filename)
Fungsi yang digunakan secara internal untuk mengirim file statis dari folder statis ke browser.

app = Flask(__name__)
@app.route('/<path:path>')
def static_file(path):
    return app.send_static_file(path)
Mamba hitam
sumber
1
Ini adalah satu-satunya yang bekerja untuk saya tanpa sakit kepala yang besar.
Kenny Powers
Sama. Di mana saya menyadari Flash sangat bergantung pada gagasan bahwa kita akan menggunakan sistem template mereka, bukan RIA di mana HTML diproduksi di tempat lain.
NiKo
15
PERINGATAN: Ini adalah masalah keamanan yang sangat besar untuk dipanggil send_static_filedengan input pengguna. Jangan gunakan solusi ini dalam hal yang penting.
xApple
41

Apa yang saya gunakan (dan sudah berfungsi dengan baik) adalah direktori "templat" dan direktori "statis". Saya menempatkan semua file .html / templat termos saya di dalam direktori templat, dan statis berisi CSS / JS. render_template berfungsi dengan baik untuk file html generik sepengetahuan saya, terlepas dari sejauh mana Anda menggunakan sintaks templating Flask. Di bawah ini adalah contoh panggilan dalam file views.py saya.

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

Pastikan Anda menggunakan url_for () ketika Anda ingin mereferensikan beberapa file statis di direktori statis terpisah. Anda mungkin akhirnya akan melakukan ini di tautan file CSS / JS dalam html. Contohnya...

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

Berikut ini tautan ke tutorial Labu informal "kanonik" - banyak kiat bagus di sini untuk membantu Anda memulai.

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

Kyle Sum
sumber
38

Contoh kerja paling sederhana berdasarkan jawaban lain adalah sebagai berikut:

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)

Dengan HTML disebut index.html :

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

PENTING: Dan index.html ada dalam folder bernama statis , artinya <projectpath>punya .pyfile, dan <projectpath>\staticpunya htmlfile.

Jika Anda ingin server terlihat di jaringan, gunakan app.run(debug=True, host='0.0.0.0')

EDIT: Untuk menampilkan semua file dalam folder jika diminta, gunakan ini

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

Yang pada dasarnya adalah BlackMambajawaban, jadi beri mereka suara positif.

EpicPandaForce
sumber
Terima kasih atas pengamatan penting!
Gleidson Cardoso da Silva
13

Untuk aliran sudut + boilerplate yang membuat pohon folder berikutnya:

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

Saya menggunakan solusi berikut:

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

Ini membantu untuk mendefinisikan kembali folder 'statis' untuk custom.

pengguna1671599
sumber
berdasarkan jawaban Anda saya melakukan ini: stackoverflow.com/a/29521067/303114 pemberitahuan saya menggunakan 'add_url_rule' intead 'route' yang pada dasarnya sama
danfromisrael
7

Jadi saya dapat pekerjaan (berdasarkan jawaban @ user1671599) dan ingin membaginya dengan kalian.

(Saya harap saya melakukannya dengan benar karena ini adalah aplikasi pertama saya di Python)

Saya melakukan ini -

Struktur proyek:

masukkan deskripsi gambar di sini

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
sumber
untuk pemahaman yang lebih baik Anda dapat membaca jawaban ini: stackoverflow.com/a/23501776/303114 (yang mengarahkan Anda ke sumber di github)
danfromisrael
6

Salah satu cara sederhana untuk dilakukan. Bersulang!

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)

Sekarang buat nama folder yang disebut templates . Tambahkan file index.html Anda di dalam folder template

index.html

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

Struktur Proyek

-demo.py
-templates/index.html
Virus mahesh
sumber
Anda tidak membaca pertanyaannya. Saya dengan tegas mengatakan bahwa saya mengetahui render_templatesolusinya tetapi tidak ingin melakukannya karena file itu statis tanpa penggantian: "Ya, saya bisa menggunakan render_template tetapi saya tahu datanya tidak templatized."
hughdbrown
Satu-satunya solusi yang bekerja dengan mudah di Windows, terima kasih!
Basj
4

Berpikir untuk berbagi .... contoh ini.

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

Ini berfungsi lebih baik dan sederhana.

Jeevan Chaitanya
sumber
Bisakah Anda jelaskan bagaimana ini akan bekerja lebih baik?
arsho
1
lmao setiap metode memberi saya beberapa file yang mengganggu tidak ditemukan kesalahan. nice1 jeevan
Dmitri DB
3

Gunakan redirectdanurl_for

from flask import redirect, url_for

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

Server ini semua file (css & js ...) dirujuk dalam html Anda.

forzagreen
sumber
2

Cara paling sederhana adalah membuat folder statis di dalam folder proyek utama. Folder statis yang berisi file .css.

folder utama

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

Gambar folder utama yang berisi folder templat dan statis serta skrip flask

labu

from flask import Flask, render_template

app = Flask(__name__)

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

html (tata letak)

<!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 %}
ESBOS
sumber
2
app = Flask(__name__, static_folder="your path to static")

Jika Anda memiliki template di direktori root Anda, menempatkan aplikasi = Labu ( nama ) akan berfungsi jika file yang berisi ini juga berada di lokasi yang sama, jika file ini di lokasi lain, Anda harus menentukan lokasi template untuk mengaktifkan Labu untuk menunjuk ke lokasi

Novak254
sumber
4
Bisakah Anda memberikan penjelasan mengapa ini berhasil?
ekonomi
1
Apa bedanya dengan jawaban ini yang memberikan penjelasan?
Gino Mempin
Saya telah menawarkan penjelasan untuk jawaban saya @ekonomi
Novak254
1

Semua jawaban baik tetapi apa yang bekerja dengan baik bagi saya hanya menggunakan fungsi sederhana send_filedari Flask. Ini berfungsi dengan baik ketika Anda hanya perlu mengirim file html sebagai respons ketika host: port / ApiName akan menampilkan output file di 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])```
Binoy S Kumar
sumber
0

   Secara default, labu menggunakan folder "templat" untuk memuat semua file templat Anda (file teks biasa, tetapi biasanya .htmlatau semacam bahasa templat seperti jinja2) & folder "statis" untuk memuat semua file statis Anda (mis. .js .cssDan gambar Anda).
   Di Anda routes, Anda dapat menggunakan render_template()untuk merender file template (seperti yang saya katakan di atas, secara default ditempatkan di templatesfolder) sebagai respons untuk permintaan Anda. Dan dalam file template (biasanya file .html-like), Anda dapat menggunakan beberapa .jsdan / atau file .css, jadi saya kira pertanyaan Anda adalah bagaimana Anda menautkan file-file statis ini ke file template saat ini.

Harvey
sumber
0

Jika Anda hanya mencoba membuka file, Anda bisa menggunakannya app.open_resource(). Jadi membaca file akan terlihat seperti itu

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