【问题标题】:How to serve static files in Flask如何在 Flask 中提供静态文件
【发布时间】:2015-11-03 21:42:07
【问题描述】:

所以这很尴尬。我在Flask 中有一个应用程序,现在它只是提供一个带有一些指向 CSS 和 JS 的链接的静态 HTML 页面。而且我在文档Flask 中找不到描述返回静态文件的位置。是的,我可以使用render_template,但我知道数据没有模板化。我原以为send_file 或url_for 是正确的,但我无法让它们发挥作用。与此同时,我正在打开文件、阅读内容并使用适当的 mimetype 装配Response:

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)

有人想为此提供代码示例或网址吗?我知道这将非常简单。

【问题讨论】:

  • 为什么不使用 nginx 或其他 web 服务器来提供静态文件。
  • 请记住,您实际“提供”文件的方式在生产(在您的 Web 服务器上)和开发(在您的本地计算机或其他一些测试区域)之间可能会有所不同。正如一些答案所指出的那样,您可能不想使用烧瓶提供静态文件,而是将它们放在自己的目录中,然后让您的实际 Web 服务器(Apache、nginx 等)直接为这些文件提供服务。跨度>
  • "为什么不使用 nginx ..." 因为当我在笔记本电脑上以开发人员模式运行它时,只需要运行一件事就很好,而且只运行一件事。是的,它让事情有点不同,但没关系。
  • 即使在生产中,也很常见,当然前面有缓存层(例如 Varnish 或 Nginx 或 CDN)。

标签: python flask static-files


【解决方案1】:

首选方法是使用 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()

或者,您可以使用app.send_file 或app.send_static_file,但强烈建议不要这样做,因为它可能会导致用户提供的路径存在安全风险; send_from_directory 旨在控制这些风险。

