I am trying to receive a file in python flask endpoint via post request, and then send it to another endpoint. I receive a file on my API but I do not know how to wrap it for the next request.
The code looks like this
#app.route('/file', methods=['POST'])
def get_task_file():
response_obj = {}
if 'file' not in request.files:
abort(400)
print(request.files["file"].filename)
# TEXT EXTRACT
url1 = 'http://localhost:5001/text/api'
headers = {'Content-type': 'multipart/form-data'}
print(type(request.files['file']))
r1 = requests.request("POST", url1, files={'file':request.files['file']}, headers=headers)
print(r1.text)
Right now the file type is <class 'werkzeug.datastructures.FileStorage'> and I am getting 400 errors. I just want to add that the logic on the second API is similar, should I change it?
I got code:400 as return or it shows the file I upload is empty, I found requests in flask is a little bit tricky.
change this
r1 = requests.request("POST", url1, files={'file':request.files['file']}, headers=headers)
into:
file = request.files['file']
r1 = requests.post(url1, files={'file':(file.filename, file.stream, file.content_type, file.headers)})
Related
I'm working on an API for my application to send a POST request to an external API. So for example, if my app hits endpoint /myapp/api I want it to call out to an external API and grab some data. In this case, I need this to be a POST request because in order to get the data that I want, I need to pass in some values.
I have made this POST call directly to the external API via Postman successfully many times, and I'm now trying to reproduce that in my python code.
In my views.py, I have this:
class MyAPPViews(APIView):
def post(self,request):
crt = 'path/to/crtfile'
key = 'path/to/keyfile'
#here I tried to reproduce the headers from the postman call
#as best as I could just to be extra specific
headers = {
'Content-Type': 'application/json',
'Accept': '/*/',
'Accept-Encoding': 'gzip,deflate,br',
'Connection': 'keep-alive'
}
payload = {
"unitList": [
"CFQU319303"
]
}
res = requests.post('external/api/url', headers=headers, data=payload, cert=(crt,key))
print('this is the true http resonse: ',res.status_code)
data = res.json()
return Response({"status": "success", "data": data})
in my urls.py I have this
path('externalAPI',MyAPPViews.as_view()),
Now, when I try to go into postman and hit http://127.0.0.1:8000/myapp/externalAPI
I get a 500 status code returned and the JSONDecodeError of Expecting value: line 1 column 1 (char 0). I understand that this means that basically the json response was empty and there was nothing for it to parse.
Is there anything blatantly wrong with my views.py code?
EDIT Within the res = requests.post('external/api/url', headers=headers, data=payload, cert=(crt,key)), I had to change data=payload to data=json.dumps(payload). I'm assuming it didn't like the way I formatted my payload initially. After that, I also deleted everything but the Content-Type: application/json out my headers dictionary.
I'd say, it's just a wrong URL in your call of the external API, as you are using some path without a domain.
So instead of
res = requests.post('external/api/url', headers=headers, data=payload, cert=(crt,key))
it should be sth. like
res = requests.post('https://SOMEDOMAIN.ORG/external/api/url', headers=headers, data=payload, cert=(crt,key))
Within the res = requests.post('external/api/url', headers=headers, data=payload, cert=(crt,key)), I had to change data=payload to data=json.dumps(payload). I'm assuming it didn't like the way I formatted my payload initially. After that, I also deleted everything but the Content-Type: application/json out my headers dictionary.
I have this at django
#csrf_exempt
#require_http_methods(['POST'])
#api_token
def foo(request):
upload_file = request.FILES['file']
random_function(upload_file)
return HttpResponse('done')
and this at another python file
import requests
url = "http://127.0.0.1:8000/api/external/path"
payload = {}
files = {'file': open('csv_file.csv', 'rb')}
headers = {
'x-api-key': 'api-key-token',
'Content-Type': 'application/form-data'
}
response = requests.request("POST", url, headers=headers, data=payload, files=files)
print(response.text.encode('utf8'))
the problem is that when I executed the python file I got this error
django.utils.datastructures.MultiValueDictKeyError: 'file'
it was like request.FILES can't find file name
but when I execute it from postman it works fine
Okay guys, I've tried
headers = {
'x-api-key': 'api-key-token'
}
And then it works. My team said because the boundary has gone missing since we force the content type to be multipart/form-data but I still don't quite get it yet. If anyone can explain it would be much appreciated
I want to send a file with the post method, but I don't know what's wrong with my code
I have chat_id, file_id, and every requirement parameters
this is a sample code for sending voice via POST Request
import requests
my_data = {'chat_id': '72600457' ,'file_id': 'AwADBAADPAYAAvFWCVFZFfPyZdGLfhYE'}
my_url = 'https://api.telegram.org/bot<MY TOKEN>/sendVoice'
request.post(url=my_url, data=my_data)
when I run the code, no error happens. But nothing is shown from the bot;
This file_id works with GET METHOD and I could send text with POST METHOD, But for files it seems it doesn't work.
Check documentation for sendVoice - it doesn't use name file_id but voice
data = {'chat_id': '72600457', 'voice': 'AwADBAADPAYAAvFWCVFZFfPyZdGLfhYE'}
If you use file ID then you can use POST but also GET
And you should get response from server to see information about wrong request
import requests
token = '<MY TOKEN>'
data = {'chat_id': '72600457', 'voice': 'AwADBAADPAYAAvFWCVFZFfPyZdGLfhYE'}
url = f'https://api.telegram.org/bot{token}/sendVoice'
#response = requests.post(url, data=data)
response = requests.get(url, params=data)
print(response.json())
By the way: there is module python-telegram-bot. GitHub: python-telegram-bot
I am using python to try and get an image from one API, and then post it to a separate API.
So far I have this code:
def create_item(token, user_id):
url = '<api_url_to_post_to>'
headers = {"Authorization": "Token {0}".format(token)}
image_url = '<some_random_image_url>'
image_obj = requests.get(image_url)
data = {
"image": image_obj.content
}
r = requests.post(url, headers=headers, files=data)
response = r.json()
print(response)
return response
The issue is that when I try to post it to the second API, I get a "File extension '' is not allowed." error. This is obviously a custom error message, but it signifies that there is something wrong with the file that I am posting.
Any suggestions on what I may be doing wrong?
Try specifying the file type, just image_obj.content is the raw binary image:
r = requests.post(
url,
headers=headers,
files={'image': ('my_image.jpg', image_obj.content, 'image/jpg')})
This should add the correct headers to the multipart boundary for the image.
If you don't know for sure the content type, you might actually get it from the headers of your previous response: image_obj.headers['Content-Type'] should be "image/jpg" but for a different image it might be "image/png".
It wants a file extension, I'd hazard a guess that image should be image.<SOME_FORMAT>.
I am using a Flask route as a proxy to download a file, like this:
#esa_handler.route("/data/<int:series>/<int:file_num>", methods=["GET"])
def DownloadRemote(series, file_num):
"""
Downloads the remote files from the ESA.
:param series: 0-20.
:param file_num: File within the series, 0-255
:return: Compressed CSV file.
"""
# if the file is bad.
if series >= 20 and file_num > 110:
return jsonify({"error": "file does not exist."})
url = "http://cdn.gea.esac.esa.int/Gaia/gaia_source/csv/GaiaSource_000-{:03d}-{:03d}.csv.gz".format(series,
file_num)
req = requests.get(url, stream=True)
return Response(stream_with_context(req.iter_content(chunk_size=2048)), content_type=req.headers["content-type"])
It works fine, however, the filename that is presented to the client is whatever the file number that is passed to the endpoint. For example, if I put http://127.0.0.1:5000/esa/data/0/0 to download the very first file, it downloads, but Chrome/Firefox/IE/Edge are offering to save the file with a filename as "0". While there is nothing wrong with that, I would like a better user experience.
How can I intercept the response to proffer a filename based off the URL requested?
This can be done with the Content-Disposition HTTP header. Here you can specify a filename for the newly downloaded file.
This can be added to a Flask Response as follows:
url = "http://cdn.gea.esac.esa.int/Gaia/gaia_source/csv/GaiaSource_000-{:03d}-{:03d}.csv.gz".format(series,
req = requests.get(url, stream=True)
headers = Headers()
headers .add('Content-Type', req.headers["content-type"])
headers .add('Content-Disposition', 'attachment; filename="filename.txt"')
return Response(stream_with_context(req.iter_content(chunk_size=2048)), headers=headers)
Note: The Content-Type was moved into headers for simplicity