Render default image in flask - python

I'm using url_for to generate my images url.
<img src="{{ url_for('static', filename='imgs/' + images[id]) }}" >
How to make url_for return /static/imgs/default.jpg when the requested image does not exist on the server ?

Solution 1: HTML / JavaScript
You can use onerror attribute:
<img src="{{ url_for('static', filename='imgs/' + images[id]) }}" onerror="this.src='/static/imgs/default.jpg'">
Or listen error event for img element with JavaScript (jQuery):
$('img').on("error", function() {
$(this).attr('src', '/static/imgs/default.jpg');
});
Solution 2: Flask
If you just want make it with Flask, you will need to create a custom view function to handle your images, for example (not test yet):
import os
from flask import send_from_directory
# ...
#app.route('/img/<path:filename>')
def get_image(filename):
static_path = os.path.join(app.root_path, 'static')
img_path = os.path.join(static_path, filename)
if not os.path.exists(img_path):
return send_from_directory(os.path.join(static_path, '/imgs/default.jpg'))
return send_from_directory(img_path)
Template code:
<img src="{{ url_for('get_image', filename='/static/imgs/' + images[id]) }}" >
Solution 3: Nginx
In production, you may use web server like Nginx to serve images, in this case, you can use [try_files](http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files) directive in Nginx:
location /static/imgs/ {
try_files $uri /static/imgs/default.jpg;
}

Related

Android emulator how to display image to remote destination in flask

I was referring to this github https://github.com/ahmedfgad/AndroidFlask and https://heartbeat.comet.ml/uploading-images-from-android-to-a-python-based-flask-server-691e4092a95e. I was unable to display the images into the remote destination from android emulator. I dont think the source code that they provide, do display images to the remote destination using flask.
Below is my github. It shows all the changes I have made.
https://github.com/2100723/AndroidEmulator/tree/main. I have made changes to the flask_server.py and images_template.html which is located in the templates folder . I need in the image to display in the remote host.
My android emulator has managed to save my images from emulator in the file call static whenever press connect to server & upload enter image description here. All I left is to send my image to a remote destination.
#Flask_server.py
import flask
import werkzeug
import time
app = flask.Flask(__name__, static_folder='static')
#app.route('/', methods = ['GET', 'POST'])
def handle_request():
image_paths = []
files_ids = list(flask.request.files)
print("\nNumber of Received Images : ", len(files_ids))
image_num = 1
for file_id in files_ids:
print("\nSaving Image ", str(image_num), "/", len(files_ids))
imagefile = flask.request.files[file_id]
filename = werkzeug.utils.secure_filename(imagefile.filename)
print("Image Filename : " + imagefile.filename)
timestr = time.strftime("%Y%m%d-%H%M%S")
file_path = timestr+'_'+filename
imagefile.save(file_path)
image_paths.append(filename)
image_num = image_num + 1
print("\n")
return flask.render_template('image_template.html', images=image_paths)
app.run(host="0.0.0.0", port=5000, debug=True)
#image_template.html
<!doctype html>
<html>
<head>
<title>Image Gallery</title>
</head>
<body>
<h1>Image Gallery</h1>
{% for image in images %}
<img src="{{ url_for('static', filename=image) }}" alt="image" width="200">
{% endfor %}
</body>
</html>
I try changing my Flask_server.py code and adding image_template.html to make my code to display to the remote server using flask. Nothing is display in the remote destination using flask

FastAPI serving static files through symlinks

