如何在Flask中提供静态文件


539

所以这很尴尬。我有一个集成在一起的应用程序,Flask现在它只提供一个静态HTML页面,其中包含指向CSS和JS的链接。而且我找不到文档中Flask描述返回静态文件的位置。是的,我可以使用,render_template但是我知道数据没有模板化。我还以为send_file或者url_for是正确的事情,但我不能让这些工作。同时,我正在打开文件,阅读内容,并装配Response具有适当mimetype的:

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)

有人要为此提供代码示例或网址吗?我知道这将变得简单。


6
为什么不使用Nginx或其他Web服务器来提供静态文件。
2013年

8
请记住,在生产(在Web服务器上)和开发(在本地计算机或其他测试区域)之间,实际“服务”文件的方式可能有所不同。正如一些答案所指出的那样,您可能不希望使用flask提供静态文件,而是将它们放在自己的目录中,然后让实际的Web服务器(Apache,nginx等)直接将这些文件服务器。
Mark Hildreth


75
“为什么不使用nginx…”因为当我在笔记本电脑上以开发人员模式运行它时,只需要运行一件事,而只运行一件事是很好的。是的,这使事情有所不同,但这没关系。
塔纳托斯

1
即使在生产中,看到这一点也是非常普遍的,当然前面还有一个缓存层(例如Varnish或Nginx或CDN)。
Thomas Decaux

Answers:


642

首选方法是使用nginx或其他Web服务器提供静态文件。他们将比Flask更有效率。

但是,您可以使用send_from_directory从目录发送文件,这在某些情况下非常方便:

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

千万不能使用send_filesend_static_file与用户提供的路径。

send_static_file 例:

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:return app.send_static_file(os.path.join('js',path).replace('\\','/'))
Tony BenBrahim 2014年

9
攻击者能否利用这种方法通过浏览/ js / <../ yourflaskapp.py“>的某种巧妙编码来浏览Flask源文件?
akiva

30
@kiwi send_from_directory旨在解决该安全问题。如果路径通向特定目录之外,则存在错误。
jpmc26 2014年

10
“请勿将send_file或send_static_file与用户提供的路径一起使用。” 为什么不?
德鲁·弗里

6
@DenisV与Python本身无关,它是用于定义URL参数的Flask约定(请参阅http://flask.pocoo.org/docs/0.12/api/#url-route-registrations)。简而言之,<path>它等效于<string:path>,并且因为您希望Flask确保所要求的类似路径的参数<path:path>
b4stien

135

如果只想移动静态文件的位置,则最简单的方法是在构造函数中声明路径。在下面的示例中,我将模板和静态文件移动到了名为的子文件夹中web

app = Flask(__name__,
            static_url_path='', 
            static_folder='web/static',
            template_folder='web/templates')
  • static_url_path=''从URL中删除任何前面的路径(即默认值/static)。
  • static_folder='web/static'将在文件夹中找到的所有文件 web/static作为静态文件提供。
  • template_folder='web/templates' 同样,这会更改模板文件夹。

使用此方法,以下URL将返回CSS文件:

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

最后,这是文件夹结构的快照,flask_server.pyFlask实例在哪里:

嵌套的静态烧瓶文件夹


9
这对我也起作用。尽管有所有建议,Send_from_directory仍然无法使用。
GA

完美的作品。非常感谢<3。
Thuat Nguyen

路径是什么样的?get_static_file('index.html')
蝙蝠侠

就像GA所说的那样,此方法效果很好,对我来说没有其他方法可以解决所有问题。非常感谢
Andrey Starenky

81

您也可以(这是我的最爱)将文件夹设置为静态路径,以便每个人都可以访问其中的文件。

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

通过该设置,您可以使用标准HTML:

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

4
如果有project/static/style.css可用文件,则效果很好。
帕维尔·弗拉索夫

6
“ app = Flask(....)”行也需要“ static_folder”作为参数
daddinhquoc

我已经为这个问题苦苦挣扎了好几个小时!我只想念一个论点!
LogicalBranch

78

我确定您会在这里找到所需的资源:http : //flask.pocoo.org/docs/quickstart/#static-files

基本上,您只需要在软件包根目录中添加一个“静态”文件夹,然后就可以url_for('static', filename='foo.bar')通过http://example.com/static/foo.bar使用或直接链接到您的文件。

编辑:如评论中所建议,您可以直接使用'/static/foo.bar'URL路径, url_for()开销(在性能方面)非常低,并且使用它意味着您以后可以轻松自定义行为(更改文件夹,更改URL路径,将您的静态文件移至S3等)。


14
为什么不'/static/foo.bar'直接呢?
泰勒·朗

3
@TylerLong是正确的-如果要链接到已保存在静态目录中的文件,则可以直接链接到该文件,而无需任何路由代码。
hamx0r 2014年

42

您可以使用此功能:

send_static_file(filename)
内部使用的功能,用于将静态文件从静态文件夹发送到浏览器。

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

1
这是唯一为我工作而不会头痛的方法。
肯尼·鲍尔斯

相同。我意识到Flash很大程度上取决于我们将使用其模板系统的想法,而不是一些在其他地方生成HTML的RIA。
NiKo

15
警告:send_static_file使用用户输入进行调用时,这是一个很大的安全问题。不要在任何重要的事情上使用此解决方案。
xApple

41

我使用的(并且运行良好)是“模板”目录和“静态”目录。我将所有.html文件/ Flask模板放在模板目录中,而static包含CSS / JS。据我所知,无论您使用Flask的模板语法的程度如何,render_template都能很好地适用于通用html文件。以下是我的views.py文件中的示例调用。

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

只要确实要在单独的静态目录中引用某些静态文件时确保使用url_for()即可。无论如何,您可能最终都会在HTML的CSS / JS文件链接中执行此操作。例如...

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

这是“规范”非正式Flask教程的链接-此处有许多很棒的技巧可帮助您快速入门。

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


38

基于其他答案的最简单的工作示例如下:

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)

