python flask browsing through directory with files
Question:
Is it possible to use flask to browse through a directory with files?
My code never seems to work correctly as weird appending between strings happens.
Also I don`t know how to implement a kind of check whether the path is a file or a folder.
Here is my Flask app.route:
@app.route('/files', defaults={'folder': None,'sub_folder': None}, methods=['GET'])
@app.route('/files/<folder>', defaults={'sub_folder': None}, methods=['GET'])
@app.route('/files/<folder>/<sub_folder>', methods=['GET'])
def files(folder,sub_folder):
basedir = 'files/'
directory = ''
if folder != None:
directory = directory + '/' + folder
if sub_folder != None:
directory = directory + '/' + sub_folder
files = os.listdir(basedir + directory)
return render_template('files.html',files=files,directory=basedir + directory,currdir=directory)
and here is my html template, if anyone could give me some pointers it would be greatly appreciated!
<body>
<h2>Files {{ currdir }}</h2> </br>
{% for name in files: %}
<A HREF="{{ directory }}{{ name }}">{{ name }}</A> </br></br>
{% endfor %}
</body>s.html',files=files,directory=basedir + directory,currdir=directory)
Answers:
A path
converter (docs link) in the url structure is better than hardcoding all the different possible path structures.
os.path.exists
can be used to check if the path is valid and os.path.isfile
and os.path.isdir
for checking if the path is a file or a directory, respectively.
Endpoint:
@app.route('/', defaults={'req_path': ''})
@app.route('/<path:req_path>')
def dir_listing(req_path):
BASE_DIR = '/Users/vivek/Desktop'
# Joining the base and the requested path
abs_path = os.path.join(BASE_DIR, req_path)
# Return 404 if path doesn't exist
if not os.path.exists(abs_path):
return abort(404)
# Check if path is a file and serve
if os.path.isfile(abs_path):
return send_file(abs_path)
# Show directory contents
files = os.listdir(abs_path)
return render_template('files.html', files=files)
Template (Now with directory browsing 🙂 ):
<ul>
{% for file in files %}
<li>
<a href="{{ (request.path + '/' if request.path != '/' else '') + file }}">
{{ (request.path + '/' if request.path != '/' else '') + file }}
</a>
</li>
{% endfor %}
</ul>
Note: abort
and send_file
functions were imported from flask.
Here is a working example.
# app.py
from flask import Flask
from flask_autoindex import AutoIndex
app = Flask(__name__)
ppath = "/" # update your own parent directory here
app = Flask(__name__)
AutoIndex(app, browse_root=ppath)
if __name__ == "__main__":
app.run()
Here is a working repo
I created this function for my project … it is working perfectly … it start browsing at this folder /home/myuser/myfolder
@app.route('/myfolder/<path:folders>')
@app.route('/myfolder/')
def mybrowser(folders=''):
environ = flask.request.environ
path = environ.get('PATH_INFO')
path = path.lower()
#if path=='/myfolder': return flask.redirect(path+'/',code=307)
os_path = '/home/myuser'+path.rstrip('/')
if path.endswith('/'):
HTML_HEADER = """<html><head><title>Index of {path_title}</title></head><body bgcolor="white"><h1>Index of {path_title}</h1><hr><pre><a href="../">../</a>n"""
HTML_FOOTER = "</pre><hr></body></html>"
path_title = os_path.split('myuser')[1]+'/'
html = HTML_HEADER.format(path_title=path_title)
import os,time
files = os.listdir(os_path)
for file in files:
path = os_path+'/'+file
size = str(os.path.getsize(path))
date = os.path.getmtime(path)
date = time.gmtime(date)
date = time.strftime('%d-%b-%Y %H:%M',date)
spaces1 = ' '*(50-len(file))
spaces2 = ' '*(20-len(size))
if os.path.isdir(path): html += '<a href="' + file + '/">' + file + '/</a>'+spaces1+date+spaces2+' -n'
else: html += '<a href="' + file + '">' + file + '</a>'+spaces1+' '+date+spaces2+size+'n'
html += HTML_FOOTER
#open(os_path+'/index.html','w').write(html)
response_headers = {'Content-Type':'text/html','Content-Length':str(len(html))}
status = '200 OK'
response = flask.Response(html,status=status,headers=response_headers)
else:
response = flask.send_file(os_path,conditional=True)
return response
Here’s a quick and dirty implementation using pathlib
‘s .iterdir
and Flask.send_from_directory
to create dynamic routes to files in the same directory as this flask app and generates a unordered list of links to be displayed at the root route.
This isn’t recursive. It won’t create routes for sub-directories or files within subdirectories. It’s what I needed when I came looking here on SO for answers.
"""Simple Flask App to serve the contents of the current directory.
$ python serve_directory.py
this serves browseable contents of this file's directory.
to http://localhost:8080.
"""
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
from flask import Flask, send_from_directory
if TYPE_CHECKING:
from typing import Iterator
from flask import Response
# Instantiate a Flask app object
app: Flask = Flask(__name__)
# Get the parent directory of this script. (Global)
DIR_PATH: Path = Path(__file__).parent
def get_files_from_this_directory() -> Iterator[str]:
"""Generate the items within this script's directory.
Yields:
Generator: item(s) in __file__'s directory.
"""
for dir_item in DIR_PATH.iterdir():
yield dir_item.name
@app.route("/files/<file_name>") # type: ignore
def serve_file(file_name: str) -> Response:
"""Set up a dynamic routes for directory items at /files/.
Args:
file_name (str): regular file.
Returns:
Response: regular file.
"""
return send_from_directory(DIR_PATH, file_name)
def html_ul_of_items() -> str:
"""Create a unordered list of anchors/links to file routes.
Returns:
str: a <ul> with N <li> elements where N is the number of
elements in __file__'s directory.
"""
html: str = "<ul>"
for dir_item in get_files_from_this_directory():
html += f"<li><a href='files/{dir_item}'>{dir_item}</a`></li>"
return f"{html}</ul>"
@app.route("/") # type: ignore
def serve_index() -> str:
"""Root route which displays an unordered list of directory items.
Returns:
str: a <ul> with N <li> elements where N is the number of
elements in __file__'s directory.
"""
return html_ul_of_items()
def main() -> None:
"""Run the flask app."""
app.run(port=8080)
if __name__ == "__main__":
main()
Is it possible to use flask to browse through a directory with files?
My code never seems to work correctly as weird appending between strings happens.
Also I don`t know how to implement a kind of check whether the path is a file or a folder.
Here is my Flask app.route:
@app.route('/files', defaults={'folder': None,'sub_folder': None}, methods=['GET'])
@app.route('/files/<folder>', defaults={'sub_folder': None}, methods=['GET'])
@app.route('/files/<folder>/<sub_folder>', methods=['GET'])
def files(folder,sub_folder):
basedir = 'files/'
directory = ''
if folder != None:
directory = directory + '/' + folder
if sub_folder != None:
directory = directory + '/' + sub_folder
files = os.listdir(basedir + directory)
return render_template('files.html',files=files,directory=basedir + directory,currdir=directory)
and here is my html template, if anyone could give me some pointers it would be greatly appreciated!
<body>
<h2>Files {{ currdir }}</h2> </br>
{% for name in files: %}
<A HREF="{{ directory }}{{ name }}">{{ name }}</A> </br></br>
{% endfor %}
</body>s.html',files=files,directory=basedir + directory,currdir=directory)
A path
converter (docs link) in the url structure is better than hardcoding all the different possible path structures.
os.path.exists
can be used to check if the path is valid and os.path.isfile
and os.path.isdir
for checking if the path is a file or a directory, respectively.
Endpoint:
@app.route('/', defaults={'req_path': ''})
@app.route('/<path:req_path>')
def dir_listing(req_path):
BASE_DIR = '/Users/vivek/Desktop'
# Joining the base and the requested path
abs_path = os.path.join(BASE_DIR, req_path)
# Return 404 if path doesn't exist
if not os.path.exists(abs_path):
return abort(404)
# Check if path is a file and serve
if os.path.isfile(abs_path):
return send_file(abs_path)
# Show directory contents
files = os.listdir(abs_path)
return render_template('files.html', files=files)
Template (Now with directory browsing 🙂 ):
<ul>
{% for file in files %}
<li>
<a href="{{ (request.path + '/' if request.path != '/' else '') + file }}">
{{ (request.path + '/' if request.path != '/' else '') + file }}
</a>
</li>
{% endfor %}
</ul>
Note: abort
and send_file
functions were imported from flask.
Here is a working example.
# app.py
from flask import Flask
from flask_autoindex import AutoIndex
app = Flask(__name__)
ppath = "/" # update your own parent directory here
app = Flask(__name__)
AutoIndex(app, browse_root=ppath)
if __name__ == "__main__":
app.run()
Here is a working repo
I created this function for my project … it is working perfectly … it start browsing at this folder /home/myuser/myfolder
@app.route('/myfolder/<path:folders>')
@app.route('/myfolder/')
def mybrowser(folders=''):
environ = flask.request.environ
path = environ.get('PATH_INFO')
path = path.lower()
#if path=='/myfolder': return flask.redirect(path+'/',code=307)
os_path = '/home/myuser'+path.rstrip('/')
if path.endswith('/'):
HTML_HEADER = """<html><head><title>Index of {path_title}</title></head><body bgcolor="white"><h1>Index of {path_title}</h1><hr><pre><a href="../">../</a>n"""
HTML_FOOTER = "</pre><hr></body></html>"
path_title = os_path.split('myuser')[1]+'/'
html = HTML_HEADER.format(path_title=path_title)
import os,time
files = os.listdir(os_path)
for file in files:
path = os_path+'/'+file
size = str(os.path.getsize(path))
date = os.path.getmtime(path)
date = time.gmtime(date)
date = time.strftime('%d-%b-%Y %H:%M',date)
spaces1 = ' '*(50-len(file))
spaces2 = ' '*(20-len(size))
if os.path.isdir(path): html += '<a href="' + file + '/">' + file + '/</a>'+spaces1+date+spaces2+' -n'
else: html += '<a href="' + file + '">' + file + '</a>'+spaces1+' '+date+spaces2+size+'n'
html += HTML_FOOTER
#open(os_path+'/index.html','w').write(html)
response_headers = {'Content-Type':'text/html','Content-Length':str(len(html))}
status = '200 OK'
response = flask.Response(html,status=status,headers=response_headers)
else:
response = flask.send_file(os_path,conditional=True)
return response
Here’s a quick and dirty implementation using pathlib
‘s .iterdir
and Flask.send_from_directory
to create dynamic routes to files in the same directory as this flask app and generates a unordered list of links to be displayed at the root route.
This isn’t recursive. It won’t create routes for sub-directories or files within subdirectories. It’s what I needed when I came looking here on SO for answers.
"""Simple Flask App to serve the contents of the current directory.
$ python serve_directory.py
this serves browseable contents of this file's directory.
to http://localhost:8080.
"""
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
from flask import Flask, send_from_directory
if TYPE_CHECKING:
from typing import Iterator
from flask import Response
# Instantiate a Flask app object
app: Flask = Flask(__name__)
# Get the parent directory of this script. (Global)
DIR_PATH: Path = Path(__file__).parent
def get_files_from_this_directory() -> Iterator[str]:
"""Generate the items within this script's directory.
Yields:
Generator: item(s) in __file__'s directory.
"""
for dir_item in DIR_PATH.iterdir():
yield dir_item.name
@app.route("/files/<file_name>") # type: ignore
def serve_file(file_name: str) -> Response:
"""Set up a dynamic routes for directory items at /files/.
Args:
file_name (str): regular file.
Returns:
Response: regular file.
"""
return send_from_directory(DIR_PATH, file_name)
def html_ul_of_items() -> str:
"""Create a unordered list of anchors/links to file routes.
Returns:
str: a <ul> with N <li> elements where N is the number of
elements in __file__'s directory.
"""
html: str = "<ul>"
for dir_item in get_files_from_this_directory():
html += f"<li><a href='files/{dir_item}'>{dir_item}</a`></li>"
return f"{html}</ul>"
@app.route("/") # type: ignore
def serve_index() -> str:
"""Root route which displays an unordered list of directory items.
Returns:
str: a <ul> with N <li> elements where N is the number of
elements in __file__'s directory.
"""
return html_ul_of_items()
def main() -> None:
"""Run the flask app."""
app.run(port=8080)
if __name__ == "__main__":
main()