Save file from client to server by Python and FastAPI - python-3.x

I create a API by fastAPI like the following:
#app.post('/uploadFile/')
async def uploadFile(file:UploadFile = File(...)):
file_name,extension_file_name = os.path.splitext(file.filename)
base_path = os.path.join(os.getcwd(),'static')
path_cheked_exists = os.path.join(base_path,extension_file_name[1:])
if not os.path.exists(path_cheked_exists):
os.makedirs(path_cheked_exists)
try:
shutil.copy(file,path_cheked_exists)
except Exception as exp:
print("can not copy that file")
return {'file':'okay'}
API returned the result but didn't save file .
Thanks for any help with shutil module

Instead of using shutil module, you can use a simple context manager
with open('filename.ext', 'wb+') as file_obj:
file_obj.write(file.file.read())
Also your problem could be that you only wrote file.
Try shutil.copy(file.file,path_cheked_exists)

Related

How to get filename from a file object in PYTHON?

I am using below code where I am using PUT api from POSTMAN to send a file to a machine hosting the api using python script
#app.route('/uploadFIle', methods=['PUT'])
def uploadFile():
chunk_size = 4096
with open("/Users/xyz/Documents/filename", 'wb') as f:
while True:
chunk = request.stream.read(chunk_size)
if len(chunk) == 0:
break
f.write(chunk)
return jsonify({"success":"File transfer initiated"})
Is there a way to get the original filename so that I can use the same while saving the file ?
Can do as below by passing name from PUT api itself, but is it the best solution ?
#app.route('/uploadFIle/<string:filename>', methods=['PUT'])
def uploadFile(filename):
Below is how I achieved it using flask -
Choose form-data under body in POSTMAN
You can give any key, i used 'file' as key, then choose option 'file' from drop down arrow in key column
Attach file under 'value' column and use below code to get the file name -
from flask import request
file = request.files['file']
file_name = file.filename

Send a file to the user, then delete file from server [duplicate]

This question already has an answer here:
Remove file after Flask serves it
(1 answer)
Closed 1 year ago.
I want my server to send a file to the user, and then delete the file.
The problem is that in order to return the file to the user, i am using this:
return send_file(pathAndFilename, as_attachment=True, attachment_filename = requestedFile)
Since this returns, how can i delete the file from the os with os.remove(pathAndFilename)?
I also tried this:
send_file(pathAndFilename, as_attachment=True, attachment_filename = requestedFile)
os.remove(pathAndFilename)
return 0
But i got this error:
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a int.
Since send_file already returns the response from the endpoint, it is no longer possible to execute code afterwards.
However, it is possible to write the file to a stream before the file is deleted and then to send the stream in response.
from flask import send_file
import io, os, shutil
#app.route('/download/<path:filename>')
def download(filename):
path = os.path.join(
app.static_folder,
filename
)
cache = io.BytesIO()
with open(path, 'rb') as fp:
shutil.copyfileobj(fp, cache)
cache.flush()
cache.seek(0)
os.remove(path)
return send_file(cache, as_attachment=True, attachment_filename=filename)
In order to achieve better use of the memory for larger files, I think a temporary file is more suitable as a buffer.
from flask import send_file
import os, shutil, tempfile
#app.route('/download/<path:filename>')
def download(filename):
path = os.path.join(
app.static_folder,
filename
)
cache = tempfile.NamedTemporaryFile()
with open(path, 'rb') as fp:
shutil.copyfileobj(fp, cache)
cache.flush()
cache.seek(0)
os.remove(path)
return send_file(cache, as_attachment=True, attachment_filename=filename)
I hope your conditions are met.
Have fun implementing your project.

Flask: delete file from server after send_file() is completed

I have a Flask backend which generates an image based on some user input, and sends this image to the client side using the send_file() function of Flask.
This is the Python server code:
#app.route('/image',methods=['POST'])
def generate_image():
cont = request.get_json()
t=cont['text']
print(cont['text'])
name = pic.create_image(t) //A different function which generates the image
time.sleep(0.5)
return send_file(f"{name}.png",as_attachment=True,mimetype="image/png")
I want to delete this image from the server after it has been sent to the client.
How do I achieve it?
Ok I solved it. I used the #app.after_request and used an if condition to check the endpoint,and then deleted the image
#app.after_request
def delete_image(response):
global image_name
if request.endpoint=="generate_image": //this is the endpoint at which the image gets generated
os.remove(image_name)
return response
Another way would be to include the decorator in the route. Thus, you do not need to check for the endpoint. Just import after_this_request from the flask lib.
from flask import after_this_request
#app.route('/image',methods=['POST'])
def generate_image():
#after_this_request
def delete_image(response):
try:
os.remove(image_name)
except Exception as ex:
print(ex)
return response
cont = request.get_json()
t=cont['text']
print(cont['text'])
name = pic.create_image(t) //A different function which generates the image
time.sleep(0.5)
return send_file(f"{name}.png",as_attachment=True,mimetype="image/png")
You could have another function delete_image() and call it at the bottom of the generate_image() function

Getting 'positional argument' Error while sending a XML file to RabbitMQ from Python

