This question already has answers here:
How to serve static files in Flask
(24 answers)
Closed 5 years ago.
I'm making an upload/download service just for fun but am struggling to serve files that are outside of static directory, which causes an issue because anyone could visit www.mysite.com/static and view the contents.
This is what I have so far. Forgive the ugly paths of my network storage :)
if not os.path.exists('\\ATTILA\\Development\\GIT\\MyCloud\\static\\'+ session['username']):
os.makedirs('\\\\ATTILA\\Development\\GIT\\MyCloud\\static\\'+ session['username'])
#Download links for all files
pathing = []
my_path = '\\\\ATTILA\\Development\\GIT\\MyCloud\\static\\'+ session['username'] + '\\'
os.chdir('\\\\ATTILA\\Development\\GIT\\MyCloud\\static\\'+ session['username'])
for myfile in glob.glob('*'):
downs = my_path + myfile
pathing.append(tuple([downs, myfile]))
In my template I have a simple for loop
{% for myfile, name in pathing %}
<a href='{{ myfile }}' download>{{ name }}</a><br>
{% endfor %}
So my view looks like this:
As it stands my files are downloadable, however if I change the file paths for the downloads to a folder outside of 'static' then instead of download links I get 404 errors that point to the URL + the file path like so www.mysite.com\ATTILLA\Development\some_other_folder any suggestions?
If you want place your app in production you need use solutions like a nginx for serving you static files.
Usually in development stage Flask work with regulary static files (css, js and another) self. It's normal.
If you want hide some private data or uploaded files you need use something like this:
from flask import Flask, make_response
...
#app.route('/file_downloads/<filename>')
def file_downloads(filename):
headers = {"Content-Disposition": "attachment; filename=%s" % filename}
with open('../tmp/you_private_file.zip', 'r') as f:
body = f.read()
return make_response((body, headers))
Related
I followed this tutorial and everything works except for downloading the images that were uploaded. I get 404 errors for everything I try to display in the browser. I can see the images on the hard drive just fine, but they do not download on the /uploads route. ANy ideas?
#bp.route('/uploads/<filename>')
def upload(filename):
print('path: '+str(current_app.config['PRODUCT_UPLOAD_PATH'])+', filename: '+filename)
return send_from_directory(current_app.config['PRODUCT_UPLOAD_PATH'], filename)
Here is the html template that loads the files. The filenames popup proeprly, but the route just shows 404 errors for all the images.
{% for file in files %}
<img src="{{ url_for('main.upload', filename=file) }}" style="width: 64px">
{% endfor %}
config.py to show where the directories are pointing.
MAX_CONTENT_LENGTH = 1024 * 1024
UPLOAD_EXTENSIONS = ['.jpg', '.png', '.gif']
PRODUCT_UPLOAD_PATH = 'uploads/products/'
Here is the solution I found that makes this work. Thanks for the help from everyone!
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
#This string does not work
#PRODUCT_UPLOAD_PATH = 'uploads/products/'
#This is the proper way to configure file paths
PRODUCT_UPLOAD_PATH = os.path.join(basedir, 'uploads', 'products')
This question already has answers here:
How to serve static files in Flask
(24 answers)
Link to Flask static files with url_for
(2 answers)
Closed 4 years ago.
I have created a flask app that accesses images in a folder specified in a config file. For some reason, when I specify the full path to the folder (img_folder: '/user/userName/static/img/') I get a 404 error but when I specify the relative path (img_folder: './static/img/') it works. Looking at my terminal, the 404 error specifies the correct path and file, so the path is certainly correct.
What is going on? How can I use the full path without receiving a 404.
This is what it looks like when I specify the full path in the config folder:
img_folder : '/user/userName/static/img/'
This is the HTTP error when I specify the full path in the config:
127.0.0.1 - - [06/Jul/2018 11:26:25] "GET /user/userName/static/img/lion.jpg HTTP/1.1" 404
When I specify the relative path in the config folder like this:
img_folder : './static/img/'
I don't get the 404 error
In my flask app file, I have a route that uses the files from the folder as such:
files = os.listdir(img_folder)
#app.route('/', methods=['GET', 'POST'])
def init():
return render_template('display.html', file=files[0])
display.html
<!doctype html>
<div id='img_area'>
<img src="{{file}}"></img>
</div>
</html>
So the file is not being displayed when the full path is used, but is being displayed with the relative path.
I am running a Flask app where the user uploads a file and must select the root folder path of where to upload the file on a network drive. This path is an IIS available network path and is also a network drive on all user's computers.
I want to dynamically show the available folders in HTML, even if new folders are created after the app starts.
I know this can't be done with pure HTML due to security but wanted to know if there was a way around this with Flask. The goal is to use Python to move the upload file to the choosen folder path.
I have tried:
<form><input type="file" name=dir webkitdirectory directory multiple/></form>
But this only works in Chrome. With the path choosen by the user I can pass this onto Python to copy the upload file to there.
Due to modern browser limitations I decided to use JSTree as a solution. And it is working very well. It features a tree structure browser. The structure is the result of outputting the folders as JSON. You can add a search bar as well so the user can just type in a folder name to search.
Please see JSTree https://www.jstree.com/
How to implement this with Flask
HTML/JS:
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css">
<div>
<input class="search-input form-control" placeholder="Search for folder"></input>
</div>
<script id="jstree1" name="jstree1">
/*Search and JS Folder Tree*/
$(function () {
$(".search-input").keyup(function () {
var searchString = $(this).val();
console.log(searchString);
$('#container').jstree('search', searchString);
});
$('#container').jstree({
'core': {
"themes": {
"name": "default"
, "dots": true
, "icons": true
}
, 'data': {
'url': "static/JSONData.json"
, 'type': 'GET'
, 'dataType': 'JSON'
}
}
, "search": {
"case_insensitive": true
, "show_only_matches": true
}
, "plugins": ["search"]
});
});
{ /* --- THIS IS FOLDER SELECTOR FOR ID "folderout" --- */
$("#container").on("select_node.jstree", function (evt, data) {
var number = data.node.text
document.getElementById("folderout").value = number;
});
In Flask/WTForms call on the id "folderout". This will return the path to WTForms when the user clicks the folder.
folderout = TextField('Folder:', validators=[validators.required()])
To Create the JSON JStree File using Python:
import os
# path : string to relative or absolute path to be queried
# subdirs: tuple or list containing all names of subfolders that need to be
# present in the directory
def all_dirs_with_subdirs(path, subdirs):
# make sure no relative paths are returned, can be omitted
path = os.path.abspath(path)
result = []
for root, dirs, files in os.walk(path):
if all(subdir in dirs for subdir in subdirs):
result.append(root)
return result
def get_directory_listing(path):
output = {}
output["text"] = path.decode('latin1')
output["type"] = "directory"
output["children"] = all_dirs_with_subdirs(path, ('Maps', 'Reports'))
return output
with open('test.json', 'w+') as f:
listing = get_directory_listing(".")
json.dump(listing, f)
Python runs on your server, therefoere it will not be possible to use that to move the files on the client side. If you think about it, let's assume you manage to somehow (magically) send python commands to the clients to move files, do you know if they even have python installed to be able to interpret your commands?
Javascript on the other hand is running on client side and was used to achieve this. However, like you said, due to security reasons modern browswers won't allow that. If they would allow it then any website could potentially see your whole File System.
Here is an article that explains a bit why. Look up the File Upload Control section of it. Hope this makes things a bit clearer.
EDIT: after seeing your comment you could achieve that using os.walk. Beware it could be slow.
for root, dirs, files in os.walk(rootPath): # for example "C:/Users/"
for file in files:
if file == (wantedFile):
print(os.path.join(root,file))
break
I followed the instructions from How to serve static files in Flask, but still couldn't get it working.
Here's my project structure:
Project_path
|
+--app
| |
| +--main.py
+--static
|
+--js
|
+--jquery-1.11.2.min.js
Here's main.py:
#app.route('/js/<path:path>')
def serve_static(path):
root_dir = os.path.dirname(os.getcwd())
print(os.path.join(root_dir, 'static', 'js', path))
return app.send_static_file(os.path.join(root_dir, 'static', 'js', path))
Here's index.html:
...
<script type="text/javascript" src="/js/jquery-1.11.2.min.js"></script>
...
And when I visit /, I could see the correct path of javascript file printed on the screen
which is Project_path/static/js/jquery-1.11.2.min.js.
But still, I got
127.0.0.1 - - [22/Dec/2014 11:26:30] "GET /js/jquery-1.11.2.min.js HTTP/1.1" 404 -
Any help is appreciated.
EDIT
After stepping through the send_static_file method, I find out what's going on. Basically, I shouldn't use abspath as argument, flask has a judgement in send_static_file:
if os.path.isabs(filename) or \
filename == '..' or \
filename.startswith('../'):
raise NotFound()
And since the filename I passed into is a abspath, flask raise NotFound().
It seems that what it supposed to be passed in is a relative path to self.static_folder(self is <Flask 'main'>), which, in my project, is Project_name/app/static. However, I didn't set static_folder myself which means flask thinks the static folder should be there.
I'm still trying to figure out what to do.
Finally got it working. use flask.send_from_directory
from flask import send_from_directory
#app.route('/js/<path:filename>')
def serve_static(filename):
root_dir = os.path.dirname(os.getcwd())
return send_from_directory(os.path.join(root_dir, 'static', 'js'), filename)
It is now clear to me that flask really hate people putting app.py or in my case main.py into a subdirectory. Use send_static_file only if your static folder is what flask thinks to be, i.e. a folder with name static in the same directory with app.py.
All you need to do is, pass the static_folder parameter to the initiator:
static_url_path – can be used to specify a different path for the
static files on the web. Defaults to the name of the static_folder
folder.
static_folder – the folder with static files that should be served at
static_url_path. Defaults to the 'static' folder in the root path of
the application.
app = Flask(__name__, static_folder=os.path.abspath('/foo/bar/zoo/'))
Now, flask will look for a directory named static in /foo/bar/zoo from where to serve static files. You only use send_from_directory if you are serving media files which may not be in the same location as static files.
You forgot to add 'static' in the last os.path.join in the return clause.
for me this one worked :
#app.route('/static/<path:filename>')
def serve_static(filename):
root_dir = os.path.dirname(os.getcwd())
return send_from_directory(os.path.join(root_dir, 'static', 'js'), filename)
beside adding this script into init
app._static_folder = os.path.abspath("static/")
app = Flask(__name__)
into __init__.py
One possible cause of 404 error for pages you just added (even if programmed correctly), is if you have previous versions of the script (.py file) still running: make sure to close out and end (terminate) the process.
In my view callable, I want users to be able to create a new file called filename like so:
#view_config(route_name='home_page', renderer='templates/edit.pt')
def home_page(request):
if 'form.submitted' in request.params:
name= request.params['name']
input_file=request.POST['stl'].filename
vertices, normals = [],[]
for line in input_file:
parts = line.split()
if parts[0] == 'vertex':
vertices.append(map(float, parts[1:4]))
elif parts[0] == 'facet':
normals.append(map(float, parts[2:5]))
ordering=[]
N=len(normals)
...parsing data...
data=[vertices,ordering]
jsdata=json.dumps(data)
renderer_dict = dict(name=name,data=jsdata)
app_dir = request.registry.settings['upload_dir']
filename = "%s/%s" % ( app_dir , name )
html_string = render('tutorial:templates/view.pt', renderer_dict, request=request)
with open(filename,'w') as file:
file.write(new_comment)
return HTTPFound(location=request.static_url('tutorial:pages/%(pagename)s.html' % {'pagename': name}))
return {}
right now, when I attempt to upload a file, I am getting this error message: IOError: [Errno 2] No such file or directory: u'/path/pages/one' (one is the name variable) I believe this is because I am incorrectly defining the app_dir variable. I want filename to be the url of the new file that is being created with the name variable that is defined above (so that it can be accessed at www.domain.com/pages/name). Here is the file structure of my app:
env
tutorial
tutorial
templates
home.pt
static
pages
(name1)
(name2)
(name3)
....
views.py
__init__.py
In my init.py I have:
config.add_static_view(name='path/pages/', path=config.registry.settings['upload_dir'])
In my development.ini file I have
[app:main]
use = egg:tutorial
upload_dir = /path/pages
Edit: If anyone has an idea on why this question isn't getting much attention, I would love to hear it.
While I feel like you probably have a misunderstanding of how to serve up user-generated content, I will show you a way to do what you're asking. Generally user-generated content would not be uploaded into your source, you'll provide some configurable spot outside to place it, as I show below.
Make the path configurable via your INI file:
[app:main]
use = egg:tutorial
upload_dir = /path/to/writable/upload/directory
Add a static view that can serve up files under that directory.
config.add_static_view(name='/url/to/user_uploads', path=config.registry.settings['upload_dir'])
In your upload view you can get your app_dir via
app_dir = request.registry.settings['upload_dir']
Copy the data there, and from then on it'll be available at /url/to/user_uploads/filename.