I've been following multiple Flask tutorials (but mostly this one) on creating a web app that I can use to read an image into a FastAi model. At some point, I was even able to get the images to upload into a folder, but I could never get them to display on the webpage itself no matter what I tried to do (refactor the function, move the folder to 'static', explicitly declare paths, etc.). I assume I'm probably missing something incredibly basic but I don't know enough about Flask and how web apps work to know what that is.
Edit: I keep getting 400 Bad Request pages every time I attempt to upload an image through the "index.html" page and the image doesn't appear in the folder when I inspect it with Windows Explorer.
file structure:
main.py
app
--> __init__.py
--> routes.py
main.py:
from app import app
main.py:
from flask import Flask
app = Flask(__name__)
from app import routes
routes.py:
from app import app
import os
from fastai.vision.widgets import *
from flask import Flask, render_template, request
#app.route('/', methods=['GET','POST'])
def index():
return render_template('index.html')
def get_predictions(img_path):
global learner_inf
learner_inf = load_learner('app/static/model/export.pkl')
return learn_inf.predict(img_path)
#app.route('/predict', methods=['GET','POST'])
def predict():
if request.method == 'POST':
file = request.files['file']
filename = file.filename
file_path = os.path.join('app/static/uploads', filename)
file.save(file_path)
result = get_predictions(file_path)
return render_template('predict.html', result = str(result), user_image = file_path)
index.html
<!doctype html>
<html>
<head>
<title>Grocery Classifier</title>
</head>
<body>
<h1>Grocery Classifier</h1>
<form method="POST" action="/predict" enctype="multipart/form-data">
<p><input type="file" name="file /"></p>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
predict.html
<!doctype html>
<html>
<head>
<title>Grocery Classifier</title>
</head>
<body>
<h1>Grocery Classifier</h1>
<img src="{{ user_image }}" alt="Selected Image" class="img-thumbnail">
<h2>{{ result }}</h2>
</body>
</html>
When referencing static items such as images and files, use the static folder to hold them.
app = Flask(__name__, static_folder="static")
After that, include it in CSS/HTML. This is not the direct application of inserting images in your situation, but it serves as a good example.
background-image: url("bg.gif");
The url to reference the image can be literally anything, just be sure to remember it for later reference. The reference "bg.gif" seen in the CSS automatically does GET request to "yoururl.com/bg.gif" to obtain the image.
Now link that url to the site path.
#app.route('/bg.gif', methods=['GET'])
def bghome():
return send_from_directory("static", "bg.gif")
The route MUST be correct and exact and the file MUST exist. If you use a kwarg to get the image url, be sure to keep that url consistent to what you define in your flask backend pathing.
Related
The structure looks like this:
app
— base
——static
———-images
———-files
——other
data
—images
—files
I was storing images and files in the static folder and it was easily accessible through url_for(). However, I was required to store the items outside the app folder and I could not find a way to access these files.
Is there a way to route to folders outside app to display to HTML?
Basically, you can include any location that you think is right and safe.
I recommend that you familiarize yourself with the Instance Folder.
The default location is './instance/var/'. However, by configuration it is possible to place this at any location that has the necessary access rights.
This simple example can be modified and extended depending on the requirements. Access to another folder outside of the application root directory, within the instance path, is made possible. The behavior is according to the static folder. Subdirectories are therefore also possible.
import os
from flask import Flask
from flask import render_template, send_from_directory
from werkzeug.security import safe_join
app = Flask(__name__,
instance_relative_config=True,
instance_path='/path/to/instance/folder'
)
app.config.from_mapping(
DATA_FOLDER='data'
)
try:
os.makedirs(app.instance_path)
except OSError:
pass
#app.route('/')
def index():
filenames = os.listdir(safe_join(
app.instance_path,
app.config.get('DATA_FOLDER', 'data')
))
return render_template('index.html', **locals())
#app.route('/data/<path:filename>')
def data(filename):
target = safe_join(
app.instance_path,
app.config.get('DATA_FOLDER', 'data')
)
return send_from_directory(target, filename)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<ul>
{% for filename in filenames -%}
<li><a href="{{ url_for('data', filename=filename) }}">{{ filename }}<a></li>
{% endfor -%}
</ul>
</body>
</html>
I have a bunch of files in a directory that I wish to render and serve to the user, but have been unable to. Going to the path always returns just the 'page_template.htm' and not the rendered file. Here's my code:
from flask import Flask, request, render_template, send_from_directory
# Instantiate the Flask app
app = Flask(__name__)
app.config.from_object(__name__)
pages = FlatPages(app)
#app.route("/")
#app.route("/index")
def index():
return render_template('index.html')
#app.route("/Special_Data/<path:path>")
def page(path):
page = send_from_directory('Special_Data', path)
return render_template('page_template.htm', page=page)
What I wish to do is to grab raw text files from the 'Special_Data' directory and to render them into html files so they look nice, then send them to the user if they click on a link.
The files are in the directory 'Special_Data' and the 'page_template.htm' is in the 'templates' directory.
Where am I going wrong?
The following example shows you how you can use FlatPages to list, display and offer files for download. The markdown code is rendered beforehand and integrated into the specified or the default template.
from flask import Flask
from flask import render_template, send_file
from flask_flatpages import FlatPages
from io import BytesIO
import os
app = Flask(__name__)
# Optional configuration here!
pages = FlatPages(app)
# ...
# List all available pages.
#app.route('/contents')
def contents():
return render_template('contents.html', pages=pages)
# Display the rendered result.
#app.route('/page/<path:path>')
def page(path):
page = pages.get_or_404(path)
template = page.meta.get('template', 'flatpage.html')
return render_template(template, page=page)
# Download the rendered result.
#app.route('/download/<path:path>')
def download(path):
page = pages.get_or_404(path)
template = page.meta.get('template', 'flatpage.html')
return send_file(
BytesIO(str(render_template(template, page=page)).encode()),
as_attachment=True,
attachment_filename=f'{os.path.basename(path)}.html'
)
templates/contents.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<ul>
{% for page in pages %}
<li>
[Download] -
{{ page.title }}
</li>
{% endfor %}
</ul>
</body>
</html>
templates/flatpage.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{ page.title }}</title>
</head>
<body>
{{ page }}
</body>
</html>
pages/Special_Data/ncs1.html
title: Hello
published: 2010-12-22
Hello, *World*!
Lorem ipsum dolor sit amet
If you want to use a different template for rendering, you can define one within the metadata based on the file name. Add the following below published and a suitable template within the templates folder.
template: mytemplate.html
The problem is, that send_from_directory sends the file to the client, it does not load it into memory. So your page variable is actually of type Response instead of str or a fileobject. If you want to read the file and than display it use
import werkzeug
...
#app.route("/Special_Data/<path:path>")
def page(path):
p = werkzeug.security.safe_join("Special_Data", path)
with open(p, "r", encoding="utf-8") as f:
return render_template('page_template.htm', page=f.read())
Otherwise I'm afraid I can't help as I don't know what you want to do.
Using send_from_directory Return a file to client.
So, you need to change like this
#app.route("/Special_Data/<path:path>")
def page(path):
return send_from_directory('Special_Data', path)
Or
return send_file(filename_or_fp=file_path, as_attachment=True, add_etags=True, conditional=True)
render_template will render your html template, it may return a html string. Therefore you need return file instead HTML string
I am trying to deploy my flask app so that it is available on any computers in my LAN. However, when I execute it I get the following error in the apache2 error.log file :
The architecture of my app is the following:
This is for __init__.py (python3) :
from flask import Flask, redirect, url_for, render_template,request
app = Flask('__name__')
#app.route("/", methods=["POST", "GET"])
def home():
if request.method == "POST":
return render_template("index.html")
else:
return render_template("index2.html")
if __name__ == "__main__":
app.run()
And my code index.html is :
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>Home Page</h1>
<form action="#" method="post">
<input type="submit" name="submit">
</form>
</body>
</html>
For index2.html it is the same code as index.html only that the tag h1 has a different content.
When I executed this in locally (http://127.0.0.1:5000/) it was working fine.
I am following this tutorial : https://www.youtube.com/watch?v=YFBRVJPhDGY
Any idea on what I might be doing wrong?
Instead of
app = Flask('__name__')
do
app = Flask(__name__)
Flask uses the package/module name to locate the templates folder.
Double check your deployment structure on the server. It's possible that your templates folder is in the wrong location. Like how you have it structured locally, the templates folder should be under your project root folder.
I'm tying to get an image attachment from couchdb using flask & python then pass the image to imgurl.html to be displayed.
The problem is that I'm only get this:
couchdb.http.ResponseBody object at 0x103b9c0b8> returned.
app.py
from flask import Flask, request, render_template, flash, request, url_for, redirect
import couchdb
import requests
app = Flask(__name__)
couch = couchdb.Server('http://127.0.0.1:5984/')
db = couch['test2']
doc = db.get_attachment('2', 'aboutme.jpg', default=None)
print("doc: ", doc)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/imgurl/')
def imgurl():
return render_template('imgurl.html', doc = doc)
#app.route('/page/')
def page():
return("Andrew Irwin")
#return render_template('index.html')
if __name__ == '__main__':
app.run()
test.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<img src="{{ doc }}" id="imgslot" >
<h1 id="h1id"> {{ doc }} </h1>
</body>
</html>
One option could be to base64 encode the image data returned from couchdb and pass the encoded data as a string to the template where you can render it. E.g.
img = db.get_attachment('2', 'aboutme.jpg', default=None).read()
img_b64 = base64.b64encode(img)
def imgurl():
return render_template('imgurl.html', doc = img_b64)
Then in the template:
<img src="data:image/png;base64,{{ doc }}" id="imgslot" >
Another option depending on your security model could be to serve the image directly from couchdb by adding the image url to the image tag, e.g. Getting url for an attachment
Before I saw #SHC's answer I managed to come up with a solution my self.
What I did was retrieve the name of the attachment and then append that to the end of a url like i.e "http:localhost:5984/dbName/docId/attachmentName".
I passed that url to the html img src and it worked then. Thank you you #SHC for your answer. sorry I didnt see it sooner.
I am getting an Internal Server error, and I'm not sure what the issue is. index.html is in the same directory as the Python file.
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def hello_world():
author = "Rick"
name = "1st flask app"
return render_template('index.html', author=author, name=name)
if __name__ == "__main__":
app.run()
<!DOCTYPE html>
<html>
<head>
<title>{{ author }}'s app</title>
</head>
<body>
<h2>Hello {{ name }}!</h2>
<p>Please show up on the page</p>
</body>
</html>
Why am I getting a 500 error rather than my rendered template?
Create a directory called templates and put index.html in it.
You can get more information about errors by running in debug mode:
app.run(debug=True)
When you create your app
app = Flask(__name__)
you can also include the template_folder parameter to specify the folder that contains your templates. If you don't, render_template will look for them in the default folder called templates. If such folder doesn´t exist, or if your template can't be found inside it, you will get an internal server error 500 rather than your rendered template.