使用名为index.html的HTML

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

重要提示:而且index.html的是一个文件夹,名为在静态,这意味着<projectpath>.py文件,<projectpath>\statichtml文件。

如果希望服务器在网络上可见,请使用 app.run(debug=True, host='0.0.0.0')

编辑:如果需要显示文件夹中的所有文件,请使用此

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

从本质BlackMamba上讲,这是答案,所以给他们投票。


感谢您的重要观察!
Gleidson Cardoso da Silva,

13

对于创建下一个文件夹树的角度+样板流:

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

我使用以下解决方案:

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

它有助于将“静态”文件夹重新定义为自定义。


根据您的回答,我执行了以下操作:stackoverflow.com/a/29521067/303114 注意,我使用了“ add_url_rule”,“路线”,这基本相同
danfromisrael 2015年

7

因此,我开始工作(基于@ user1671599答案),并希望与大家分享。

(我希望我做对了,因为这是我的第一个Python应用程序)

我做到了-

项目结构:

在此处输入图片说明

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)

为了更好地理解,您可以阅读以下答案:stackoverflow.com/a/23501776/303114 (将您指向github中的源代码)
danfromisrael 2015年

6

一种简单的方法。 干杯!

演示

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)

现在创建名为模板的文件夹名称。将您的index.html文件添加到模板文件夹中

index.html

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

项目结构

-demo.py
-templates/index.html

您没有阅读问题。我明确表示我知道render_template解决方案,但不想这样做,因为该文件是静态的,不能进行替换:“是的,我可以使用render_template,但是我知道数据没有模板化。”
hughdbrown

唯一在Windows中轻松工作的解决方案,谢谢!
巴吉

4

想共享...这个例子。

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

这样效果更好,更简单。


您能详细说明一下如何更好地工作吗?
arsho

1
lmao其他所有方法给了我一些令人讨厌的文件未找到错误。nice1 jeevan
Dmitri DB

3

使用redirecturl_for

from flask import redirect, url_for

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

这将处理您html中引用的所有文件(css和js ...)。


2

最简单的方法是在主项目文件夹中创建一个静态文件夹。包含.css文件的静态文件夹。

主文件夹

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

包含静态和模板文件夹以及flask脚本的主文件夹的图像

烧瓶

from flask import Flask, render_template

app = Flask(__name__)

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

html(布局)

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

如果您的根目录中有模板,则如果包含app_Flask(name)的文件也位于同一位置,则放置app = Flask(name)将起作用,如果该文件位于其他位置,则必须指定模板位置才​​能启用烧瓶指向该位置


4
您能否提供一些解释,说明其为何有效?
经济

1
它与提供解释的答案有何不同?
Gino Mempin

我已经提供了一个解释我的回答@economy
Novak254

1

所有的答案都不错,但是对我来说,行之有效的只是使用send_fileFlask 的简单功能。当host:port / ApiName将在浏览器中显示文件的输出时,当您只需要发送一个HTML文件作为响应时,此方法很好用


@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

   默认情况下,flask使用“模板”文件夹包含所有模板文件(任何纯文本文件,但通常是.html某种模板语言,例如jinja2),并使用“静态”文件夹包含所有静态文件(即.js .css和您的图片)。
   在您的中routes,您可以使用render_template()呈现模板文件(如上所述,默认情况下将其放置在templates文件夹中)作为您请求的响应。并且在模板文件(通常是类似.html的文件)中,您可能会使用某些.js和/或`.css'文件,所以我想您的问题是如何将这些静态文件链接到当前模板文件。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.