Handle multiple request at same time on Flask web app - python

What I have: I've a Flask web app deployed to Heroku's server, which consists of only one web process app.py. Here it is:
#importation
from flask import Flask, render_template, current_app, send_file, request, json, send_file
import os
#working functions
#json write
def json_write(dictionary):
with open("./json/info.json", "w+") as f:
json.dump(dictionary, f, indent=4)
#make file name
def make_file_name(name):
filename = "tube4u_"
for t in str(name):
if t.isalnum():
filename += t
filename += ".mp4"
return filename
#application initialisation
app=Flask(__name__)
#home
#app.route("/")
def home():
return render_template("index.html")
#processor
#app.route("/process/", methods=["GET"])
def process():
#get url
url = request.args["url"]
#import & initialisation
from pytube import YouTube
import pickle
json_dict = {}
try:
yt = YouTube(url)
except:
return "<h1>Invalid URL</h1>"
all_videos = yt.streams.filter(type="video", progressive=True)
json_dict["title"] = yt.title
json_dict["thumbnail"] = yt.thumbnail_url
json_dict["name"] = make_file_name(yt.title)
with open("./pickle/ytobj.pkl", "wb") as f:
pickle.dump(all_videos, f)
#videos with itag
json_dict["videos"] = [ {"itag": item.itag, "res": item.resolution} for item in all_videos]
json_write(json_dict)
return render_template("menu.html")
#download
#app.route("/download/", methods=["GET"])
def download():
import pickle
itag = int(request.args["itag"])
with open("./json/info.json") as f:
json_dict = json.load(f)
with open("./pickle/ytobj.pkl", "rb") as f:
all_videos = pickle.load(f)
video = all_videos.get_by_itag(itag)
video.download(output_path="./video", filename=f"{json_dict['name']}")
return render_template("thank.html")
#return video
#app.route("/video/", methods=["GET"])
def video():
filename = request.args["filename"]
return send_file(f"./video/{filename}", as_attachment=True)
#return json
#app.route("/json")
def fetchjson():
with open("./json/info.json") as f:
content = json.load(f)
return content
#get name
#app.route("/name")
def fetchname():
with open("./json/info.json") as f:
content = json.load(f)
return content
#app.route("/list")
def listall():
return f"{os.listdir('./video')}"
#running the app
if __name__ == "__main__":
app.run(debug=True)
How it works: here I made the app like that, whenever someone enter a URL and click Go then it creates a json file with the name info.json. after it gets everything properly it performs some task with the given URL reading from the file.
My problem:
Now the problem is, if I make a request of the web it will create a json with my given URL, suppose at the same time someone else make a request and enter a URL then server will lost my information and rewrite the json file with another client's given input URL my task will be performed with another's input url. It's really weird.
How to fix it? Like if there any way to create the info.json file on separate path for each client and gets deleted after work done?

There is a lot of ways in my point of view
When the server get client request then check if there is already a file.if there is already a file then add timestamp or add something else in the filename so the file will not be overwritten.
Ask the user file name and also add timestamp in the name and save it.
You can also use databases to store data of different clients .may be you can create login system and give every user an id and store data for every user in database accordingly.
So on...
You can see there is a lot of ways to solve this.

Related

Uploading xml file and processing using flask

I am developing a Python backend to which I send an xml file from the front end. This is so that I can generate python code based on it and show the contents in the front end. How can I do this using flask?
I have attached the code I tried below. It does not work for me. I was not able to save the xml file into a directory.
from flask import Flask, request, render_template
app = Flask(__name__, template_folder='templates')
from main import run
import os
#app.route('/')
def home():
return render_template('home.html')
#app.route('/submit/', methods=['POST'])
def upload():
if request.method == 'POST':
uploaded_file = xmltodict.parse(request.get_data())
file = os.path.join(app.config['upload'].uploaded_file.filename)
uploaded_file.save(file)
return "Successfully uploaded"
#app.route('/submit/')
def convert():
path='upload'
os.chdir(path)
for file in os.listdir():
if file.endswith(".py"):
file_path = f"{path}\{file}"
run(file_path,'tmp','python')
return "Code generated"
#app.route('/view/')
def view_python_script():
# Folder path
path='tmp'
os.chdir(path)
content=""
for file in os.listdir():
if file.endswith(".py"):
file_path = f"{path}\{file}"
with open(file_path, "r") as f:
content = content + f.read().replace('\n','<br>')
return render_template('upload.html', details=content)
if __name__ == "__main__":
app.run(port=3000, debug=True)
I occupy this: uploaded_file = request.files ['file_upload'].
file_upload I pass it from the html with the parameter name = "file_upload" of input contained within the form.
The problem I have is that when I want to share it in another html page it closes and throws me a ValueError: I / O operation on closed file.
But well, I hope it helps you !!!

