Cách phục vụ các tệp tĩnh trong Flask


539

Vì vậy, điều này là xấu hổ. Tôi đã có một ứng dụng mà tôi đã sử dụng cùng nhau Flaskvà hiện tại nó chỉ đang phục vụ một trang HTML tĩnh duy nhất với một số liên kết đến CSS và JS. Và tôi không thể tìm thấy nơi trong tài liệu Flaskmô tả trả về các tệp tĩnh. Có, tôi có thể sử dụng render_templatenhưng tôi biết dữ liệu không phải là templatized. Tôi đã nghĩ send_filehoặc url_forlà điều đúng đắn, nhưng tôi không thể làm cho chúng hoạt động được. Trong thời gian chờ đợi, tôi đang mở các tệp, đọc nội dung và xử lý một mô hình Responsephù hợp:

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)

Ai đó muốn đưa ra một mẫu mã hoặc url cho việc này? Tôi biết điều này sẽ trở nên đơn giản.


6
Tại sao không sử dụng nginx hoặc các máy chủ web khác để phục vụ tệp tĩnh.
atupal

8
Xin lưu ý rằng cách bạn thực sự "phục vụ" các tệp có thể sẽ khác nhau giữa sản xuất (trên máy chủ web của bạn) và phát triển (trên máy tính cục bộ của bạn hoặc một số khu vực kiểm tra khác). Như một số câu trả lời đã chỉ ra, có lẽ bạn sẽ KHÔNG muốn phục vụ các tệp tĩnh của mình bằng bình, mà thay vào đó hãy để chúng trong thư mục riêng của chúng và sau đó có máy chủ web thực sự của bạn (Apache, nginx, v.v.) trực tiếp lưu trữ các tệp đó.
Mark Hildreth


75
"Tại sao không sử dụng nginx," Bởi vì khi tôi chạy nó ở chế độ nhà phát triển trên máy tính xách tay của mình, thật tuyệt khi chỉ cần chạy một thứ, và một thứ duy nhất. Vâng, nó làm cho mọi thứ trở nên khác biệt, nhưng không sao.
Thanatos

1
Ngay cả trong sản xuất, rất phổ biến để thấy điều này, tất nhiên có một lớp bộ đệm ở phía trước (chẳng hạn như Varnish hoặc Nginx hoặc CDN).
Thomas Decaux

Câu trả lời:


643

Phương pháp ưa thích là sử dụng nginx hoặc một máy chủ web khác để phục vụ các tệp tĩnh; họ sẽ có thể làm điều đó hiệu quả hơn Flask.

Tuy nhiên, bạn có thể sử dụng send_from_directoryđể gửi tệp từ một thư mục, điều này có thể khá thuận tiện trong một số trường hợp:

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

Đừng không sử dụng send_filehoặc send_static_filevới một con đường người dùng cung cấp.

send_static_file thí dụ:

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
để hỗ trợ Windows: return app.send_static_file (os.path.join ('js', path) .replace ('\\', '/'))
Tony BenBrahim

9
Kẻ tấn công có thể khai thác phương pháp này để duyệt các tệp nguồn bình bằng cách duyệt loại / js / <một số mã hóa thông minh của "../ yourflaskapp.py">?
akiva

30
@kiwi send_from_directoryđược thiết kế để giải quyết vấn đề bảo mật đó. Nó tồn tại để lỗi ra nếu đường dẫn dẫn đến bên ngoài thư mục cụ thể.
jpmc26

10
"Không sử dụng send_file hoặc send_static_file với đường dẫn do người dùng cung cấp." tại sao không?
Drew Verlee

