I want to serve a HTML page from a subdirectory (not the 'static' one, via YAML, not the 'template' one, via Django or Jinja)
How can I reference the path + file name in the self.response.out.write(?) statement?
I am not sure I undersood, but I feel like you want to consider HTML files without template.
If so, you may want to try this solution...
You can define the path as follows:
path = os.path.join(os.path.dirname(__file__), 'your_directory/your_html.html')
Then, you can print the content of your HTML like this:
self.response.out.write(template.render(path, 0))
Related
I am stuck with a really weird problem.
I have a file called: "test.txt".
It is in the same directory with views.py. But I can't read it... FileNotFoundError.
But if I create read_file.py in the same directory with views.py and test.txt, it works absolutely fine.
What is wrong with views? Is this some sort of restriction by Django?
This code works on read_file, doesn't work on views.py:
fkey = open("test.txt", "rb")
key = fkey.read()
I think the problem may be relative vs absolute file handling. Using the following Python snippet, you can work out where the interpreter's current working directory is:
import os
print(os.getcwd())
You should notice it's not in the same directory as the views.py, as views.py is likely being invoked/called from another module. You may choose to change all your open calls to include the whole path to these files, or use an implementation like this:
import os
# This function can be used as a replacement for `open`
# which will allow you to access files from the file.
def open_relative(path, flag="r"):
# This builds the relative path by joining the current directory
# plus the current filename being executed.
relative_path = os.path.join(os.path.dirname(__file__), path)
return open(relative_path, flag) # return file handler
Then, this should work:
fkey = open_relative("test.txt", "rb")
key = fkey.read()
Hope this helps!
Using flask. I have made an internal file browser/media player. This is for local network only so everyone who has access to the page has access to these files all ready.
Nevertheless I am dealing with 1000s of files in 1000s of locations. Is it possible to source a file in a html video player, or a img src that is local. The source files cant be moved, so cant go to the static folder etc...
like this
<video src="{{ clip }}" autoplay controls></video>
when clip is the file_path /projects/project_234/video/video_file.mov
I have all the variables needed just not able to get the file to play.
EDIT 01
It has come to my attention that mov files dont play in chrome only mp4's.
#app.route('/projects/<project>/<clip>', methods=['GET'])
def project_page_clip(project, clip):
file_path = request.args.get('file_path')
file_location = file_path
file_name = '90Sec_Approval.mp4'
if file_name:
return send_from_directory(file_location,file_name)
return render_template("project_selected_clip.html", file_path=file_path,
title=project, project=project, clip=clip)
So when clicked on the previous page this just opens the clip on a browser without rendering the project_selected_clip.html template
How can I get it to use the return send from directory as a src on the page instead?
After much deliberation, the best of many evils was to generate symlinks for the fles
This seems to be a big help
So after that... what works for me is to include this;
from Flask import send_file
top_dir = 'top/'
mid_dir = 'mid/'
more_dir = '/and/this/'
filename = 'some_photo.jpg'
#wip.route('/photo')
def photo():
return send_file(top_dir+mid_dir+more_dir+filename)
serves up the file!
Here is an answer to clarify how to use the send_file approach in an app setting with app.route and a url_for.
import os
from flask import Flask, url_for, send_file
app = Flask(__name__)
# set the path where you store your assets - it can be outside of the root dir
app.config['CUSTOM_STATIC_PATH'] = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../archive'))
# have a route listening for requests
#app.route('/this/can/be/anything/<path:filename>')
def send_media(filename):
path = os.path.join(app.config['CUSTOM_STATIC_PATH'], filename)
return send_file(path)
# in your template, you can then use the following
# <img src="{{ url_for('send_media', filename='<dir>/<filename>') }}">
I am doing content linking check on user's upload zip file with Python's zipfile and BeautifulSoup module.
In the zip file, there is a file "a.html" and its full path in the zip file is "content/product1/component1/a.html". File 'a.html' has a <a href="../../product2/component2/b.html"> link to another HTML file.
I want to know how to combine the path "content/product1/component1/a.html" with "../../product2/component2/b.html" and get the right path which is "content/product2/component2/b.html". So I can check where this file exists.
I tried os.path.join("content/product1/component1/a.html","../../product2/component2/b.html), but I don't get "content/product2/component2/b.html". Does anyone know how to do that?
You need to extract the path component from "content/product1/component1/a.html", join that to the "../../product2/component2/b.html" href, and then normalize the result.
import os.path
src = "content/product1/component1/a.html"
srcdir = os.path.dirname(src)
href = "../../product2/component2/b.html"
url = os.path.normpath(os.path.join(srcdir, href))
print(url)
output
content/product2/component2/b.html
You might want to try using str.split() (with / as the separator) and then use os.path.join() on the parts you need.
I'd like Django to serve some media files (e.g. user-uploaded files) only for logged-in users. Since my site is quite low-traffic, I think I will keep things simple and do not use django-sendfile to tell Nginx when to serve a file. Instead I'll let Django/Gunicorn do the job. To me this seems a lot simpler and for a low traffic site this maybe more secure.
But what is the best way to organize the file storage location? Media files are all stored below MEDIA_ROOT and this directory is served by Nginx in production. If I upload my files to MEDIA_ROOT/protected/ I have to tell Nginx not to serve the files in the subdirectory protected.
But is this a good idea? It seems a litte risky to me to allow Nginx access /media/ in the first place and then protect the subdirectory /media/protected/. Wouldn't it be better not to use a subdirectory of MEDIA_ROOT to store protected files?
But if I try something like this quick-and-dirty in my model:
upload_to='../protected/documents/%Y/%m/'
Django complains:
SuspiciousFileOperation at /admin/core/document/add/
The joined path (/home/me/projects/project/protected/documents/2016/09/test.file) is located outside of the base path component (/home/me/projects/project/media)
So I thing it is not good practice to "leave" the MEDIA_ROOT.
What is the best solution to store and serve protected media files?
Serving media files ( that may be large files) from view directly is not good. You can use sendfile extension available in nginx server; a sample nginx configuration is like below.
location /projects/project/media/{
# this path is not public
internal;
# absolute path
alias /projects/project/media/;
}
change your view to
#login_required
def serve_protected_document(request, file):
document = get_object_or_404(ProtectedDocument, file="protected/documents/" + file)
# Split the elements of the path
path, file_name = os.path.split(file)
response = HttpResponse()
response["Content-Disposition"] = "attachment; filename=" + file_name
# nginx uses this path to serve the file
response["X-Accel-Redirect"] = document.name # path to file
return response
Link: More details on configuring sendfile extension on nginx is here
I now came up with the following solution:
I have this in my Django settings:
MEDIA_ROOT = "/projects/project/media/"
MEDIA_URL = "/media/
In my models I do either:
document = models.FileField(upload_to="public/documents")
or
document = models.FileField(upload_to="protected/documents")
This way, I now have the two subdirectories 'public' and 'protected' in my media files directory.
Nginx or Djangos development server only serves the files in the 'public' subdirectory.
For Djangos development server:
if os.environ["ENVIRONMENT_TYPE"] == 'development':
urlpatterns += static(settings.MEDIA_URL + "public/", document_root=settings.MEDIA_ROOT + "public/")
And for Nginx (used in production):
location /media/public/ {
alias /projects/project/media/public/;
}
When I want to serve a protected document, I do the following:
In urls.py:
url(r'^media/protected/documents/(?P<file>.*)$', core.views.serve_protected_document, name='serve_protected_document'),
And in views.py:
#login_required()
def serve_protected_document(request, file):
document = get_object_or_404(ProtectedDocument, file="protected/documents/" + file)
# Split the elements of the path
path, file_name = os.path.split(file)
response = FileResponse(document.file,)
response["Content-Disposition"] = "attachment; filename=" + file_name
return response
I would appreciate any comments! Are there better ways to implement this?
I'm using flask.send_file to send images to my client.
However, when I was trying to get the url of a image using url_for, I found the result returned is not the same with the image's path.
def get(self):
print url_for('static', filename='test.jpg')
filename = 'static/img/test.jpg'
return send_file(filename)
I got
/static/test.jpg
in the console.
The file structure is
└── static
└── img
└── test.jpg
Why the url is not /static/img/test.jpg?
EDIT
And when I request from the browser, the code above works well. On the contrast, the browser return error if I change the filename to static/test.jpg.
Because you've told it to print the URL for "test.jpg" inside "static". Since you haven't included any reference to "img", there's no way that url_for can include it.
Note that this has nothing to do with the actual URL you return from the function.
Because you should give url_for the full path to your image relative to the static folder.
So I think in your case it would be
url_for('static', filename='img/test.jpg')
url_for will just concatenate the path you give it to the static folder path, so to generate a URI to that file, but it won't magically map or guess the location of your files.