I have mounted the static directory in my FastAPI app using the following code:
from fastapi.staticfiles import StaticFiles
app = FastAPI(
title="Title of the Application",
description="Over all description of the application")
app.mount("/public", StaticFiles(directory='public'), name='public')
If I have a symlink pointing to a path outside the app folder, e.g.
/home/xyz/app/main.py
/home/xyz/app/index.html
/home/xyz/app/public/data -> /home/xyz/static/whatever.tgz
The FastAPI application can recognize the URL xyz.com/public/index.html, but it can't recognize xyz.com/public/data.
Is this doable? Unfortunately, I cannot use FileResponse due to the blob size being too large. I want to return the file with a simple link somehow.
It is doable, as long as you mount a StaticFiles instance on that specific path as well. For example:
app.mount("/public", StaticFiles(directory="public"), name="public")
app.mount("/publicsym", StaticFiles(directory="public/data"), name="publicsym")
Then in your Jinja2 template you can requesst the files as below:
<link href="{{ url_for('public', path='/styles.css') }}" rel="stylesheet">
<img src="{{ url_for('publicsym', path='/image.png')}}" width="50%">
or, as per your given example (if there is a "static" directory including a "whatever.tgz" file):
{{ url_for('publicsym', path='static/whatever.tgz')}}

Equivalent to wildcards in Flask's url_for?

I am developing a Flask application, and have it set so that my app's CSS file is generated with a random name via this build script:
rm style/main-*.css;
KEY=`tr -dc A-Za-z0-9 </dev/urandom | head -c 6`;
postcss build ./style/src/main.css -o ./style/main-$KEY.css
This is mostly for cache-related reasons; but it regardless results in a name with the wildcard pattern main-*.css
Since this name is randomized every build, I do not want to edit the template I include it in. Unfortunately, wildcards do not appear to work in Flask, as this:
<link rel='stylesheet" href='{{ url_for('static', filename='style/main-*.css') }}'>
… does nothing (except, of course, yield a 404).
Is there a way to do this kind of behavior via Flask? Thank you.
You can write your own function and add it to the jinja2 environment as a global function.
import os
from glob import glob
from flask import current_app
def glob_assets(target):
root = current_app.static_folder
return [f[len(root)+1:] for f in glob(os.path.join(root, target))]
app.jinja_env.globals.update(get_assets=glob_assets)
Then you can add the stylesheets to the template within one iteration.
{% for css in get_assets('style/main-*.css') -%}
<link rel="stylesheet" href="{{ url_for('static', filename=css) }}">
{% endfor -%}

Dynamically update image using Python Flask AJAX

I have 1 very simple web application I am building right now but am very new to flask and jinja (and web development as a whole actually).
I have a watch folder, which will be getting an image sent to it via ftp on a pulse for ever. This wtch folder will only ever have one image in. Every 1 minute, the old image is replaced by a new image, with a new timestamp.
I would like to dynamically update the page, (and displayed timestamp) on a pulse as well, without having to reload any banners or static images that I will add later. I only want to update the following two lines out of the "Channels.Jinja" sample to follow.
<br>{{screenshot_datetime}}<br/>
<img src={{screenshot_location}} width="100%"/>
Channels.Jinja
<!DOCTYPE HTML>
<html>
<head>
<title>Training</title>
</head>
<body bgcolor=white>
<div id=main>
<br>Date and Time of Screenshot <br/>
<br>{{screenshot_datetime}}<br/>
<img src={{screenshot_location}} width="100%"/>
</div>
<div id='test'>
<p>
<script>
var myVar=setInterval(function(){get_image()},1000);
function get_image() {
$.ajax({
type: 'GET',
cache: false,
url: 'get_data',
success: function({{data}}) {
$('img').attr('src', data);
}
});
}
</script>
</p>
</div>
</body>
</html>
Channels.py
def render_channel_route(cr):
static_folder = os.path.join('static',cr)
file_list = os.listdir(static_folder)
channel_files = [f for f in file_list if f.startswith(cr)]
if not channel_files :
logger.error('Could not find image file for Channel. File should start with {0}'.format(cr))
abort(404)
img = os.path.join(static_folder,file_list[0])
ts = get_time_from_filename(file_list[0],cr)
return render_template('Channels.jinja',screenshot_datetime=time.strftime('%c',ts),screenshot_location=img)
#app.route('/channel01-10')
def first_tab():
return render_channel_route('channel01-10')
#app.route('/get_data', methods=['GET'])
def get_data():
return render_template('Channels.jinja',
screenshot_datetime=time.strftime('%c',ts),screenshot_location=img)
Im at a loss, Ive been bumbling around for a while now. Any and all advice is welcome! I am seeing a 304 response upon refresh, but not even the timer i am trying to put on it is working. Pardon sloppy code, highly volatile code is getting changed often -_-
I don't know it there is a "special" way to deal with Ajax using some Flask extension, but in the "normal" Ajax flow first you need to use url_for to put the correct url in your Ajax call and return the data formatted in some way (in my example in JSON) and not to render the template again:
$.ajax({
type: 'GET',
cache: false,
url: "{{ url_for('get_data') }}",
success: function(resp){
$('img').attr('src', resp.url);
$('#sst').html(resp.time);
}
});
So, in your get_data function in your controller you have to get the time and the path again for your image an then return some like this (to fit in my example before):
from flask import json
#app.route('/get_data', methods=['GET'])
def get_data():
#get time and path
time=...
path=...
return json.dumps({time:time,url:path}), 200, {'Content-Type':'application/json'}
Look that I use $('#sst') so you have to put in your HTML:
<br><span id='sst'>{{screenshot_datetime}}</span><br/>