【讨论】:

  • 攻击者能否利用这种方法通过浏览 /js/ 的方式来浏览烧瓶源文件 ?
  • @kiwi send_from_directory 旨在解决该安全问题。如果路径指向特定目录之外,则存在错误。
  • "不要将 send_file 或 send_static_file 与用户提供的路径一起使用。"为什么不呢?
  • @drewverlee 我猜它不能确保它不会引出指定目录,在这种情况下他们可以访问他们不应该访问的文件
  • @DenisV 它与 Python 本身无关,它是用于定义 URL 参数的 Flask 约定(参见http://flask.pocoo.org/docs/0.12/api/#url-route-registrations)。简而言之,&lt;path&gt; 等同于&lt;string:path&gt;,并且因为您希望 Flask 确保您要求 &lt;path:path&gt; 提供类似路径的参数。
【解决方案2】:

如果您只想移动静态文件的位置,那么最简单的方法是在构造函数中声明路径。在下面的示例中,我已将模板和静态文件移动到名为 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.py 是 Fl​​ask 实例:

【讨论】:

    【解决方案3】:

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

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

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

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

    【讨论】:

    • 如果有文件project/static/style.css 可用,效果很好。
    • “app = Flask(....)”这一行也需要“static_folder”作为参数
    • static_url_path='/static' 是 2020 年的默认行为,因此没有必要
    【解决方案4】:

    我相信你会在那里找到你需要的东西:http://flask.pocoo.org/docs/quickstart/#static-files

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

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

    【讨论】:

      【解决方案5】:

      你可以使用这个功能:

      send_static_file(filename)
      内部用于发送静态的函数 文件从静态文件夹到浏览器。

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

      【讨论】:

      • 警告:使用用户输入调用send_static_file 是一个巨大的安全问题。不要在任何重要的事情上使用此解决方案。
      【解决方案6】:

      我使用的(而且效果很好)是一个“模板”目录和一个“静态”目录。我将所有 .html 文件/Flask 模板放在模板目录中,静态包含 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

      【讨论】:

        【解决方案7】:

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

        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)
        

        用 HTML 调用 index.html:

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

        重要提示:并且index.html位于一个名为static的文件夹中,这意味着&lt;projectpath&gt;拥有.py文件,而@ 987654325@ 有html 文件。

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

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

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

        这基本上是BlackMamba 的答案,所以给他们一个赞成票。

        【讨论】:

          【解决方案8】:

          对于创建下一个文件夹树的 angular+boilerplate 流程:

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

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

          【讨论】:

            【解决方案9】:
            app = Flask(__name__, static_folder="your path to static")
            

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

            【讨论】:

              【解决方案10】:

              默认情况下,名为“static”的文件夹包含所有静态文件 这是代码示例:

              &lt;link href="{{ url_for('static', filename='vendor/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet"&gt;

              【讨论】:

                【解决方案11】:

                所以我得到了工作(基于@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)
                

                【讨论】:

                  【解决方案12】:

                  一种简单的方法。 干杯!

                  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)
                  

                  现在创建名为 templates 的文件夹名称。 在 templates 文件夹中添加您的 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 但我知道数据没有模板化。”
                  【解决方案13】:

                  使用redirect 和url_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...)提供服务。

                  【讨论】:

                    【解决方案14】:

                    想到分享....这个例子。

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

                    这样更好更简单。

                    【讨论】:

                      【解决方案15】:

                      所有答案都很好,但对我来说效果很好的只是使用来自 Flask 的简单函数send_file。当 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])```
                      
                      

                      【讨论】:

                        【解决方案16】:

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

                        主文件夹

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

                        Image of main folder containing static and templates folders and flask script

                        烧瓶

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

                        【讨论】:

                          【解决方案17】:

                          可以使用静态端点创建静态文件的 URL,如下所示:

                          url_for('static', filename = 'name_of_file')
                          <link rel="stylesheet" href="{{url_for('static', filename='borders.css')}}" />
                          

                          【讨论】:

                            【解决方案18】:

                            我遇到的问题与使用 static_url_path 和 static_folder 时未为目录服务的 index.html 文件有关。

                            这是我的解决方案:

                            import os
                            from flask import Flask, send_from_directory
                            from flask.helpers import safe_join
                            
                            app = Flask(__name__)
                            static = safe_join(os.path.dirname(__file__), 'static')
                            
                            @app.route('/')
                            def _home():
                              return send_from_directory(static, 'index.html')
                            
                            @app.route('/<path:path>')
                            def _static(path):
                              if os.path.isdir(safe_join(static, path)):
                                path = os.path.join(path, 'index.html')
                              return send_from_directory(static, path)
                            

                            【讨论】:

                              【解决方案19】:

                              这对我有用:

                              import os
                              from flask import Flask, render_template, send_from_directory
                              app = Flask(__name__)
                              
                              
                              root = os.path.join(os.path.dirname(os.path.abspath(__file__)), "whereyourfilesare")
                              
                              @app.route('/', methods=['GET'])
                              def main(request):
                                  path = request.path
                                  if (path == '/'):
                                      return send_from_directory(root, 'index.html')
                                  else:
                                      return send_from_directory(root, path[1:])
                              

                              【讨论】:

                                【解决方案20】:

                                您可以使用 Jinja 模板引擎做到这一点。

                                <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
                                <script type="text/javascript" src="{{url_for('static', filename='script.js')}}">
                                

                                【讨论】:

                                  【解决方案21】:

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

                                  【讨论】:

                                    【解决方案22】:

                                    如果你只是想打开一个文件,你可以使用app.open_resource()。所以读取文件看起来像

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

                                    【讨论】:

                                      【解决方案23】:

                                      在静态目录中,在该目录中创建模板目录添加所有html文件为css和javascript创建单独的目录,因为flask将处理或识别模板目录中的所有html文件。

                                      static -
                                             |_ templates
                                             |_ css
                                             |_javascript
                                             |_images
                                      

                                      【讨论】:

                                        【解决方案24】:

                                        我们应该将 html 模板放在“templates”文件夹中,以便烧瓶可以默认选择它。同样,静态文件夹应该提供 css/js/ 图像。请在下面找到文件夹结构。

                                        【讨论】:

                                          【解决方案25】:

                                          在我的情况下,我需要用户可以访问静态文件夹中的所有文件,并且我需要为我的一些 html 文件使用模板,以便可以将常见的 html 代码放在模板中并代码被重用。这是我如何一起实现它们的:

                                          from flask import Flask, request, render_template
                                          from flask.json import JSONEncoder
                                          
                                          app = Flask(__name__, template_folder='static')
                                          
                                          
                                          @app.route('/<path:path>')
                                          def serve_static_file(path):
                                              # In my case, only html files are having the template code inside them, like include.
                                              if path.endswith('.html'):
                                                  return render_template(path)
                                              # Serve all other files from the static folder directly.
                                              return app.send_static_file(path)
                                          

                                          我所有的文件都保存在静态文件夹下,与主烧瓶文件平行。

                                          【讨论】:

                                            猜你喜欢
                                            相关资源
                                            最近更新 更多