How to read a URL from a text file in python and return its content in a JSON format?

I have a txt file containing URL. I want to read that text file and if it contains the URL read that URL and returns its content as a JSO, else return the contents of the text file if something else. My codes represent a FLASK application and I want to represent if as if elif else.
However, when I run the application it only shows me the URL itself not the contents of that URL.
However, when I run the application and click on the provided address, it shows me "true" and then opens a new tab and shows me connection error. I expect, when I click on the link, it shows me the json content of the URL.
Could you please help me to fix it?
from flask import Flask, jsonify
import json,re, requests
import webbrowser
app = Flask(__name__)
#app.route('/', methods = ['GET','POST'])
def test_function():
with open('test1.txt', encoding='utf-8', errors='ignore') as j:
contents = j.read()
#for line in contents:
if re.findall('https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', contents):
for url in contents:
s = webbrowser.open_new_tab(url)
return jsonify(s)
else:
return jsonify(contents)
if __name__ == '__main__':
app.run(debug=True)

How do I save a image using a flask API then return it to my React App can use it

I am trying to use my Flask API to save an image to the database OR just a file system but this is something I have never done and am getting nowhere with it.
I would like to be able to return the image back when the route is called and be able to use it in my ReactJS Application using just a img tag.
All I have been able to find is how to save the image to the Database and then download it using a route. I need to be able to return it. (It works just not what I need.)
Here is what that was:
#app.route('/img-upload', methods=['POST'])
def img_upload():
file = request.files['image']
newFile = Mealplan(name=file.filename, data=file.read())
db.session.add(newFile)
db.session.commit()
return jsonify({"Done!" : "The file has been uploaded."})
#app.route('/get-mealplan-image/<given_mealplan_id>')
def download_img(given_mealplan_id):
file_data = MealPlan.query.filter_by(id=given_mealplan_id).first()
return send_file(BytesIO(file_data.data), attachment_filename=file_data.name, as_attachment=True)
Save the files on the file system will be a more proper method. Here is a minimal example:
from flask import send_from_directory
basedir = os.path.abspath(os.path.dirname(__file__))
uploads_path = os.path.join(basedir, 'uploads') # assume you have created a uploads folder
#app.route('/img-upload', methods=['POST'])
def upload_image():
f = request.files['image']
f.save(os.path.join(uploads_path , f.filename)) # save the file into the uploads folder
newFile = Mealplan(name=f.filename) # only save the filename to database
db.session.add(newFile)
db.session.commit()
return jsonify({"Done!" : "The file has been uploaded."})
#app.route('/images/<path:filename>')
def serve_image(filename):
return send_from_directory(uploads_path, filename) # return the image
In your React app, you can use the filename to build to the image URL: /images/hello.jpg
Update:
If you can only get the id, the view function will be similar:
#app.route('/get-mealplan-image/<given_mealplan_id>')
def download_img(given_mealplan_id):
file_data = MealPlan.query.filter_by(id=given_mealplan_id).first()
return send_from_directory(uploads_path, file_data.name)

requests.content works normally but saves corrupt pdf file when used inside app.route flask