6
@DenisV không liên quan gì đến Python per-se, đó là quy ước Flask để xác định tham số URL (xem http://flask.pocoo.org/docs/0.12/api/#url-route-registations ). Tóm lại, <path>tương đương với <string:path>và vì bạn muốn Flask đảm bảo tham số giống như đường dẫn mà bạn yêu cầu <path:path>.
b4stien

135

Nếu bạn chỉ muốn di chuyển vị trí của các tệp tĩnh, thì phương pháp đơn giản nhất là khai báo các đường dẫn trong hàm tạo. Trong ví dụ dưới đây, tôi đã chuyển các mẫu và tệp tĩnh của mình vào một thư mục con có tên web.

app = Flask(__name__,
            static_url_path='', 
            static_folder='web/static',
            template_folder='web/templates')
  • static_url_path=''xóa mọi đường dẫn trước khỏi URL (tức là mặc định /static).
  • static_folder='web/static'để phục vụ bất kỳ tệp nào được tìm thấy trong thư mục web/staticdưới dạng tệp tĩnh.
  • template_folder='web/templates' tương tự, điều này thay đổi thư mục mẫu.

Sử dụng phương pháp này, URL sau sẽ trả về tệp CSS:

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

Và cuối cùng, đây là một snap của cấu trúc thư mục, flask_server.pyví dụ Flask:

Các thư mục bình tĩnh lồng nhau


9
Đây là những gì làm việc cho tôi là tốt. Send_from_directory chỉ không hoạt động mặc dù tất cả các khuyến nghị cho nó.
GA

Hoạt động hoàn hảo. Cảm ơn bạn rất nhiều <3.
Thuat Nguyễn

Con đường trông như thế nào? get_static_file('index.html')?
dơi

Điều này hoạt động tốt, như GA nói, không có gì khác làm việc cho tôi và điều này đã khắc phục tất cả. Được đánh giá cao
Andrey Starenky

81

Bạn cũng có thể, và đây là mục ưa thích của tôi, đặt thư mục là đường dẫn tĩnh để các tệp bên trong có thể truy cập được cho mọi người.

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

Với bộ đó, bạn có thể sử dụng HTML tiêu chuẩn:

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

4
Hoạt động tốt nếu có project/static/style.csssẵn tập tin .
Pavel Vlasov

6
dòng "app = Flask (....)" cần "static_folder" để trở thành một tham số
datdinhquoc

Tôi đã vật lộn với vấn đề này trong nhiều giờ! Tôi chỉ thiếu một đối số duy nhất!
LogicalBranch

78

Tôi chắc chắn bạn sẽ tìm thấy những gì bạn cần ở đó: http://flask.pocoo.org/docs/quickstart/#static-files

Về cơ bản, bạn chỉ cần một thư mục "tĩnh" ở thư mục gốc của gói và sau đó bạn có thể sử dụng url_for('static', filename='foo.bar')hoặc liên kết trực tiếp đến các tệp của mình bằng http://example.com/static/foo.bar .

EDIT : Như được đề xuất trong các nhận xét, bạn có thể trực tiếp sử dụng '/static/foo.bar'đường dẫn URL NHƯNG url_for() chi phí (hiệu suất khôn ngoan) khá thấp và sử dụng nó có nghĩa là bạn sẽ có thể dễ dàng tùy chỉnh hành vi sau đó (thay đổi thư mục, thay đổi đường dẫn URL, di chuyển các tệp tĩnh của bạn sang S3, v.v.).


14
Tại sao không '/static/foo.bar'trực tiếp?
Tyler Long

3
@TylerLong là đúng - nếu bạn muốn liên kết đến một tệp đã được lưu trong thư mục tĩnh của mình, bạn có thể liên kết trực tiếp đến nó mà không cần bất kỳ mã tuyến đường nào.
hamx0r

42

Bạn có thể sử dụng chức năng này:

send_static_file(filename)
Chức năng được sử dụng nội bộ để gửi các tệp tĩnh từ thư mục tĩnh đến trình duyệt.

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

1
Đây là người duy nhất làm việc cho tôi mà không phải đau đầu.
Kenny Powers

Tương tự. Nơi tôi nhận ra Flash phụ thuộc rất nhiều vào ý tưởng rằng chúng tôi sẽ sử dụng hệ thống mẫu của họ chứ không phải RIA nơi HTML được sản xuất ở nơi khác.
NiKo

15
CẢNH BÁO: Đây là một mối quan tâm bảo mật rất lớn để gọi send_static_filevới đầu vào của người dùng. Đừng sử dụng giải pháp này trong bất cứ điều gì quan trọng.
xApple

41

Những gì tôi sử dụng (và nó đã hoạt động rất tốt) là một thư mục "mẫu" và thư mục "tĩnh". Tôi đặt tất cả các tệp .html / mẫu Flask của mình trong thư mục mẫu và tĩnh chứa CSS / JS. render_template hoạt động tốt đối với các tệp html chung theo hiểu biết của tôi, bất kể mức độ bạn đã sử dụng cú pháp tạo khuôn mẫu của Flask. Dưới đây là một cuộc gọi mẫu trong tập tin lượt xem của tôi.

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

Chỉ cần đảm bảo bạn sử dụng url_for () khi bạn muốn tham chiếu một số tệp tĩnh trong thư mục tĩnh riêng biệt. Cuối cùng, bạn có thể sẽ thực hiện việc này trong các liên kết tệp CSS / JS của mình trong html. Ví dụ...

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

Đây là một liên kết đến hướng dẫn Flask không chính thức "kinh điển" - rất nhiều lời khuyên tuyệt vời ở đây để giúp bạn tiếp tục chạy.

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


38

Một ví dụ hoạt động đơn giản nhất dựa trên các câu trả lời khác là như sau:

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)

Với HTML được gọi là index.html :

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

QUAN TRỌNG:index.html nằm trong một thư mục có tên là static , nghĩa là <projectpath>.pytệp và <projectpath>\statichtmltệp.

Nếu bạn muốn máy chủ hiển thị trên mạng, hãy sử dụng app.run(debug=True, host='0.0.0.0')

EDIT: Để hiển thị tất cả các tệp trong thư mục nếu được yêu cầu, hãy sử dụng

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

Đó thực chất BlackMambalà câu trả lời, vì vậy hãy cung cấp cho họ một upvote.


Cảm ơn sự quan sát quan trọng!
Gleidson Cardoso da Silva

