I know how to serve static files in FastAPI using StaticFiles, how can I enable
directory listing like in Apache web server?
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI(...)
app.mount("/samples", StaticFiles(directory='samples'), name="samples")
# GET http://localhost:8000/samples/path/to/file.jpg -> OK
# GET http://localhost:8000/samples -> not found error
I am not sure FastAPI/Starlette exposes functionality to do that out of the box.
But it shouldn't be too hard to implement something yourself. This could be a starting point:
import os
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
#app.get("/static", response_class=HTMLResponse)
def list_files(request: Request):
files = os.listdir("./static")
files_paths = sorted([f"{request.url._url}/{f}" for f in files])
print(files_paths)
return templates.TemplateResponse(
"list_files.html", {"request": request, "files": files_paths}
)
list_files.html
<html>
<head>
<title>Files</title>
</head>
<body>
<h1>Files:</h1>
<ul>
{% for file in files %}
<li>{{file}}</li>
{% endfor %}
</ul>
</body>
</html>
When you hit /static you would see:
Additional notes
Note that list_files.html is an html template and should sit in the directory templates/ as #Michel Hua pertinently mentioned in his comment. For more info on templates check the templates docs.
Using the code below,
class MainHandler(webapp.RequestHandler):
def get(self):
upload_url = blobstore.create_upload_url('/upload')
self.response.out.write('<html><body>')
self.response.out.write('<form action="%s" method="POST" enctype="multipart/form-data">' % upload_url)
self.response.out.write("""Upload File: <input type="file" name="file"><br> <input type="submit"
name="submit" value="Submit"> </form></body></html>""")
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file') # 'file' is file upload field in the form
blob_info = upload_files[0]
self.redirect('/serve/%s' % blob_info.key())
class ServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
resource = str(urllib.unquote(resource))
blob_info = blobstore.BlobInfo.get(resource)
self.send_blob(blob_info)
app = webapp.WSGIApplication(
[('/', MainHandler),
('/upload', UploadHandler),
('/serve/([^/]+)?', ServeHandler),
], debug=True)
if __name__ == '__main__':
run_wsgi_app(app)
I was able to read files like .pdf,.txt, and media files from my blob store. But files like .doc,.docx returns files that are not readable.
I have tried using blob_reader but still did not work, how do I get to read files like .doc and .docx?
Your browser cannot handle doc files. But you can download the file and open the file with a viewer.
To download a blob, use:
self.send_blob(blob_info,save_as=True)
or:
self.send_blob(blob_info,save_as='amsdoc.docx')
What is wrong with this code? I can not download a blob following the download url sent as response of the UploadHandler. I am getting a 404 response from the server.
I have doubts about how to send an url safe version of the blob key.
import urllib
import webapp2
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
MAIN = """<html>
<body>
<form action="%s" method="POST" enctype="multipart/form-data">
<p>Upload File:<input type="file" name="file"></p>
<p><input type="submit" name="submit" value="Submit">
</form>
</body>
</html>
"""
DOWNLOAD = """<html><body><p>%s</p></body></html>"""
class MainHandler(webapp2.RequestHandler):
def get(self):
upload_url = blobstore.create_upload_url('/upload')
self.response.out.write(MAIN % upload_url)
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file') # 'file' is name field in the form
blob_info = upload_files[0]
_key = blob_info.key()
_url = '/download/%s' % str(_key)
_url_text = blob_info.filename
self.response.out.write(DOWNLOAD % (_url, _url_text))
class DownloadHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
blob_info = blobstore.Blob.get(resource)
self.sendblob(blob_info)
app = webapp2.WSGIApplication([('/', MainHandler),
('/upload', UploadHandler),
('/download/<resource>', DownloadHandler)],
debug=True)
app.yaml file is
application: georef
version: 1
runtime: python27
api_version: 1
threadsafe: false
libraries:
- name: webapp2
version: latest
handlers:
- url: /.*
script: georef.app
It looks like you copy and pasted the code wrong from the docs:
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
class ServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
resource = str(urllib.unquote(resource))
blob_info = blobstore.BlobInfo.get(resource)
self.send_blob(blob_info)
You're missing the line to unencode the resource string in case it has any strange characters in it.
I'm trying to make a simple app on GAE that allows a user to enter a url to an image and a name. The app then uploads this image to the Datastore along with its name.
After the upload the page self redirects and then should send the image back to the client and display it on their machine.
After running all I get is a Server error. Since I am new to GAE please could someone tell me if my code is at least correct.
I can't see what is wrong with my code. (I have checked for correct indentation and whitespace). Below is the code:
The python:
import jinja2 # html template libary
import os
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
import urllib
import urllib2
import webapp2
from google.appengine.ext import db
from google.appengine.api import urlfetch
class Default_tiles(db.Model):
name = db.StringProperty()
image = db.BlobProperty(default=None)
class MainPage(webapp2.RequestHandler):
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render())
class Upload(webapp2.RequestHandler):
def post(self):
# get information from form post upload
image_url = self.request.get('image_url')
image_name = self.request.get('image_name')
# create database entry for uploaded image
default_tile = Default_tiles()
default_tile.name = image_name
default_tile.image = db.Blob(urlfetch.Fetch(image_url).content)
default_tile.put()
self.redirect('/?' + urllib.urlencode({'image_name': image_name}))
class Get_default_tile(webapp.RequestHandler):
def get(self):
name = self.request.get('image_name')
default_tile = get_default_tile(name)
self.response.headers['Content-Type'] = "image/png"
self.response.out.write(default_tile.image)
def get_default_tile(name):
result = db.GqlQuery("SELECT * FROM Default_tiles WHERE name = :1 LIMIT 1", name).fetch(1)
if (len(result) > 0):
return result[0]
else:
return None
app = webapp2.WSGIApplication([('/', MainPage),
('/upload', Upload),
('/default_tile_img', Get_default_tile)],
debug=True)
The HTML:
<html>
<head>
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
</head>
<body>
<form action="/upload" method="post">
<div>
<p>Name: </p>
<input name="image_name">
</div>
<div>
<p>URL: </p>
<input name="image_url">
</div>
<div><input type="submit" value="Upload Image"></div>
</form>
<img src="default_tile_img?{{ image_name }}">
</body>
</html>
Any help at all will be so much appreciated. Thanks you!
UPDATE
Thanks to Greg, I know know how to view error logs. As Greg said I was missing a comma, I have updated the code above.
The app now runs, but when I upload an image, no image shows on return. I get the following message in the log:
File "/Users/jamiefearon/Desktop/Development/My Programs/GAE Fully functional website with css, javascript and images/mywebsite.py", line 53, in get
default_tile = self.get_default_tile(name)
TypeError: get_default_tile() takes exactly 1 argument (2 given)
I only passed one argument to get_default_tile() why does it complain that I passed two?
You're missing a comma after ('/upload', Upload) in the WSGIApplication setup.
use this python code
import jinja2 # html template libary
import os
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
import urllib
import urllib2
import webapp2
from google.appengine.ext import db
from google.appengine.api import urlfetch
class Default_tiles(db.Model):
name = db.StringProperty()
image = db.BlobProperty(default=None)
class MainPage(webapp2.RequestHandler):
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render())
class Upload(webapp2.RequestHandler):
def post(self):
# get information from form post upload
image_url = self.request.get('image_url')
image_name = self.request.get('image_name')
# create database entry for uploaded image
default_tile = Default_tiles()
default_tile.name = image_name
default_tile.image = db.Blob(urlfetch.Fetch(image_url).content)
default_tile.put()
self.redirect('/?' + urllib.urlencode({'image_name': image_name}))
class Get_default_tile(webapp2.RequestHandler):
def get_default_tile(self, name):
result = db.GqlQuery("SELECT * FROM Default_tiles WHERE name = :1 LIMIT 1", name).fetch(1)
if (len(result) > 0):
return result[0]
else:
return None
def get(self):
name = self.request.get('image_name')
default_tile = self.get_default_tile(name)
self.response.headers['Content-Type'] = "image/png"
self.response.out.write(default_tile)
app = webapp2.WSGIApplication([('/', MainPage),
('/upload', Upload),
('/default_tile_img', Get_default_tile)],
debug=True)
I'm getting a TemplateNotFound error on Google App Engine with Jinja2 (complete stack trace below.)
What I expect to see is the index.html with the "greet" variable passed to the index.html template file. What I don't understand is why I get the template not found error when the path to index.html in the TraceBack is correct.
What I've tried...
tried a relative path by taking out "os.path.dirname(file)" in template path.
using "template" instead of themes as a directory name.
Here is my code.
app.yaml
application: codemywayout
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /admin/.*
script: admin.app
login: admin
- url: /static/([^/]+)/(.*)
static_files: template/\1/static/\2
upload: static/.*
- url: /favicon\.ico
static_files: favicon.ico
upload: favicon\.ico
- url: .*
script: static.app
builtins:
- remote_api: on
libraries:
- name: webapp2
version: "2.5.1"
- name: jinja2
version: latest
admin.py
from google.appengine.ext import db
import webapp2
import jinja2
import os
import fix_path
import config
def render_template(template_name, template_vals=None, theme=None):
template_path = os.path.join(os.path.dirname(__file__) , \
"themes", theme or config.theme, template_name)
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(template_path))
return env.get_template(template_path, template_vals or {})
class BlogPost(db.Model):
title = db.StringProperty()
body = db.StringProperty()
def render(self):
template_vals = {
'config': config,
'post': self,
}
return render_template("post.html", template_vals)
class BaseHandler(webapp2.RequestHandler):
def render_to_response(self, template_name, \
template_vals=None, theme=None):
template_name = os.path.join("admin", template_name)
self.response.out.write(render_template(template_name,\
template_vals, theme))
class AdminHandler(BaseHandler):
def get(self):
greet = "hello"
template_vals = {
'greet': greet
}
self.render_to_response("index.html", template_vals)
config.py
# Name of the blog
blog_name = 'My Blog'
# Selects the theme to use. Theme names correspond to directories under
# the 'themes' directory, containing templates and static content.
theme = 'default'
# Defines the URL organization to use for blog postings. Valid substitutions:
# slug - the identifier for the post, derived from the title
# year - the year the post was published in
# month - the month the post was published in
# day - the day the post was published in
# URL Options
# post_path_format = '/%(year)d/%(month)02d/%(slug)s'
post_path_format = '/%(slug)s'
TraceBack
Traceback (most recent call last):
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 1536, in __call__
rv = self.handle_exception(request, response, e)
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 1530, in __call__
rv = self.router.dispatch(request, response)
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 1278, in default_dispatcher
return route.handler_adapter(request, response)
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 1102, in __call__
return handler.dispatch()
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 572, in dispatch
return self.handle_exception(e, self.app.debug)
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 570, in dispatch
return method(*args, **kwargs)
File "C:\Users\john\webdev\workspace\codemywayout\admin.py", line 49, in get
self.render_to_response("index.html", template_vals)
File "C:\Users\john\webdev\workspace\codemywayout\admin.py", line 34, in render_to_response
template_vals, theme))
File "C:\Users\john\webdev\workspace\codemywayout\admin.py", line 14, in render_template
return env.get_template(template_path, template_vals or {})
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\environment.py", line 719, in get_template
return self._load_template(name, self.make_globals(globals))
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\environment.py", line 693, in _load_template
template = self.loader.load(self, name, globals)
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\loaders.py", line 115, in load
source, filename, uptodate = self.get_source(environment, name)
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\loaders.py", line 162, in get_source
pieces = split_template_path(template)
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\loaders.py", line 33, in split_template_path
raise TemplateNotFound(template)
TemplateNotFound: C:\Users\john\webdev\workspace\codemywayout\themes\default\admin\index.html
I hope your not trying to load the Jinja template from the same path as your static files
static_files: template/\1/static/\2
As that path is not accessible to your application.
I would try and log the path that Jinja is trying to load the template from to help you understand where it is trying to load the template.
If you look at C:\Users\john\webdev\google\lib\jinja2\jinja2\loaders.py line 33 you can probably set a pdb breakpoint import pdb; pdb.set_trace( ) and see what's going on.
I'm using the SDK source distribution on a Mac, but if your code is the same as what I see, it's:
def split_template_path(template):
"""Split a path into segments and perform a sanity check. If it detects
'..' in the path it will raise a `TemplateNotFound` error.
"""
pieces = []
for piece in template.split('/'):
if path.sep in piece \
or (path.altsep and path.altsep in piece) or \
piece == path.pardir:
raise TemplateNotFound(template)
elif piece and piece != '.':
pieces.append(piece)
return pieces
Note the for piece in template.split('/') where it splits on '/' instead of path.sep. I suspect that your input is a C:\Path\to\file.html in which case piece is the entire path, adn path.sep (\ on windows) is indeed in piece, which gives a TemplateNotFound error.
Instead of template_name = os.path.join("admin", template_name) you could try template_name = 'admin/%s' % template_name, don't include template_name in the template_path and pass template_name instead of template_path into env.get_template(template_name, template_vals or {}) as loaders.py line 162 indicates that it looks for template_name in template_path
Your code, modified but untested would look something like
from google.appengine.ext import db
import webapp2
import jinja2
import os
import fix_path
import config
def render_template(template_name, template_vals=None, theme=None):
template_path = os.path.join(os.path.dirname(__file__) , \
"themes", theme or config.theme)
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(template_path))
return env.get_template(template_name, template_vals or {})
class BlogPost(db.Model):
title = db.StringProperty()
body = db.StringProperty()
def render(self):
template_vals = {
'config': config,
'post': self,
}
return render_template("post.html", template_vals)
class BaseHandler(webapp2.RequestHandler):
def render_to_response(self, template_name, \
template_vals=None, theme=None):
template_name = "admin/%s" % template_name
self.response.out.write(render_template(template_name,\
template_vals, theme))
class AdminHandler(BaseHandler):
def get(self):
greet = "hello"
template_vals = {
'greet': greet
}
self.render_to_response("index.html", template_vals)