I am trying to download a pdf from the link :
https://ptenantectdtest.blob.core.windows.net/documentcontainer/fae488ce-514d-4367-be48-610b19193e10?sv=2015-12-11&sr=b&sig=It1gKsb%2BHmQwjqxAprbAROySOKAdd2qyFnW%2FoBi0uM0%3D&st=2019-07-18T18%3A20%3A05Z&se=2019-07-19T18%3A30%3A05Z&sp=r&rscd=attachment%3B%20filename%3D%20%228d4508bf-453e-45fd-8457-8fd158152ba7.pdf%22
When I use requests.content inside a python function, it works well and downloads the pdf but when I use the same inside app.route, it saves a corrupt pdf file.
Code for normal python function using requests:
def download_url(url):
r = requests.get(url)
with open('D:/file_.pdf', 'wb') as f:
f.write(r.content)
categories = convert('D:/file_.pdf')
return categories
Code for downloading from app.route:
import requests
import ectd
from ectd import convert
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class ectdtext(Resource):
def get(self, result):
return {'data': ectd.convert(result)}
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def get_dir(path):
r = requests.get(path)
with open('D:/3file_.pdf', 'wb') as f:
f.write(r.content)
categories = convert('D:/3file_.pdf')
return categories
if __name__ == '__main__':
app.run()

Best way to create a download link for a file in Flask?

In my project, when a user clicks a link, an AJAX request sends the information required to create a CSV. The CSV takes a long time to generate and so I want to be able to include a download link for the generated CSV in the AJAX response. Is this possible?
Most of the answers I've seen return the CSV in the following way:
return Response(
csv,
mimetype="text/csv",
headers={"Content-disposition":
"attachment; filename=myplot.csv"})
However, I don't think this is compatible with the AJAX response I'm sending with:
return render_json(200, {'data': params})
Ideally, I'd like to be able to send the download link in the params dict. But I'm also not sure if this is secure. How is this problem typically solved?
I think one solution may the futures library (pip install futures). The first endpoint can queue up the task and then send the file name back, and then another endpoint can be used to retrieve the file. I also included gzip because it might be a good idea if you are sending larger files. I think more robust solutions use Celery or Rabbit MQ or something along those lines. However, this is a simple solution that should accomplish what you are asking for.
from flask import Flask, jsonify, Response
from uuid import uuid4
from concurrent.futures import ThreadPoolExecutor
import time
import os
import gzip
app = Flask(__name__)
# Global variables used by the thread executor, and the thread executor itself
NUM_THREADS = 5
EXECUTOR = ThreadPoolExecutor(NUM_THREADS)
OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__))
# this is your long running processing function
# takes in your arguments from the /queue-task endpoint
def a_long_running_task(*args):
time_to_wait, output_file_name = int(args[0][0]), args[0][1]
output_string = 'sleeping for {0} seconds. File: {1}'.format(time_to_wait, output_file_name)
print(output_string)
time.sleep(time_to_wait)
filename = os.path.join(OUTPUT_DIR, output_file_name)
# here we are writing to a gzipped file to save space and decrease size of file to be sent on network
with gzip.open(filename, 'wb') as f:
f.write(output_string)
print('finished writing {0} after {1} seconds'.format(output_file_name, time_to_wait))
# This is a route that starts the task and then gives them the file name for reference
#app.route('/queue-task/<wait>')
def queue_task(wait):
output_file_name = str(uuid4()) + '.csv'
EXECUTOR.submit(a_long_running_task, [wait, output_file_name])
return jsonify({'filename': output_file_name})
# this takes the file name and returns if exists, otherwise notifies it is not yet done
#app.route('/getfile/<name>')
def get_output_file(name):
file_name = os.path.join(OUTPUT_DIR, name)
if not os.path.isfile(file_name):
return jsonify({"message": "still processing"})
# read without gzip.open to keep it compressed
with open(file_name, 'rb') as f:
resp = Response(f.read())
# set headers to tell encoding and to send as an attachment
resp.headers["Content-Encoding"] = 'gzip'
resp.headers["Content-Disposition"] = "attachment; filename={0}".format(name)
resp.headers["Content-type"] = "text/csv"
return resp
if __name__ == '__main__':
app.run()

Categories