I am trying to send a XML file to RabbitMQ from python but I am getting the below error
Error
File "<ipython-input-134-8a1b7f8b2e41>", line 3
channel.basic_publish(exchange='',queue='abc',''.join(lines))
^
SyntaxError: positional argument follows keyword argument
My Code
import ssl
!pip install pika
import pika
ssl_options = pika.SSLOptions(ssl._create_unverified_context())
credentials = pika.PlainCredentials(username='abcc', password='abcc')
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='xxxx', port=5671, virtual_host ='xxx', credentials=credentials,
ssl_options=ssl_options))
channel = connection.channel()
result = channel.queue_declare(queue='abc')
with open('20200205280673.xml', 'r') as fp:
lines = fp.readlines()
channel.basic_publish(exchange='',queue='abc',''.join(lines))
Whats wrong in the above code?
As #ymz suggested, you are missing the body key in the basic.publish method. Also, the basic_publish method has no argument called queue. Please have a look at its implementation docs
Edit #1: I have already answered this question elsewhere How to send a XML file to RabbitMQ using Python?
Edit #2: Automating publishing of XML files. Assuming all the files are present in a directory called xml_files
import os
DIR = '/path/to/xml_files'
for filename in os.listdir(DIR):
filepath = f"{DIR}/{filename}"
with open(filepath) as fp:
lines = fp.readlines()
channel.basic_publish(exchange='exchange', routing_key='queue', body=''.join(lines))

Using Flask Routing in GCP Function?

I am looking to serve multiple routes from a single GCP cloud function using python. While GCP Functions actually use flask under the hood, I can't seem to figure out how to use the flask routing system to serve multiple routes from a single cloud function.
I was working on a very small project, so I wrote a quick router of my own which is working well. Now that I'm using GCP Functions more, I'd either like to figure out how to use the Flask router or invest more time on my hand rolled version and perhaps open source it, though it would seem redundant when it would be a very close copy of flask's routing, so perhaps it would be best to add it directly to Flask if this functionality doesn't exist.
Does anyone have any experience with this issue? I'm guessing I'm missing a simple function to use that's hidden in Flask somewhere but if not this seems like a pretty big/common problem, though I guess GCP Functions python is beta for a reason?
Edit:
Abridged example of my hand rolled version that I'd like to use Flask for if possible:
router = MyRouter()
#router.add('some/path', RouteMethod.GET)
def handle_this(req):
...
#router.add('some/other/path', RouteMethod.POST)
def handle_that(req):
...
# main entry point for the cloud function
def main(request):
return router.handle(request)
Following solution is working for me:
import flask
import werkzeug.datastructures
app = flask.Flask(__name__)
#app.route('some/path')
def handle_this(req):
...
#app.route('some/other/path', methods=['POST'])
def handle_that(req):
...
def main(request):
with app.app_context():
headers = werkzeug.datastructures.Headers()
for key, value in request.headers.items():
headers.add(key, value)
with app.test_request_context(method=request.method, base_url=request.base_url, path=request.path, query_string=request.query_string, headers=headers, data=request.data):
try:
rv = app.preprocess_request()
if rv is None:
rv = app.dispatch_request()
except Exception as e:
rv = app.handle_user_exception(e)
response = app.make_response(rv)
return app.process_response(response)
Based on http://flask.pocoo.org/snippets/131/
Thanks to inspiration from Guillaume Blaquiere's article and some tweaking I have an approach that enables me to use ngrok to generate a public URL for local testing and development of Google Cloud Functions.
I have two key files, app.py and main.py.
I am using VS-Code, and can now open up app.py press F5, select "Debug the current file". Now I can set breakpoints in my function, main.py. I have the 'REST Client' extension installed, which then enables me to configure GET and POST calls that I can run against my local and ngrok urls.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#app.py
import os
from flask import Flask, request, Response
from main import callback
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def test_function():
return callback(request)
def start_ngrok():
from pyngrok import ngrok
ngrok_tunnel = ngrok.connect(5000)
print(' * Tunnel URL:', ngrok_tunnel.public_url)
if __name__ == '__main__':
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
start_ngrok()
app.run(debug=True)
#!/usr/bin/env python3
# This file main.py can be run as a Google Cloud function and deployed with:
# gcloud functions deploy callback --runtime python38 --trigger-http --allow-unauthenticated
from flask import Response
import datetime
now = datetime.datetime.now()
def callback(request):
if request.method == 'POST': # Block is only for POST request
print(request.json)
return Response(status=200)
return Response(f'''
<!doctype html><title>Hello from webhook</title>
<body><h1>Hello! </h1><p>{now:%Y-%m-%d %H:%M}</p>
</body></html>
''', status=200)
Simplified version of #rabelenda's that also works for me:
def main(request):
with app.request_context(request.environ):
try:
rv = app.preprocess_request()
if rv is None:
rv = app.dispatch_request()
except Exception as e:
rv = app.handle_user_exception(e)
response = app.make_response(rv)
return app.process_response(response)
The solution by Martin worked for me until I tried calling request.get_json() in one of my routes. The end result was the response being blocked in a lower level due to the data stream already being consumed.
I came across this question looking for a solution using functions_framework in Google Cloud Run. It is already setting up an app which you can get by importing current_app from flask.
from flask import current_app
app = current_app
I believe functions_framework is used by Google Cloud Functions so it should also work there.
Thanks to #rabelenda's answer above for inspiring my answer, which just tweaks the data/json parameters, as well as enables support for an InternalServerError unhandled exception handler:
import werkzeug.datastructures
def process_request_in_app(request, app):
# source: https://stackoverflow.com/a/55576232/1237919
with app.app_context():
headers = werkzeug.datastructures.Headers()
for key, value in request.headers.items():
headers.add(key, value)
data = None if request.is_json else (request.form or request.data or None)
with app.test_request_context(method=request.method,
base_url=request.base_url,
path=request.path,
query_string=request.query_string,
headers=headers,
data=data,
json=request.json if request.is_json else None):
try:
rv = app.preprocess_request()
if rv is None:
rv = app.dispatch_request()
except Exception as e:
try:
rv = app.handle_user_exception(e)
except Exception as e:
# Fallback to unhandled exception handler for InternalServerError.
rv = app.handle_exception(e)
response = app.make_response(rv)
return app.process_response(response)

Resources