首页 > 解决方案 > 如何在 Flask/python app.py 或 views.py 文件中提供、引用和保存静态文件,避免 FileNotFound 错误

问题描述

类似于这个问题,但我有一些后续行动。如何在 Flask 中提供静态文件

我在调整上一篇文章中的解决方案时遇到的主要问题是我需要使用 matplotlib 的.savefig()函数,它只接受一个字符串作为保存路径。所以我需要知道传递什么字符串。

我记得当我第一次在本地处理我的 Flask 应用程序时,我遇到了很多关于静态文件的问题。我不知道我应该如何从我的app.py/ views.pypython 文件中正确引用静态文件的路径。我了解url_for()在 html 中使用什么,但在 python 中引用静态文件并不那么简单。

我的烧瓶应用程序具有以下结构。根目录称为Neuroethics_Behavioral_Task

在此处输入图像描述

我有一个名为的文件experiment.py,该文件基本上是一个views.pyapp.py文件。但我似乎找不到引用静态文件的正确方法。

这是experiment.py代码。

import functools

from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for, Flask
)
from werkzeug.security import check_password_hash, generate_password_hash


app = Flask(__name__, instance_relative_config=True, static_url_path='') # creates the flask instance

bp= Blueprint('experiment', __name__)


@app.route('/')
@app.route('/index')
def index():
    return render_template("welcome.html")

@app.route('/experiment_app', methods=['GET', 'POST'])
def exp_js_psych_app():
    total_trials = int(request.form['trials'])
    import random 
    import string 
    user_id = ''.join([random.choice(string.ascii_letters 
                + string.digits) for n in range(8)]) 
    user_id = str(user_id)
    stim_metadata = generate_stim_for_trials(total_trials, user_id)
    return render_template("index.html", data={"user_id": str(user_id), "total_trials":total_trials, "lst_of_stim_jsons":stim_metadata})
def generate_stim_for_trials(trials, uid):
    import matplotlib
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    import json
    import os
    json_dict = {}
    json_dict["stimuli path info"] = []
    lst_of_jsons_for_stimuli = []
    # Unused but problematic static reference.
    stim_metadata_path = "/static/textdata/stimulus_data" + str(uid) + ".json"
    # Another problematic static reference.
    stimuli_path = '/static/images/stimulus_img' + 1 + ".png"
    df = pd.DataFrame({"Risk":[25], "No Effect" : [50], "Gain":[25]}) 
    df.plot(kind='barh', legend = False, stacked=True, color=['#ed713a', '#808080', '#4169e1'])
    plt.axis('off')
    plt.gca().set_axis_off()
    plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0)
    plt.ylim(None,None)
    plt.autoscale()
    plt.margins(0,0)

    plt.savefig(stimuli_path) # this fails

需要注意的是,将static_url_path参数传递给Flask()调用并没有真正按预期工作,我认为这会将根目录建立为首先查找我的静态文件的位置,但该代码仍然失败的事实表明否则,因为我的/static/目录显然在根文件夹中。

否则,正如我所说...我需要使用plt.savefig()which 接受路径,但我仍然无法弄清楚我应该如何编写这条路径。

在此之前,我已经设法让静态文件正常工作。我不得不将上面对静态文件的两个引用更改为以下内容:

stimuli_path = 'Neuroethics_Behavioral_Task/static/images/stimulus_img' + "1" + ".png"
stim_metadata_path = "Neuroethics_Behavioral_Task/static/textdata/stimulus_data" + str(uid) + ".json"

换句话说,我可以通过在路径中包含根目录来获取静态文件。

这至少让我度过了......直到我尝试将我的应用程序部署到 Heroku 上。然后我开始收到Neuroethics_Behavioral_Task/static/images/不是可识别路径的错误......所以现在我觉得我需要真正开始正确地引用静态文件,我现在不知道如何去做。

我尝试了变体(例如:)images/stimulusimg1.png并尝试更改static_url_path参数(例如:)static_url_path='/static/,但这些都只是导致我的 Flask 应用程序在本地崩溃说 FileNotFound。

我有另一个重要的后续问题......

该应用程序将在 Amazon Mechanical Turk 上发布在线实验。图像应该由我正在使用的名为 JsPsych 的框架预加载。但是像这样的帖子和我第一次包含的帖子鼓励您使用 NGINX提供文件“提供静态文件”到底是什么意思?

我不知道 Nginx 或 wsgi 是如何工作的。我是否还应该费心尝试以这种方式提供所有静态文件,如果是,我该如何开始?

总而言之,我要问的问题是

编辑:url_for()也不工作。

stimuli_in_static = "images/stimulus_img" + "1" + ".png"
stimuli_path = url_for('static', filename=stimuli_in_static)

这仍然会导致.savefig()调用时出现 FileNotFound 错误,

FileNotFoundError: [Errno 2] No such file or directory: '/static/images/stimulus_img1.png'

标签: pythonnginxherokuflaskstatic

解决方案


根据我的评论,因为 Heroku 不支持持久文件系统存储,所以推荐的方法是使用 S3。为此,您需要plot.fig写入字节 IO 缓冲区,然后将其上传到 S3。这个过程包含在这个 SO 线程中。

无论哪种方式,这都是关于静态文件内容的答案,这在 Heroku 上是允许的,前提是所讨论的静态文件实际上是 repo 的一部分(javascript 和 CSS 文件等)......


static_url_path旨在更改静态文件可用的 URL。这会影响提供静态文件的前端 URL路径。

使用以下配置静态文件可用,http://example.com/apple/无论它们在文件系统上的什么位置:

app = Flask(__name__, static_url_path='apple')

如果要定义 Flask 在文件系统中查找静态文件的位置,请static_folder改用。在您的情况下,应用程序位于app_dir/experiment.py并且您的static目录位于顶层。因此,您需要提供与 where 相关experiment.py的参数:

app = Flask(__name__, static_folder='../static')

就我个人而言,我试图消除这整个头痛并创建一个符合 Flask 默认期望的目录结构:

project
├── experiment.py
├── static
│   └── some.js
└── templates
    └── some.html

关于您的代码的另一个提示:

def generate_stim_for_trials(trials, uid):
    import matplotlib
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    import json
    import os

这是低效的,因为每次调用该函数时都会加载所有这些导入。

你真的想把这些导入放在文件的顶部,这样它们就会在应用服务器运行时加载,然后才加载。:

import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import json
import os

# ...

def generate_stim_for_trials(trials, uid):
    json_dict = {}
    # ...

推荐阅读