Flask: How to get uploads folder path - python

I'm trying to get the uploads folder path.
UPLOAD_FOLDER = '/uploads'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
I upload the images in the path:
.
└── uploads
└── img_articles
└── 2017
└── 02
└── image.jpg
I want to use image.jpg in a Jinja Template
{% extends "layout.html" %}
{% block content %}
<div class="container">
<img src="{{image_path}}" alt="">
</div>
{% endblock %}
What should I do?

Have a look at the Uploading Files Pattern in the information for the Pros.
Define a route for your uploads:
from flask import send_from_directory
#app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
In your template you can then use the route to get your filename
<img src="{{ url_for('uploaded_file', filename='yourimage.jpg') }}">

Related

Unable to show uploaded files using Flask

I have followed Miguel Grinberg's Flask Mega Tutorial and also his tutorial on uploading files using Flask. I'm trying to combine the two to build my own web app. I have a site that handles user logins and allows files (scans of exam papers) to be uploaded to app/uploads/userID. The file details, including the filename, are stored (successfully) in a SQLite database. I can see the files in File Explorer and VS Code, but I cannot get them to display / download from within the website. I don't want to use the static folder as I want to keep each user's uploads private to them.
Here is the relevant section of routes.py:
#app.route('/user/<username>', methods=['GET', 'POST'])
#login_required
def user(username):
user = User.query.filter_by(username=username).first_or_404()
papers=user.papers.all()
form = PaperForm()
if form.validate_on_submit():
uploaded_file = request.files['file']
filename = secure_filename(uploaded_file.filename)
if filename != '':
file_ext = os.path.splitext(filename)[1]
if file_ext not in app.config['UPLOAD_EXTENSIONS']:
flash('Invalid file type. Only PDF files are accepted.')
abort(400)
myPath = os.path.join(app.config['UPLOAD_PATH'],current_user.get_id())
if os.path.isdir(myPath)==False:
os.mkdir(myPath)
uploaded_file.save(os.path.join(app.config['UPLOAD_PATH'],current_user.get_id(), filename))
paper = Paper(paper_name=form.paper.data, author=current_user, filename = filename)
db.session.add(paper)
db.session.commit()
flash('Your paper has been added to the database.')
return redirect(url_for('user', username=user.username))
return render_template('user.html', user=user, form=form, papers=papers)
#app.route('/uploads/<filename>')
#login_required
def upload(filename):
return send_from_directory(os.path.join(app.config['UPLOAD_PATH'], current_user.get_id()), filename)
And here is the template for user.html, which shows the upload form and beneath it displays a table of all the papers uploaded by that user:
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h2>Add a paper</h2>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
<hr>
<h2>My Papers</h2>
<table class="table">
{% for paper in papers %}
<tr valign="top">
<td>{{ paper.paper_name }}</td>
<td>{{ paper.author.username }}</td>
<td>{{ paper.created_time }}</td>
<td>{{ url_for('upload', filename=paper.filename) }}</td>
<td>{{ paper.filename }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
When clicking on the link to a file I just get a 404 file not found error.
Update:
I found a partial solution elsewhere on Stack Overflow. I'm pretty sure the problem is the path to the files. If I change the upload path to
UPLOAD_PATH = './uploads/' then I can see the files which have been uploaded already. However, when I then try to upload a file it fails with an error that the new directory couldn't be created. If I create a directory parallel to the app folder rather than within it I can upload, but of course downloads then fail! I am using Windows, which I suspect may be part of the problem.
Examscanner/
├─ app/
│ ├─ static/
│ ├─ __pycache__/
│ ├─ templates/
│ ├─ uploads/
│ ├─ __init__
│ ├─ errors.py
│ ├─ forms.py
│ ├─ models.py
│ ├─ routes.py
├─ migrations/
├─ examvenv/
I got this working in the end by changing the config.py file to include:
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
UPLOAD_PATH = os.path.join(APP_ROOT, 'uploads')
The files are stored in the uploads folder alongside the app folder rather than within it. I'm hoping this won't cause other problems further down the line.

Unable to load templates using url_for

I have a RESTful API that is built with flask, and on the home page / I want to build a web GUI into it. In order to do so I'm trying to build a flask app into the Index part of my API calls:
class Index(Resource):
def get(self):
from flask import Response, render_template, Flask
from lib.interface.settings import get_db_folders, get_file_type_count, get_recent_scans, make_keys
log_request(request, "/", "GET")
recent_scans = get_recent_scans(DATABASE_FILES_PATH, amount=10)
all_folders = get_db_folders(DATABASE_FILES_PATH)
file_types = get_file_type_count(DATABASE_FILES_PATH)
website = Flask(__name__)
#website.route("/everything")
def show_all():
return Response(render_template(
"everything.html"
))
#website.route("/upload")
def upload():
return Response(render_template(
"upload.html"
))
#website.route("/api")
def apidocs():
return Response(render_template(
"apidocs.html"
))
return Response(render_template(
"index.html", recent_scans=recent_scans,
all_scans_length=len(all_folders),
file_types=file_types, recent_scan_length=len(recent_scans) + 1
))
Everything works fine when I load into the main screen (/). I can see the index.html page. However when trying to redirect to a page called "everything.html" I get an error (the error depends on how I'm trying it as seen below):
If I use <li><span>All Uploads</span></li> in everything.html the error I get is:
...
<li><span>All Uploads</span></li>
TypeError: url_for() takes exactly 1 argument (2 given)
If I do <li><span>All Uploads</span></li>
I get the following error:
...
raise BuildError(endpoint, values, method, self)
BuildError: Could not build url for endpoint 'everything'. Did you mean 'getstrings' instead?
If I do <li><span>All Uploads</span></li> I get:
...
<li><span>All Uploads</span></li>
TypeError: url_for() takes exactly 1 argument (2 given)
And so on and so forth. I'm pretty sure the problem is the way I'm trying to do this part (putting it into an API, etc) but I'm not sure if there is a work around that might work? As of right now my head.html file looks like this:
<!doctype html>
<head>
<meta charset="UTF-8">
<title>Sandbox - {% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='base.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
<script src="{{ url_for('static', filename='base.js') }}"></script>
</head>
<header id="display-port">
<img id="header-image" src="{{ url_for('static', filename='base_image.png') }}"/>
</header>
<body>
<nav class="navigation-pane">
<h3 id="navigation-header">Navigation:</h3>
<ul>
<li><span>All Uploads</span></li>
<li><span>Upload Files</span></li>
<li><span>API Documentation</span></li>
</ul>
</nav>
<section class="content">
<h3>Current File Type Counts:</h3>
<ul>
{% for key in file_types.keys() %}
<li>{{ key.upper() }}: {{ file_types[key] }}</li>
{% endfor %}
</ul>
{% block header %}{% endblock %}
{% block content %}{% endblock %}
</section>
</body>
My index.html file looks like this:
{% extends "head.html" %}
{% block title %}Home{%endblock%}
{% block header %}
<h1>Recent Scans:</h1>
{% endblock %}
{% block content %}
<h5>Total Database Scans: {{ all_scans_length }}</h5>
<ul>
{% for i in range(0, recent_scan_length) %}
<li>Hash: {{ recent_scans[i] }}</li>
{% endfor %}
</ul>
{% endblock %}
Directory structure looks like this:
.
├── __init__.py
├── main.py
├── static
│   ├── base.css
│   ├── base.js
│   ├── favicon.ico
│   └── base_image.png
├── templates
│   ├── apidocs.html
│   ├── everything.html
│   ├── head.html
│   ├── index.html
│   └── upload.html
├── wsgi.py
And my everything.html file has nothing in it. I think it is worth noting that I am able to load from the static folder in head.html without problem. So question being as stated above, what is causing the issue? How can I fix it?

How to Access type="file" Input From Flask App

From my flask app, I need to access the file from the <input type="file">. Whether it be the file data or the actual file. I need to do something like this: http://jsfiddle.net/ugPDx/ but without js
thanks in advance!
Make sure you have created the static folder and inside that folder, you have the uploads folder.
upload.html
<!doctype html>
<title>Python Flask Image Upload and Display Example</title>
<h2>Select an image to upload and display</h2>
<p>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</p>
{% if filename %}
<div>
<img src="{{ url_for('display_image', filename=filename) }}">
</div>
{% endif %}
<form method="post" action="/" enctype="multipart/form-data">
<dl>
<p>
<input type="file" name="file" autocomplete="off" required>
</p>
</dl>
<p>
<input type="submit" value="Submit">
</p>
</form>
app.py
import os
import urllib.request
from flask import Flask, flash, request, redirect, url_for, render_template
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = 'static/uploads/'
app = Flask(__name__)
app.secret_key = "secret key"
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#app.route('/')
def upload_form():
return render_template('upload.html')
#app.route('/', methods=['POST'])
def upload_image():
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No image selected for uploading')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
#print('upload_image filename: ' + filename)
flash('Image successfully uploaded and displayed')
return render_template('upload.html', filename=filename)
else:
flash('Allowed image types are -> png, jpg, jpeg, gif')
return redirect(request.url)
#app.route('/display/<filename>')
def display_image(filename):
#print('display_image filename: ' + filename)
return redirect(url_for('static', filename='uploads/' + filename), code=301)
if __name__ == "__main__":
app.run()

How to display images from folder in Python html and flask

I am very new to python and Flask.
I have a folder with a list of jpeg images. I have to display them in my demo application with flask as below,
My app.py :
#app.route("/")
def home():
return render_template('home.html')
My home.html:
<img id="person" src={{ url_for('static',filename='pferson_folder/000_1.jpg') }}>
In the above code, I don't want to hardcode the images in the HTML tag. it needs to take the image source from folder dynamically.
Would you please help me with this. Thank you.
You can read all the file names out of the directory using os.listdir('Your path') and pass the array into the template:
Something like:
# Inside app.py
import os
#app.route('/')
def home():
image_names = os.listdir('Your path to images folder')
render_template('home.html', image_name=image_names)
And inside your template:
{% for name in image_names %}
<img src="{{ url_for('static', filename='pferson_folder/' + name) }}" >
{% endfor %}
Then you don't have to hardcode the names.

No such file or directory: /uploads [duplicate]

This question already has answers here:
Refering to a directory in a Flask app doesn't work unless the path is absolute
(1 answer)
Saving upload in Flask only saves to project root
(1 answer)
Flask: IOError when saving uploaded files
(2 answers)
Closed 4 years ago.
I've looked at a couple of the other questions but can't figure out what is going wrong. I get the following error: "FileNotFoundError: [Errno 2] No such file or directory: '/uploads\MRRtest.csv'" Can anyone help? What is the difference between the forward and backward slashes on the error message?
Thanks
from flask import Flask, render_template, request, redirect, url_for, flash
from flask.ext.bootstrap import Bootstrap
from werkzeug import secure_filename
import os
app = Flask(__name__)
bootstrap = Bootstrap(app)
UPLOAD_FOLDER = '/uploads'
ALLOWED_EXTENSIONS = set(['csv'])
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'],filename))
return redirect(url_for('uploaded_file', filename=filename))
return render_template('index.html')
My index.html template is as follows:
{% extends "base.html" %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Upload File</h1>
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
</div>
{% endblock %}
/uploads means an absolute link (C:/upload), so you should use upload/ instead.
Also, you can use the nice snippet from https://stackoverflow.com/a/20257725/5851179
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
UPLOAD_FOLDER = os.path.join(APP_ROOT, 'static/uploads')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
Just realized from the comments that my uploads directory is in the same directory as my run.py file rather than the 'templates' directory that index.html is running from. I modified the
UPLOAD_FOLDER = '/uploads'
to
UPLOAD_FOLDER = './uploads'
I'll now work on building the "url for endpoint"
Thanks for your help.

Categories