Sending a file via http in Python with bottle framework - python

How can I send a file as answer to an HTTP request in Python using the bottle framework?
For upload this code works for me:
#route('/upload', method='POST')
def do_upload():
category = request.forms.get('category')
upload = request.files.get('upload')
name, ext = os.path.splitext(upload.raw_filename)
if ext not in ('.png','.jpg','.jpeg'):
return 'File extension not allowed.'
save_path = category
upload.raw_filename = "hoho" + ext
upload.save(save_path) # appends upload.filename automatically
return 'OK'

Just call Bottle's static_file function and return its result:
#route('/static/<filename:path>')
def send_static(filename):
return static_file(filename, root='/path/to/static/files')
That example, when called with url /static/myfile.txt, will return the contents file /path/to/static/files/myfile.txt.
Other SO questions (like this one) contain additional examples.

Related

Python Flask Restful After Request Delete File

I am writing a simple PDF converter inside container. Send docx and get PDF, but I don't want file to stay on server, therefor I wish to delete them after download request.
I tried to use flask after_this_request on get request on Download(Resource)
class Downloader(Resource):
def get(self, doc_id):
folder, file_name = FileConverter.download_file(doc_id)
if not folder:
return jsonify({"status": "NOTOK", "error": "No File"})
#after_this_request
def _clean_file():
FileConverter.delete_file(doc_id)
return send_from_directory(folder, file_name, as_attachment=True)
FileConverter.delete_file checks if file exists and uses os.remove to delete it, however this part of code corrupt PDF into unreadable. If I remove #after_this_request, I get working PDF.
How should I do this?
I had this problem and I solved it like this:
class Downloader(Resource):
def get(self, doc_id):
folder, file_name = FileConverter.download_file(doc_id)
if not folder:
return jsonify({"status": "NOTOK", "error": "No File"})
try:
return send_from_directory(folder, file_name, as_attachment=True)
finally:
FileConverter.delete_file(doc_id)

Handle multiple request at same time on Flask web app

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.

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)

How to mock filesystem partial in Python3

I want to mock a a filesystemcall that is creating an file. But I got the problem, that I am using flask to create the output and flask also needs to read the teamplate from filesystem. So I am running in an error while rendering the output with flask.
Is there a good way to mock just one file instead of all filesystem calls?
def func_to_test(self, data_for_html):
template_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates'))
app = flask.Flask('my app', template_folder=template_dir)
with app.app_context():
rendered = render_template('index.html', data=data_for_html)
with open(self.fileName, **self.options_file) as html_file:
html_file.write(rendered)
def test_func(self, data):
fake_file_path = "fake/file/path/filename"
m = mock_open()
with patch('builtins.open', mock_open()) as m:
data_writer = FlaskObject(fileName=fake_file_path)
data_writer.write(data)
Split the function to test so you can test each part in isolation:
def _generate_content(self, data):
template_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates'))
app = flask.Flask('my app', template_folder=template_dir)
with app.app_context():
return render_template('index.html', data=data_for_html)
def _write_content(self, content):
with open(self.fileName, **self.options_file) as html_file:
html_file.write(content)
def func_to_test(self, data_for_html):
rendered = self._generate_content(data_for_html)
self._write_content(rendered)
and then you can mock those two methods and test that func_to_test calls them both with expected values.
Instead of mocking open you could create a temporary file that you write to instead using tempfile.
def test_func(self, data):
with tempfile.NamedTemporaryFile() as f:
data_writer = FlaskObject(fileName=f.name)
data_writer.write(data)
This will not work on windows, if you wish for it to work on windows you would have to create the temp file with delete=False, close the file and then delete the file after the test

How to create dynamic file sending algorithm with flask

I have some problems with file serving with flask.
Firstly, my file tree:
processedMain
--processed1
--processed2
--...
--processedN
In each folder I have bunch of files that I represent it all in table where i put href link with path of each of files and pass it through route function with send_file return.
The problem is that the only way that I was able to make it work is to creating #app.route for every subfolder hardcoded, like this:
#app.route('/' + pathFits1[1] + '<id>', methods=['GET'])
def returnImg1(id):
return send_file(pathFits1[1] + id, as_attachment=True, attachment_filename=id)
#app.route('/' + pathFits1[2] +'<id>', methods=['GET'])
def returnImg2(id):
return send_file(pathFits1[2] + id, as_attachment=True, attachment_filename=id)
#app.route('/' + pathFits1[3] + '<id>', methods=['GET'])
def returnImg3(id):
return send_file(pathFits1[3] + id, as_attachment=True, attachment_filename=id)
where pathFits1[i] is path to the subfolder and id is passed when user clicks on file in web table.
Problem is that I have a lot of subfolder, and its tiring to create #app.route for each.
Is this possible to create just one route for every thing?
P.S.
I'm quite new to flask, so if I forgot to tell something, please tell me I will fill it out.
Solution
Actually it was embarrassingly simple.
As #Dan.D pointed, flask has convertors of captured variables, for me it was all I needed, as my id variable already contains full path to file.
So now it's just this 3 lines which substitute a whole bunch or duplicated code:
#app.route('/<path:id>')
def returnImg(id):
fileName = id.rsplit('/', 1)[-1]
return send_file(id, as_attachment=True, attachment_filename=fileName)
Where id is full path to file, path: is converter to path and fileName is name of file that user will download without path in it (all after last slash)
You can capture the path in the route expression. From the documentation:
#app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return 'Subpath %s' % subpath
From http://flask.pocoo.org/docs/1.0/quickstart/
As far as I know, Flask doesn't support multiple variable sections in #app.route URLs. However, you could set the full path of the file as a variable section and then parse it to extract the sub-folder path and the file ID.
Here's a working example:
pathFits1 = [None] * 3
pathFits1[1] = 'content/images1/'
pathFits1[2] = 'content/images2/'
#app.route('/<path:fullpath>', methods=['GET'])
def returnImg(fullpath):
global pathFits1
print("--> GET request: ", fullpath)
# parse the full path to extract the subpath and ID
# subpath: get everything until the last slash
subpath = fullpath.rsplit('/', 1)[:-1][0] + '/'
# id: get string after the last slash
id = fullpath.rsplit('/', 1)[-1]
print("ID:", id, "\tsubpath:", subpath)
# try to send the file if the subpath is valid
if subpath in pathFits1:
print("valid path, attempting to sending file")
try:
return send_file(subpath + id, as_attachment=True, attachment_filename=id)
except Exception as e:
print(e)
return "file not found"
return "invalid path"
However, a much better implementation would be to send the file ID and path as GET request parameters (eg. http://127.0.0.1:8000/returnimg?id=3&path=content/images1/) and retrieve them like this:
from flask import Flask, send_file, request
app = Flask(__name__)
pathFits1 = [None] * 3
pathFits1[1] = 'content/images1/'
pathFits1[2] = 'content/images2/'
#app.route('/returnimg', methods=['GET'])
def returnImg():
global pathFits1
id = request.args.get('id')
path = request.args.get('path')
print("ID:", id, "\tpath:", path)
# try to send the file if the subpath is valid
if path in pathFits1:
print("valid path, attempting to sending file")
try:
return send_file(path + id, as_attachment=True, attachment_filename=id)
except Exception as e:
print(e)
return "file not found"
return "invalid path"

Categories