13

Đối với luồng góc + nồi hơi tạo cây thư mục tiếp theo:

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

Tôi sử dụng giải pháp sau đây:

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

Nó giúp xác định lại thư mục 'tĩnh' để tùy chỉnh.


dựa trên câu trả lời của bạn, tôi đã làm điều này: stackoverflow.com/a/29521067/303114 thông báo tôi đã sử dụng 'add_url_rule' intead 'tuyến', về cơ bản là giống nhau
danfromisrael

7

Vì vậy, tôi đã làm mọi thứ hoạt động (dựa trên câu trả lời @ user1671599) và muốn chia sẻ nó với các bạn.

(Tôi hy vọng tôi đang làm đúng vì đây là ứng dụng đầu tiên của tôi trong Python)

Tôi đã làm điều này -

Cơ cấu dự án:

nhập mô tả hình ảnh ở đây

máy chủ:

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)

để hiểu rõ hơn bạn có thể đọc câu trả lời này: stackoverflow.com/a/23501776/303114 (chỉ dẫn bạn đến nguồn trong github)
danfromisrael

6

Một trong những cách đơn giản để làm. Chúc mừng!

bản demo

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)

Bây giờ tạo tên thư mục được gọi là mẫu . Thêm của bạn tệp index.html bên trong thư mục mẫu

index.html

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

Kết cấu dự án

-demo.py
-templates/index.html

bạn đã không đọc câu hỏi. Tôi nói rõ rằng tôi đã biết về render_templategiải pháp nhưng không muốn thực hiện vì tệp này không có thay thế: "Có, tôi có thể sử dụng render_template nhưng tôi biết dữ liệu không được templatized."
hughdbrown

Giải pháp duy nhất hoạt động dễ dàng trong Windows, cảm ơn!
Basj

4

Suy nghĩ chia sẻ .... ví dụ này.

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

Điều này làm việc tốt hơn và đơn giản.


Bạn có thể vui lòng giải thích làm thế nào điều này sẽ làm việc tốt hơn?
arsho

1
lmao mọi phương pháp khác cho tôi một số tập tin gây phiền nhiễu không tìm thấy lỗi. Nice1 jeevan
Dmitri DB

3

Sử dụng redirecturl_for

from flask import redirect, url_for

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

Máy chủ này tất cả các tệp (css & js ...) được tham chiếu trong html của bạn.


2

Cách đơn giản nhất là tạo một thư mục tĩnh bên trong thư mục dự án chính. Thư mục tĩnh chứa tệp .css.

thư mục chính

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

Hình ảnh của thư mục chính chứa các thư mục tĩnh và mẫu và tập lệnh jar

bình giữ nhiệt

from flask import Flask, render_template

app = Flask(__name__)

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

html (bố cục)

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

Nếu bạn có các mẫu trong thư mục gốc của mình, việc đặt ứng dụng = Flask ( tên ) sẽ hoạt động nếu tệp chứa tệp này cũng ở cùng một vị trí, nếu tệp này ở vị trí khác, bạn sẽ phải chỉ định vị trí mẫu để bật Bình để chỉ vào vị trí


4
Bạn có thể cung cấp một số lời giải thích cho lý do tại sao điều này làm việc?
nền kinh tế

1
Làm thế nào nó khác với câu trả lời này cung cấp giải thích?
Gino Mempin

Tôi đã đưa ra một lời giải thích cho câu trả lời của mình @economy
Novak254

1

Tất cả các câu trả lời đều tốt nhưng điều làm việc tốt với tôi chỉ là sử dụng chức năng đơn giản send_filetừ Flask. Điều này hoạt động tốt khi bạn chỉ cần gửi tệp html dưới dạng phản hồi khi máy chủ: port / ApiName sẽ hiển thị đầu ra của tệp trong trình duyệt


@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

   Theo mặc định, jar sử dụng thư mục "mẫu" để chứa tất cả các tệp mẫu của bạn (bất kỳ tệp văn bản thuần túy nào, nhưng thông thường .htmlhoặc một số loại ngôn ngữ mẫu như jinja2) & thư mục "tĩnh" để chứa tất cả các tệp tĩnh của bạn (nghĩa là .js .cssvà hình ảnh của bạn).
   Trong của bạn routes, bạn có thể sử dụng render_template()để hiển thị một tệp mẫu (như tôi nói ở trên, theo mặc định, nó được đặt trong templatesthư mục) làm phản hồi cho yêu cầu của bạn. Và trong tệp mẫu (thường là tệp giống .html), bạn có thể sử dụng một số tệp .jsvà / hoặc `.css ', vì vậy tôi đoán câu hỏi của bạn là cách bạn liên kết các tệp tĩnh này với tệp mẫu hiện tại.


0

Nếu bạn chỉ đang cố mở một tệp, bạn có thể sử dụng app.open_resource(). Vì vậy, đọc một tập tin sẽ trông giống như

with app.open_resource('/static/path/yourfile'):
      #code to read the file and do something
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.