PYTHON FLASK - request.files displays as file not found eventhough it exits - python-3.x

I am trying to trigger an external api from postman by passing the uploadFile in the body as form-data. Below code throws me an error as 'FileNotFoundError: [Errno 2] No such file or directory:'
Note: In postman, uploadFile takes file from my local desktop as input. have also modified the postman settings to allow access for files apart from working directory
Any help would be highly appreciable.
Below is the Code:
#app.route('/upload', methods=['POST'])
#auth.login_required
def post_upload():
payload = {
'table_name': 'incident', 'table_sys_id': request.form['table_sys_id']
}
files = {'file': (request.files['uploadFile'], open(request.files['uploadFile'].filename,
'rb'),'image/jpg', {'Expires': '0'})}
response = requests.post(url, headers=headers, files=files, data=payload)
return jsonify("Success- Attachment uploaded successfully ", 200)

Below code throws me an error as 'FileNotFoundError: [Errno 2] No such file or directory:
Have you defined UPLOAD_FOLDER ? Please see: https://flask.palletsprojects.com/en/latest/patterns/fileuploads/#a-gentle-introduction
i am passing the attribute (upload file) in body as form-data, can this be passed as raw json
You cannot upload files with JSON. But one hacky way to achieve this is to base64 (useful reference) encode the file before sending it. This way you do not upload the file instead you send the file content encoded in base64 format.
Server side:
import base64
file_content = base64.b64decode(request.data['file_buffer_b64'])
Client side:
-> Javascript:
const response = await axios.post(uri, {file_buffer_b64: window.btoa(file)})
-> Python:
import base64
with open(request.data['uploadFile'], "rb") as myfile:
encoded_string = base64.b64encode(myfile.read())
payload = {"file_buffer_b64": encoded_string}
response = requests.post(url, data=payload)

Related

Urllib3 POST request with attachment in python

