I have a flask app where using one Flask route the server creates a csv file and saves it to the server. Using a generated button on the client page, another Flask route is triggered to get the most recent file, move it to a tmp folder and send that file to the user using send_file.
Right now, when I run the process the first time and download the file, all works as expected. However, the second time I run the process, it serves me the old CSV instead of the newly generated one. This continues until I hit the refresh button on my browser.
The following is my app code:
from flask import Flask, render_template, flash, redirect, request, url_for, Response, send_file
import os
import time
import shutil
import glob
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
#app.route('/')
def index():
return render_template('index.html')
#app.route('/downloadcsv')
def downloadcsv():
current = os.getcwd()
try:
list = glob.glob('{}/*.csv'.format(current))
except:
print('No file found')
basename = os.path.basename(os.path.normpath(max(list, key=os.path.getctime)))
shutil.move(basename, './tmp/{}'.format(basename))
return send_file('./tmp/{}'.format(basename), as_attachment=True)
In case it is needed, the following is the JS code which "generates" the download button:
var download = '<div id="downloadsection" class="container-contact100-form-btn"><button id="download" class="contact100-form-btn"> <span>DOWNLOAD CSV</span></button></div>';
Please also let me know if I am over complicating the download process...
Thanks!!
send_file has a caching timeout that you are not configuring. It will send the same file that has been cached unless you tell it not to cache the file like so:
send_file('./tmp/{}'.format(basename), as_attachment=True, cache_timeout=0)
See the following references for more information:
http://flask.pocoo.org/docs/1.0/api/#flask.send_file
http://flask.pocoo.org/docs/1.0/api/#flask.Flask.get_send_file_max_age
http://flask.pocoo.org/docs/1.0/config/#SEND_FILE_MAX_AGE_DEFAULT
#ritlew pretty much answers the question, to add to his answer, After adding cache_timeout=0, clear the browser cache and hit the URL in incognito mode.
You can also try:
Disabling caching in Flask
Related
I have a Flask application that shall provide an endpoint to download a large file. However, instead of providing it from the file system or generating the file on-the-fly, this file has to be downloaded first from another server via HTTP.
Of course, I could perform a GET request to the external server first, download the file completely and store it in the file system or in memory and then as a second step provide it as a result for the original request. This would look for example like this (also including a basic authentication to indicate why a simple proxy on a lower layer is not sufficient):
#!flask/bin/python
from flask import Flask, jsonify
import os
import requests
from requests.auth import HTTPBasicAuth
app = Flask(__name__)
#app.route('/download')
def download():
auth = HTTPBasicAuth("some_user", "some_password")
session = requests.Session()
session.auth = auth
response = session.get("http://example.com")
return response.content
if __name__ == '__main__':
app.run(host='0.0.0.0', port=1234, debug=True)
However, this increases both the latency and the storage requirements of the application. And also, even if the receiver only requires to perform a partial download (i.e. it performs a HTTP range request) of the file, it has to be fetched from the external server completely, first.
Is there a more elegant option to solve this, i.e. to provide support for HTTP range requests that are directly forwarded to the external server?
According to Proxying to another web service with Flask, Download large file in python with requests and Flask large file download I managed to make a Flask HTTP proxy in stream mode.
from flask import Flask, request, Response
import requests
PROXY_URL = 'http://ipv4.download.thinkbroadband.com/'
def download_file(streamable):
with streamable as stream:
stream.raise_for_status()
for chunk in stream.iter_content(chunk_size=8192):
yield chunk
def _proxy(*args, **kwargs):
resp = requests.request(
method=request.method,
url=request.url.replace(request.host_url, PROXY_URL),
headers={key: value for (key, value) in request.headers if key != 'Host'},
data=request.get_data(),
cookies=request.cookies,
allow_redirects=False,
stream=True)
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
headers = [(name, value) for (name, value) in resp.raw.headers.items()
if name.lower() not in excluded_headers]
return Response(download_file(resp), resp.status_code, headers)
app = Flask(__name__)
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def download(path):
return _proxy()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=1234, debug=True)
download_file() will open the request in stream mode and yield every chunk as soon as they got streamed.
_proxy() create the request then just create and return a Flask Response using the iterator download_file() as content.
I tested it with https://www.thinkbroadband.com/download where several archive files are free to download for test purpose. (be careful, archives are corrupted, so you better use checksum to make sure you got the expected file).
Some examples:
curl 'http://0.0.0.0:1234/100MB.zip' --output /tmp/100MB.zip
curl 'http://0.0.0.0:1234/20MB.zip' --output /tmp/20MB.zip
I also performed some other tests on random websites to get large images. So far I got no issues.
I am trying to make an API that when a POST request is made, it will accept images and then save them, and if a GET request is made then it will send back a URL to where you'd be able to download that image. How would that be possible?
This is the Flask app running on localhost for testing:
from flask import Flask, send_file
app = Flask(__name__)
#app.route("/upload_image/<filename>", methods=["POST"])
def upload_image(filename):
# receive the image here
# ...
#app.route("/get_image", methods=["GET"])
def get_image():
filename = "encoded_image.png"
return send_file(filename, mimetype="image/png")
if __name__ == "__main__":
app.run(host="localhost", port=5000)
I think sending the image is easy but I don't know how to go about receiving it and using requests to send it.
This is the idea behind the main code:
import requests
option = input()
if option == "get image":
print("Download the image at: https://localhost:5000/get_image")
elif option == "upload image":
post = requests.post("https://localhost:5000/upload_image/encoded_image.png")
i don't want the images to change names. I always want to send the file encoded_image.png and receive it, because I want it to be overwritten when the POST is made. This is for a discord bot but that doesn't really affect anything, I just thought I'd mention it. Thanks in advance :)
this is a two-part question: I have seen individual pieces discussed, but can't seem to get the recommended suggestions to work together. I want to create a web service to store images and their metadata passed from a caller and run a test call from Postman to make sure it is working. So to pass an image (Drew16.jpg) to the web service via Postman, it appears I need something like this:
For the web service, I have some python/flask code to read the request (one of many variations I have tried):
from flask import Flask, jsonify, request, render_template
from flask_restful import Resource, Api, reqparse
...
def post(self, name):
request_data = request.get_json()
userId = request_data['UserId']
type = request_data['ImageType']
image = request.files['Image']
Had no problem with the data portion and straight JSON but adding the image has been a bugger. Where am I going wrong on my Postman config? What is the actual set of Python commands for reading the metadata and the file from the post? TIA
Pardon the almost blog post. I am posting this because while you can find partial answers in various places, I haven't run across a complete post anywhere, which would have saved me a ton of time. The problem is you need both sides to the story in order to verify either.
So I want to send a request using Postman to a Python/Flask web service. It has to have an image along with some metadata.
Here are the settings for Postman (URL, Headers):
And Body:
Now on to the web service. Here is a bare bones service which will take the request, print the metadata and save the file:
from flask import Flask, request
app = Flask(__name__)
# POST - just get the image and metadata
#app.route('/RequestImageWithMetadata', methods=['POST'])
def post():
request_data = request.form['some_text']
print(request_data)
imagefile = request.files.get('imagefile', '')
imagefile.save('D:/temp/test_image.jpg')
return "OK", 200
app.run(port=5000)
Enjoy!
Make sure `request.files['Image'] contains the image you are sending and follow http://flask.pocoo.org/docs/1.0/patterns/fileuploads/ to save the file to your file system. Something like
file = request.files['Image']
file.save('./test_image.jpg')
might do what you want, while you will have to work out the details of how the file should be named and where it should be placed.
I have created a flask application and am hosting it on a Ubuntu server. I know that my apache config is correct since I am able serve the example flask application. However, this one seems to be giving me trouble. The code is below:
from flask import Flask, render_template, request, url_for
import pickle
import engine
import config
# Initialize the Flask application
app = Flask(__name__)
model = pickle.load(open(config.MODEL_PATH, "rb"))
collection = engine.Collection(config.DATABASE_PATH)
search_engine = engine.SearchEngine(model, collection)
#app.route('/')
def form():
return render_template('index.html')
#app.route('/search/', methods=['POST'])
def search():
query = request.form['query']
results = search_engine.query(query)
return render_template('form_action.html', query=query, results=results)
#app.route('/retrieve/<int:item_number>', methods=['GET'])
def retrieve(item_number):
item = engine.Product(item_number, collection.open_document(str(item_number)))
return render_template('document.html', item=item)
if __name__ == '__main__':
app.run()
When running the file directly through the python interpreter, it works fine and I can access. However, when starting through apache and wsgi, I get no response from the server. It just hangs when making a request and nothing is available on the logs.
I suspect that my issue may have something to do with the three objects I initialize at the beginning of the program. Perhaps it gets stuck running those?
Update: I have tried commenting out certain parts of the code to see what is causing it to stall. Tracing it out to the engine module, importing NearestNeighbors seems to be causing the issue.
import sqlite3
import config
from sklearn.neighbors import NearestNeighbors
from preprocessor import preprocess_document
from collections import namedtuple
I have a flask (0.10.1) app running on a Debian Jessie VPS and powered by nginx (1.6.2). The app is working fine but I have a problem on a specific route I added recently.
The route is intended for downloading .xml files.
It is dynamic to tell the directory and the file name:
#app.route('/backups/<dir_key>/<filename>')
And it registers a function based on the flask send_from_directory function:
def backups(dir_key,filename):
directory = os.path.join(app.config['BACKUPXML_FOLDER'], dir_key)
return send_from_directory(directory, filename, as_attachment=True)
The route is generated thanks to the flask url_for function, and returned to the frontend:
return jsonify({
'backupFileUrl': url_for('backups', dir_key=dir_key, filename = filename, _external=True)
})
where it is stored in an AngularJS variable:
$scope.backupFileUrl = response.backupFileUrl;
And finally included in a <a> tag for download :
<a class="btn btn-primary"
ng-show="sessionDownload"
ng-href="{{ backupFileUrl }}" target="_blank">
<span class="glyphicon glyphicon-save"></span> Télécharger </a>
But when I click on the button, I get the following error :
What is weird to is that :
The download is properly triggered when the app is powered by a small Python server on a local Windows machine.
I have a route intended for downloads of .xlsx files which is actually working, and both on a local Windows machine and on the Jessie VPS.
Someone see how I can define the route to make it work ?
Here is the api architecture if needed :
api/app.py
import sys
sys.path.append('../')
from flask_script import Server, Manager
from kosapp import app, db
manager = Manager(app)
if __name__ == '__main__':
manager.run()
api/config.py
from os.path import abspath, dirname, join
import tempfile
basedir = dirname(abspath(__file__))
BASEDIR = dirname(abspath(__file__))
DEBUG = True
REPORTS_FOLDER = '/tmp/reports'
# on local machine
# REPORTS_FOLDER = os.path.join(tempfile.gettempdir(), 'reports')
BACKUPXML_FOLDER = '/tmp/backups'
# on local machine
# BACKUPXML_FOLDER = os.path.join(tempfile.gettempdir(), 'backups')
api/kosapp/__init__.py
from flask import Flask
app = Flask(__name__)
app.url_map.strict_slashes = False
app.config.from_object('config')
from kosapp import views
api/kosapp/views.py
import os
from flask import send_file, jsonify, request, render_template, send_from_directory
from kosapp import app
#app.route('/reports/<dir_key>/<filename>')
def reports(dir_key, filename):
directory = os.path.join(app.config['REPORTS_FOLDER'], dir_key)
return send_from_directory(directory, filename)
#app.route('/backups/<dir_key>/<filename>')
def backups(dir_key,filename):
directory = os.path.join(app.config['BACKUPXML_FOLDER'], dir_key)
return send_from_directory(directory, filename, as_attachment=True)
As a note, the route '/reports/<dir_key>/<filename>' is intended for downloading .xlsx file and works fine.
Did you remember to reload the app on the server? That's usually the problem if i get different results on my development computer and the web server.
For instance, if you deployed with gunicorn, you would have to restart gunicorn so the server would know about the changes to your code.