Difficulty accessing json file with d3 and flask

I am using Flask as a web framework, and I am trying to implement the first example from the book Getting Started with D3, by Mike Dewar. I have a Python script named run.py and two directories, templates/ and static/, containing index.html and service_status.json, respectively. Unfortunately, my code is not rendering the data at all, nor is it producing any glaring errors.
This is what I have in run.py:
#!/usr/bin/env python
from flask import Flask, render_template, url_for
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
if __name__=="__main__":
port = 5000
app.debug = True
app.run( port=port )
This is what I have in templates/index.html:
<!DOCTYPE HTML>
<HTML>
<HEAD>
<META CHARSET="utf-8">
<SCRIPT SRC="http://d3js.org/d3.v3.min.js"></SCRIPT>
<SCRIPT>
function draw(data) {
"use strict";
d3.select("body")
.append("ul")
.selectAll("li")
.data(data)
.enter()
.append("li")
.text( function(d){
return d.name + ": " + d.status;
}
);
}
</SCRIPT>
<TITLE>MTA Data</TITLE>
</HEAD>
<BODY>
<H1>MTA Availability Data</H1>
<SCRIPT>
d3.json("{{ url_for( 'static', filename='service_status.json') }}",draw); // <---- BIG PROBLEM
</SCRIPT>
</BODY>
</HTML>
I am using Windows 7, Google Chrome, and Python 2.7.
If the JSON file is not going to change, then you should put it in the static directory and use
from flask import url_for
url_for('static', filename='service_status.json')
For this to work, also change the path in the JavaScript to '/static/service_status.json'
Static files like your json document, are by default served from a different directory from the templates - by default 'static'
You dont need to use the url_for call in your view, you can use it in your template:
d3.json("{{ url_for('static', filename='service_status.json') }}",draw);
So to summarise: 1) Move your json document in to the static folder (a folder called static along side your templates folder, by default), and 2) use the url_for call in your template to get the correct URI for your json document.
If you want to use a folder other than static, you can change that by passing static_folder to the Flask object contructor
You seem to be getting a 304 status code as you mentioned in earlier comments. I see that your JSON has the following date/time:
"Date": [
"12/15/2011"
],
"Time": [
" 7:35AM"
],
I am not 100% sure but this might help:
http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html#if-modified-since
Basically, it says that
"This request header is used with GET method to make it conditional: if the requested document has not changed since the time specified in this field the document will not be sent, but instead a Not Modified 304 reply.
Format of this field is the same as for Date:"
So, may be you can check the timestamp on the JSON and probably just do a fresh save ?

Categories