I wish to make a post request to add an attachment utilising urllib3 in python without success. I have confirmed the API itself is working in postman but cannot work out how to convert this request to python. Appreciating I'm mixing object types I just don't know how to avoid it.
Python code:
import urllib3
import json
api_key = "secret_key"
header = {"X-API-KEY": api_key, "ACCEPT": "application/json", "content-type": "multipart/form-data"}
url = "https://secret_url.com/api/"
http = urllib3.PoolManager()
with open("invoice.html", 'rb') as f:
file_data = f.read()
payload = {
"attchment": {
"file": file_data
}
}
payload = json.dumps(payload)
r = http.request('post', url, headers = header, fields = payload)
print(r.status)
print(r.data)
Postman - which works and properly sends file-name through also (I'm guessing it splits the bytes and filename up?)
Edit: I've also tried the requests library as I'm more familiar with this (but can't use it as the script will be running in AWS lambda). Removing the attachment element form the dict allows it to run but the API endpoint gives 401 presumably because it's missing the "attachement" part to the data structure as per postman below... but when I put this in I get runtime errors.
r = requests.post(url, headers = header, files={"file": open("invoice.html", 'rb')})
For anyone who stumbles upon this from Dr google a few points:
I was completely mis-interpreting the structure of the element. It's actually a string "attachment[file]" not a dict like object.
Postman has the ability to output python code in urllib/request syntax albeit not 100% what I was after. Note: the chrome version (depreciated) outputs gibberish code that only half works so the client version should be used. A short bit of work below shows it working as expected:
http = urllib3.PoolManager()
with open("invoice.html", "rb") as f:
file = f.read()
payload={
'attachment[file]':('invoice.html',file,'text/html')
}
r = http.request('post', url, headers = header, fields = payload)

How to retrieve file object in python sent via Postman without any url

I am sending a file as an object via postman POST or PUT API like below:
How can I in Python -
get this file object
read and save
If you have a working request in Postman, you could copy autogenerated Code Snippet in Python - Requests format:
It might look like this:
import requests
url = "localhost:8080"
payload="<file contents here>"
headers = {
'Content-Type': 'application/octet-stream'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
Answer to this question which i finally implemented without url -
#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"})

Python Requests Post - Additional field is not recognized for file upload

I have to post a file using Multipart upload to a company-internal REST service. The endpoint needs the file as property "file" and it needs an additional property "DestinationPath". Here is what I do:
url = r"http://<Internal IP>/upload"
files = {
"DestinationPath": "/some/where/foo.txt",
"file": open("test.txt", "rb")
}
response = requests.post(url, files=files)
The server complains that it can't get the "DestinationPath". Full error message I receive is:
{'errors': {'DestinationPath': ['The DestinationPath field is required.']},
'status': 400,
'title': 'One or more validation errors occurred.',
'traceId': '00-1993fbc53ab2ee418b683915dd7a440a-2338bd9cf34d414a-00',
'type': 'https://tools.ietf.org/html/rfc7231#section-6.5.1'}
The file upload works in curl, thus it must be python specific.
You might want to try using the data argument instead of files.
response = requests.post(url, data=files)
Thanks to #etemple1 I found the solution to my question:
url = r"http://<Internal IP>/upload"
data = {
"DestinationPath": "/some/where/foo.txt",
}
with open("test.txt", "rb") as content:
files = {
"file": content.read(),
}
response = requests.post(url, data=data, files=files)
The data for the multipart upload needed to be divided between "data" and "files". They are later combined in the body of the http post by the requests library.

Using local image for Read 3.0, Azure Cognitive Service, Computer Vision

I am attempting to use a local image in my text recognition script, the documentation has the following example (https://learn.microsoft.com/en-us/azure/cognitive-services/computer-vision/quickstarts/python-hand-text):
But when I change the image_url to a local file path, it sends a HTTPError: 400 Client Error: Bad Request for url. I have tried following other tutorials but nothing seems to work.
Any help would be greatly appreciated :)
The Cognitive services API will not be able to locate an image via the URL of a file on your local machine. Instead you can call the same endpoint with the binary data of your image in the body of the request.
Replace the following lines in the sample Python code
image_url = "https://raw.githubusercontent.com/MicrosoftDocs/azure-docs/master/articles/cognitive-services/Computer-vision/Images/readsample.jpg"
headers = {'Ocp-Apim-Subscription-Key': subscription_key}
data = {'url': image_url}
response = requests.post(
text_recognition_url, headers=headers, json=data)
with
headers = {'Ocp-Apim-Subscription-Key': subscription_key,'Content-Type': 'application/octet-stream'}
with open('YOUR_LOCAL_IMAGE_FILE', 'rb') as f:
data = f.read()
response = requests.post(
text_recognition_url, headers=headers, data=data)
And replace the following line:
image = Image.open(BytesIO(requests.get(image_url).content))
with
image = Image.open('./YOUR_LOCAL_IMAGE_FILE.png')

flask wtf upload image file to aws s3

I want to upload image to aws s3 using presigned url and the image is selected by users only.
So I make file input field using flask-wtf filefield.
After user submit the form(PostForm), I want to get the image's data and send to presigned-url.
But I don't know how to get image file's information from the Form.
Please help me!!!
I used example code from below tutorials. But the difference is that I use flask-wtf not local image file.
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html
In following code, the problem line is here.
files = {'file': (save_objectname, image.read())}
http_response = requests.post(response['url'], data=response['fields'], files=files)
when I print(image.read())
it shows
b'' <--- nothing...
How can I fix it???
def create_post():
form = PostForm()
save_post_foldername = 'post_images'
if form.validate_on_submit():
if form.post_image.data:
image_file = form.post_image.data
save_post_objectname = generate_filename(image_file)
# Generate a presigned S3 POST URL
post_to_aws_s3(image_file, save_post_foldername, save_post_objectname)
def post_to_aws_s3(image, save_foldername, save_objectname):
fields = {
"acl": "public-read",
"Content-Type": image.content_type
}
conditions = [
{"acl": "public-read"},
{"Content-Type": image.content_type}
]
try:
response = s3_client.generate_presigned_post(
Bucket=S3_BUCKET,
Key=save_foldername+'/'+save_objectname,
Fields=fields,
Conditions=conditions,
ExpiresIn=600
)
print(response)
print(response['url'])
print(image.content_type)
#with open(image, 'rb') as f:
#files = {'file': ('abc.png', f)}
#files = {'file': (image, f)}
files = {'file': (save_objectname, image.read())}
http_response = requests.post(response['url'], data=response['fields'], files=files)
print(image.read())
except ClientError as e:
print("error")
Access_ID and secret ID refer to your aws user access id and secret key, because boto3 will use that to access your aws s3 using the user you assign to it.
s3 = boto3.resource("s3", aws_access_key_id=os.getenv("ACCESS_ID"), aws_secret_access_key=os.getenv("ACCESS_SECRET_ID"))
s3.Bucket("[NAME OF BUCKET]").put_object(Key="images/"+request.files["image"].filename, Body=request.files["image"] )
make sure you you include "enctype" under forms, under jinja, e.g.
<form method="POST" action="" enctype="multipart/form-data">

Resources