Sorry Guys, I am new to Django, I am stuck with images upload.
I have a REST_API for image upload. I pass the image and inside API get that image by using
request.FILES['fileToUpload'].
Now I have an external API, which uploads an image on my behalf, that API is working fine on the postman.
Here is the path of that API.
http://164.68.110.65/file_load.php
But in Django. I am not able to pass the image to this API. I have tried many ways.
like these.
image = request.FILES['fileToUpload']
temp = Image.open(image)
byte_io = BytesIO()
temp.save(byte_io, 'png')
files = {'fileToUpload': byte_io.getvalue() }
response = requests.post( self.URL, files=files)
print(response.status_code, response.content, response.reason)
but it always giving me an error that image format is not matched.
can you please tell me, in python-requests, how we should pass images or files that are got my request.FILES['file_name_any'].
Thanks.
I will be very thankful for your favor.
Your image is an UploadedFile or a TemporaryUploadedFile which is a subclass of File. So you can just .open() it normally as any other File object:
with image.open('rb') as f:
files = {'fileToUpload': f}
response = requests.post(self.URL, files=files)
No need to take the detour through saving the file first.
Related
I have been following the advice given on this thread: How to pass image to requests.post in python?
However, I got stuck with the error, "ValueError: read of closed file".
I have been trying to invoke an API that does some image recognition. At the moment, I am using Postman's form-data feature to send it to the Django server, which then uses "requests" to invoke the API. Let me show you the code snippet.
def post(self, request):
img = request.FILES["file"]
with img.open("rb") as f:
files = {"fileToUpload": f}
url = "http://XXXXXXXXXXXXXXXX/getSimilarProducts?APIKEY=XXXXXXX"
response = requests.post(url, files=files)
After sending the request, when I check each variable with the debugger, the 'img' variable contains a temporary uploaded file like so:
<TemporaryUploadedFile: XXXXXXXXXXXXXX.png (image/png)>
What am I missing? And what should I do to overcome this error? Thanks a lot in advance!
the response should be within the with context
def post(self, request):
img = request.FILES['file']
with img.open('rb') as f:
files = {'fileToUpload': f}
url = 'http://XXXXXXXXXXXXXXXX/getSimilarProducts?APIKEY=XXXXXXX'
response = requests.post(url, files=files)
I have this existing piece of code that is used to upload files to my s3 bucket.
def get_user_upload_url(customer_id, filename, content_type):
s3_client = boto3.client('s3')
object_name = "userfiles/uploads/{}/{}".format(customer_id, filename)
try:
url = s3_client.generate_presigned_url('put_object',
Params={'Bucket': BUCKET,
'Key': object_name,
"ContentType": content_type # set to "image/png"
},
ExpiresIn=100)
except Exception as e:
print(e)
return None
return url
This returns to my client a presigned URL that I use to upload my files without a issue. I have added a new use of it where I'm uploading a png and I have behave test that uploads to the presigned url just fine. The problem is if i go look at the file in s3 i cant preview it. If I download it, it wont open either. The s3 web client shows it has Content-Type image/png. I visual compared the binary of the original file and the downloaded file and i can see differences. A file type tool detects that its is an octet-stream.
signature_file_name = "signature.png"
with open("features/steps/{}".format(signature_file_name), 'rb') as f:
files = {'file': (signature_file_name, f)}
headers = {
'Content-Type': "image/png" # without this or with a different value the presigned url will error with a signatureDoesNotMatch
}
context.upload_signature_response = requests.put(response, files=files, headers=headers)
I would have expected to have been returned a PNG instead of an octet stream however I'm not sure what I have done wrong . Googling this generally results in people having a problem with the signature because there not properly setting or passing the content type and I feel like I've effectively done that here proven by the fact that if I change the content type everything fails . I'm guessing that there's something wrong with the way I'm uploading the file or maybe reading the file for the upload?
So it is todo with how im uploading. So instead it works if i upload like this.
context.upload_signature_response = requests.put(response, data=open("features/steps/{}".format(signature_file_name), 'rb'), headers=headers)
So this must have to do with the use of put_object. It must be expecting the body to be the file of the defined content type. This method accomplishes that where the prior one would make it a multi part upload. So I think it's safe to say the multipart upload is not compatible with a presigned URL for put_object.
Im still piecing it altogether, so feel free to fill in the blanks.
Scenario: an image file stored in a GCP bucket need to be sent to a third-party REST endpoint via a POST
Question: Is this really the best pattern? Is there a more efficient less verbose way?
We have images being uploaded by a mobile app to a GCP Storage bucket. When the finalize event for the image upload fires we have a GCP Cloud Function (Python 3) that reacts to this by getting ref to uploaded image, downloads it to a temp file, and then uses that temp file as the image source for the POST. This is our current code and it works, but to my eye seems convoluted with the multiple open commands. More specifically: is there a better way to simply get the image blob from GCP Storage and simply attach it to the POST call without first saving it as a local file and then opening it so it can be attached to the POST?
def third_party_upload(data, context):
# get image from bucket
storage_client = storage.Client()
bucket = storage_client.bucket(data['bucket'])
image_blob = bucket.get_blob(data['name'])
download_path = '/tmp/{}.jpg'.format(str(uuid.uuid4())) #temp image file download location
# save GCP Storage blob as a temp file
with open(download_path, 'wb') as file_obj:
image_blob.download_to_file(file_obj)
# open temp file and send to 3rd-party via rest post call
with open(download_path, 'rb') as img:
files = {'image': (data['name'], img, 'multipart/form-data', {'Expires': '0'}) }
headers = {
'X-Auth-Token': api_key,
'Content-Type': 'image/jpg',
'Accept': 'application/json'
}
# make POST call
response = requests.post(third_party_endpoint, headers=headers, files=files)
print('POST response:', response)
Update: a couple of commenters have mentioned that Signed URLs are a possibility and I agree they are an excellent choice. However we are stuck with a requirement to include the image binary as the POST body. Signed-URLs won't work in this case.
The HTTP method POST requires data. You must provide that data in the HTTP request. There is no magic method to obtain Cloud Storage data except to read it. The process is to read the data from Cloud Storage and then provide that data to the POST request.
If you're able to send a URL to the third-party endpoint instead of the actual image contents, you could use Signed URLs give time-limited access to the image without needing to provide the 3rd party access to the bucket or make the bucket public.
More information here: https://cloud.google.com/storage/docs/access-control/signed-urls
I am currently messing around with the Kairos API and am trying to use Pythonista to take a new photo on my iPad and then upload that photo to the Kairos enroll API. I am able to get this to work fine with a URL image but for the life of me I am unable to get this to work by taking a photo with the photos module. From my understanding the photos module returns a PIL Image and I think I need to base64 encode that before uploading to the Kairos API??
Here is my code without using the photos module:
#import photos
import requests
#img = photos.capture_image()
url = "https://api.kairos.com/enroll"
values = """
{
"image": "https://images.pexels.com/photos/614810/pexels-photo-614810.jpeg?cs=srgb&dl=face-facial-hair-fine-looking-614810.jpg&fm=jpg",
"subject_id": "test",
"gallery_name": "test"
}
"""
headers = {
'Content-Type': 'application/json',
'app_id': '********',
'app_key': '************************'
}
request = requests.post(url, data=values, headers=headers)
response = request.content
print(response)
Im hoping that someone can help me out by showing me what I need to do to be able to accomplish this task. Any help is greatly appreciated.
Thank you in advance,
Colin
I was able to get this to work by converting the PIL Image with BytesIO and then encoding with base64:
with io.BytesIO() as output:
img = photos.capture_image()
img.save(output, 'JPEG')
contents = output.getvalue()
image = base64.b64encode(contents)
Hopefully this helps some in the future.
I'm trying to send a PIL image from a Django view to the browser for automatic download. The code below seems to work for many:
response = HttpResponse(content_type='image/jpg')
PIL_imageToSend.save(response, "JPEG")
response['Content-Disposition'] = 'attachment; filename="name.jpg"'
return response
I call the Django view through Ajax, and when I print the callback I get what looks to be the JPEG image, but no download is triggered. Am I missing something to get the download to automatically trigger?
The better/working approach is, first save your image to any location, then read it from there to prepare your response like this:
file = open('Read_From_Location/name.jpg', "rb").read()
rea_response = HttpResponse(file, content_type='image/jpeg')
rea_response['Content-Disposition'] = 'attachment; filename={}'.format('name.jpg')
return rea_response