I'm trying to upload document in Azure blob storage through REST API but getting authentication error.
Sharing request header and url-
message = bytes(sas_token, 'utf-8')
secret = bytes(key, 'utf-8')
signature = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest())
signature = str(signature)
header = {
"x-ms-version": "2020-04-08",
"x-ms-date":"Fri, 12 Mar 2021 23:39:12 GMT",
"Content-Type": "application/pdf; charset=UTF-8" ,
"Content-Length": "1048576",
"x-ms-copy-source" : ""
"x-ms-blob-type": "BlockBlob",
"Authorization": "SharedKey myaccount:" + signature,
}
URL - "https://"+ account_name+".blob.core.windows.net/"+container_name+"/"+name
response- requests.put(url, headers=headers)
Response - <Response [400]>
<?xml version="1.0" encoding="utf-8"?><Error><Code>InvalidAuthenticationInfo</Code><Message>Authentication information is not given in the correct format. Check the value of Authorization header.
Also I'm not sure how should I pass document if my document location is OBJECT_LOCATION = "/home/meera/Downloads/download.pdf" I want to upload this download.pdf file in blob storage.
Documentation following - https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob
Looks like you're using Shared Access Signature (SAS) instead of storage account key for authorization. If that's the case, then you don't really need to compute the signature as the signature is already calculated in SAS.
Your code would be something like:
header = {
"Content-Type": "application/pdf; charset=UTF-8" ,
"Content-Length": "1048576",
"x-ms-blob-type": "BlockBlob"
}
URL - "https://"+ account_name+".blob.core.windows.net/"+container_name+"/"+name+"?"+sas_token
response- requests.put(url, headers=headers)
UPDATE
Please try this code.
account_name = "account-name"
container_name = "container-name"
name = "file-name.pdf"
sas_token = "?shared-access-signature-token"
filepath = "full path of the file-name.pdf"
with open(filepath, 'r') as f:
file_content = f.read() # Read whole file in the file_content string
headers = { "Content-Type": "application/pdf; charset=UTF-8", "x-ms-blob-type": "BlockBlob" }
url = "https://"+ account_name+".blob.core.windows.net/"+container_name+"/"+name+sas_token
response = requests.put(url, headers=headers, data=file_content)
Related
I was trying to upload the file from python script to Laravel backend but it's not getting uploaded to server, event getting the empty array in $request->all() and false in $request->has('file).
Here's the code that I am using.
api.php
Route::post('upload-file', UploadFilesController::class);
UploadFilesController
class UploadFilesController extends Controller
{
/**
* Handle the incoming request.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
return response()->json($request->all()); // getting empty error, also used the $request->hasFile('file') and getting false
}
}
Python script
import requests
# Set the API endpoint URL
url = "http://127.0.0.1:8000/api/upload-file"
# Set the file to be uploaded
file_path = "/path/to/file.pdf"
# Set the content type to "multipart/form-data"
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
# Read the file and create the form data
with open(file_path, 'rb') as f:
file_data = {'file': f}
# Send the POST request to the API endpoint
response = requests.post(url, headers=headers, files=file_data)
# Print the response from the server
print(response.text)
I also used the headers = {'Content-Type': 'multipart/form-data'} in place of header and it's also not working.
P.S the same code is working in Insomnia REST
Use headers = {'Content-Type': 'multipart/form-data'}
Try to log request inside laravel itself or use laravel debugger link https://sentry.io/for/laravel/?utm_source=google&utm_medium=cpc&utm_campaign=9779875598&utm_content=g&utm_term=%2Blaravel%20%2Bdebugger&device=m&gclid=CjwKCAiAy_CcBhBeEiwAcoMRHI8g4U4aAptz6YRYwRS7g3fYM8-_AdCE63-YAtSc3QHS9XdEamewuxoCPLQQAvD_BwE&gclid=CjwKCAiAy_CcBhBeEiwAcoMRHI8g4U4aAptz6YRYwRS7g3fYM8-_AdCE63-YAtSc3QHS9XdEamewuxoCPLQQAvD_BwE
I uploaded a file/csv to my Pivotal Cloud Foundry (PCF) ECS S3 storage. Which was created successfully.
import requests
url = "https://host/bucket/test.csv"
payload = {}
files = [
('file-name', open('test.csv','rb'))
]
headers = {
'X-Amz-Date': '20200622T112852Z',
'Authorization': 'AWS4-HMAC-SHA256 Credential=cred_endpoint, SignedHeaders=host;x-amz-date;x-amz-target, Signature=sigV4'
}
response = requests.put(url, headers=headers, data=payload, files=files)
When I try to access it I get a response like this
GET https://host/bucket/test.csv Status Code - 200
----------------------------017095580637364775189995
Content-Disposition: form-data; name="file-name"; filename="test.csv"
Content-Type: text/csv
rank,name
1,Michael
2,Jim
3,Dwight
----------------------------017095580637364775189995--
Which is the correct data but it's in this weird format with boundaries in the body, what would be the most robust way to read this kind of response body and generate the exact same or similar file/csv as the one upload?
Also am I even doing this correctly? Because all the docs imply there should be a [181(whatever the file size) bytes of object data] in the body instead of the actual content of the file.
I want to send discord.png to a text channel using Python and the Discord API, but I keep getting an error:
{"message": "Cannot send an empty message", "code": 50006"}
I think I've done everything as the Documentation said, and I don't know what's the problem.
I know, I could just use an already existing python library for this (like discord.py) but I'm only playing with the API, and I cant't figure out what is the issue here.
headers = {"Authorization": f"Bot {TOKEN}", "Content-Type": "multipart/form-data"}
f = open("discord.png", "rb")
file_data = f.read()
f.close()
file_data = base64.b64encode(file_data).decode()
payload_json = '{"content": "Discord", "tts": False}'
data = {
"content": "Discord",
"tts": False,
"file": file_data
}
headers["User-Agent"] = "DiscordBot"
#headers["Content-Type"] = "multipart/form-data" #edited but then realised i already set the content-type
headers["Content-Disposition"] = 'form-data; name="file" filename="discord.png"'
r = requests.post(f"{http_api}/channels/{CHANNEL_ID}/messages", data, headers=headers)
print(r.content)
There are a couple tiny mistakes in your code, but the main issue here is actually that the Discord documentation is very misleading.
When sending a file (or multiple files) this is not done by sending the contents in the file field of the request as the documentation indicates. Rather, the file(s) should be in the body of the request, as in the second example here:
POST /test.html HTTP/1.1
Host: example.org
Content-Type: multipart/form-data;boundary="boundary"
--boundary
Content-Disposition: form-data; name="field1"
value1
--boundary
Content-Disposition: form-data; name="field2"; filename="example.txt"
value2
--boundary--
The requests module allows us to do this very conveniently with the files parameter:
data = {"payload_json": payload_json}
headers = {"Authorization": f"Bot {TOKEN}", "User-Agent": "DiscordBot"}
r = requests.post(f"{http_api}/channels/{CHANNEL_ID}/messages", data,
files={"discord.png": file_data}, headers=headers)
And note there is no need to set the Content-Type or Content-Disposition headers – requests will take care of that for you.
For more examples/explanation of making requests with the files parameter, see this question.
I am writing a python script to send a mail using the Microsoft Graph API. The mail is sent to only those IDs which exists in our AAD.
I wrote the code:
from adal import AuthenticationContext
import adal
import string
import urllib
import json
import requests
import pprint
import base64
import mimetypes
API_VERSION = 'beta'
RESOURCE = 'https://graph.microsoft.com'
TENANT = <my tenant ID>
CLIENTID = <Client ID>
CLIENTSECRET = <sescret>
DOMAIN = <mydomain>
# AUTHENTICATION - get access token and update session header
def get_session():
authority_host_url = 'https://login.windows.net'
tenant = TENANT
authority_url = authority_host_url + '/' + tenant
service_endpoint = "https://graph.microsoft.com/"
client_id = CLIENTID
client_secret = CLIENTSECRET
# Retrieves authentication tokens from Azure Active Directory and creates a new AuthenticationContext object.
context = AuthenticationContext(authority_url, validate_authority=True, cache=None, api_version=None, timeout=300, enable_pii=False)
# Gets a token for a given resource via client credentials.
token_response = context.acquire_token_with_client_credentials(service_endpoint, client_id, client_secret)
# Create a persistent session with the access token
session = requests.Session()
session.headers.update({'Authorization': f'Bearer {token_response["accessToken"]}', 'SdkVersion': 'python-adal', 'x-client-SKU': 'DynaAdmin'})
#session.headers.update({'Authorization': f'Bearer {token_response["accessToken"]}'})
return session
def build_uri(url):
if 'https' == urllib.parse.urlparse(url).scheme:
return url
result = urllib.parse.urljoin(f'{RESOURCE}/{API_VERSION}/', url.lstrip('/'))
print("\nURL:\n")
print(result)
return result
def sendmail(*, session, subject=None, recipients=None, body='',
content_type='HTML', attachments=None):
"""Helper to send email from current user.
session = user-authenticated session for graph API
subject = email subject (required)
recipients = list of recipient email addresses (required)
body = body of the message
content_type = content type (default is 'HTML')
attachments = list of file attachments (local filenames)
Returns the response from the POST to the sendmail API.
"""
# Verify that required arguments have been passed.
if not all([session, subject, recipients]):
raise ValueError('sendmail(): required arguments missing')
# Create recipient list in required format.
recipient_list = [{'EmailAddress': {'Address': address}}
for address in recipients]
# Create list of attachments in required format.
attached_files = []
if attachments:
for filename in attachments:
b64_content = base64.b64encode(open(filename, 'rb').read())
mime_type = mimetypes.guess_type(filename)[0]
mime_type = mime_type if mime_type else ''
attached_files.append( \
{'#odata.type': '#microsoft.graph.fileAttachment',
'ContentBytes': b64_content.decode('utf-8'),
'ContentType': mime_type,
'Name': filename})
# Create email message in required format.
email_msg = {'Message': {'Subject': subject,
'Body': {'ContentType': content_type, 'Content': body},
'ToRecipients': recipient_list,
'Attachments': attached_files},
'SaveToSentItems': 'true'}
print("\nBody:\n")
print(email_msg)
# Do a POST to Graph's sendMail API and return the response.
resp = session.post(build_uri('/users/8368b7b5-b337ac267220/sendMail'), data=email_msg, stream=True, headers={'Content-Type': 'application/json'})
return resp
# ------------ RUN CODE ------------
session = get_session()
myRecipients = "sea.chen#mydomain.com;anj.dy#mydomain.com"
response = sendmail(session=session,
subject= "hai",
recipients=myRecipients.split(';'),
body="hai this is a new mail")
print("\nRequest headers: \n")
print(response.request.headers)
print("\nResponse: \n")
print(response)
print(response.text)
The output I got is :
Body:
{'Message': {'Subject': 'hai', 'Body': {'ContentType': 'HTML', 'Content': 'hai this is a new mail'}, 'ToRecipients': [{'EmailAddress': {'Address': 'sea.chen#mydomain.com'}}, {'EmailAddress': {'Address': 'anj.dy#mydomain.com'}}], 'Attachments': []}, 'SaveToSentItems': 'true'}
URL:
https://graph.microsoft.com/beta/users/8368b7b5-b337ac267220/sendMail
Request headers:
{'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFEQ29NcGpKWHJ4VHE5Vkc5dGUtN0ZYVzRQOWpybFRZWXdIVmZieWJxd0dUVUtNRThqUG5Va0hFSlowU2JyUFRaaDlaSjdURklVMjRwV1RpOTQxZU5kaXpIeHdXdDk0ODNlYmY1OWE5IiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOWFmYjFmOGEtMjE5Mi00NWJhLWIwYTEtNmIxOTNjNzU4ZTI0LyIsIm9pZCI6ImI3NDY4ZDQxLTkxMWMtNGU4ZS1iMzA4LWY5NmM1ZGQ4MDVmMyIsInJvbGVzIjpbIlVzZXIuUmVhZFdyaXRlLkFsbCIsIkdyb3VwLlJlYWQuQWxsIiwiRGlyZWN0b3J5LlJlYWRXcml0ZS5BbGwiLCJHcm91cC5SZWFkV3JpdGUuQWxsIiwiRGlyZWN0b3J5LlJlYWQuQWxsIiwiVXNlci5SZWFkLkFsbCIsIk1haWwuU2VuZCJdLCJzdWIiOiJiNzQ2OGQ0MS05MTFjLTRlOGUtYjMwOC1mOTZjNWRkODA1ZjMiLCJ0aWQiOiI5YWZiMWY4YS0yMTkyLTQ1YmEtYjBhMS02YjE5M2M3NThlMjQiLCJ1dGkiOiJWX1d6UHJjUDhrQ000MGNPM0xZZkFBIiwidmVyIjoiMS4wIiwieG1zX3RjZHQiOjE0NTY0Nzc5MzB9.Nq0qdmfd4qAQWdnaFLVNKYWQ__t52jRYC09IsDlrSOoAhZU6d2M6ePAAaKFSR-Ss_NJ4o21FAbxRM8mRUumW3n1TFvARN0FDzjtZMG9mgIrPmYndfRQtcD3s7f5Q5JOQdtd5QDRVhPqVRNAwmC-o_TW5mm0p40oIR2Mc2MN_MB3dp-_0xH7fN3xsPzWi9yRR1-iHnvEjLmhKecY5pxCBO3RW5QVcAR6DH6kw0koV49cmjpIu-_gau4SFlF4kFdwEVXdv1jTeJj91lA02Ar9tR-2hQiPOaqsloSmKpaH0Tb4LwGQJBk2O8fiwy5Sv2NoKbi6QE2EPFck7jZPVBDh35g', 'SdkVersion': 'python-adal', 'x-client-SKU': 'DynaAdmin', 'Content-Type': 'application/json', 'Content-Length': '90'}
Response:
<Response [400]>
{
"error": {
"code": "BadRequest",
"message": "Unable to read JSON request payload. Please ensure Content-Type header is set and payload is of valid JSON format.",
"innerError": {
"request-id": "26ef0c0b-c362-401c-b8ed-48755a45d086",
"date": "2019-06-24T07:10:53"
}
}
}
The error I got is :
Unable to read JSON request payload. Please ensure Content-Type header
is set and payload is of valid JSON format.
So I tried the request in Graph Explorer and used the same URL and body I got printed in the above output. The request sent from Graph Explorer was successful and the mail was sent to the correct recipients. That means the content-body is valid.
It is obvious from the above output that the bearer token, and 'Content-Type': 'application/json' were passed as the header.
Then why I am getting the error while running the script?
Can anyone please help me?
Most likely the body of your POST is not properly formatted. When I did this with the requests library, I found I needed to set data like so:
data = json.dumps(email_msg)
The default for data is to form-encode, not pass as JSON. Have a look at https://learn.microsoft.com/outlook/rest/python-tutorial#contents-of-tutorialoutlookservicepy
Thanks for sharing this.
I had different issue when I was trying to get Graph API to send one-on-one chat message to Microsoft Teams. I could run the following code using http.client and get most of the messages posted in chat.
import http.client
headers = {'Content-type':'application/json',
"Authorization": f"Bearer {getGraphAccessToken()}"
}
body = {
"body": {
"contentType": "text",
"charset" : "utf-8",
"content": text
}}
connection = http.client.HTTPSConnection("graph.microsoft.com")
connection.request("POST",f"/v1.0/chats/{chatId}/messages",headers=headers, body = str(body))
However when I had certain emojis inside the text I got the following error:
"Unable to read JSON request payload. Please ensure Content-Type header is set and payload is of valid JSON format."
By switching the library to requests I was able to send the message with special emojis to work.
import requests
import json
headers = {'Content-type':'application/json',
"Authorization": f"Bearer {getGraphAccessToken()}"
}
body = {
"body": {
"contentType": "text",
"content": text
}}
requests.post(f"https://graph.microsoft.com/v1.0/chats/{chatId}/messages",headers=headers,data =json.dumps(body) )
I am trying to take the URL of an image, download it, and then upload it to wordpress using the Wordpress API. Here is my code:
def upload_media(self, media_url):
# Get the last path component
filename = media_url.split('/')[-1]
response = requests.get(media_url)
if response.status_code == 200:
image = Image.open(BytesIO(response.content))
upload_url = self.__upload_media_url()
headers = { "Content-Disposition": f'attachment; filename={filename}'}
files = { 'file': image.tobytes() }
request = requests.post(upload_url, auth=(self.username, self.password), files=files, headers=headers)
return request
Each time this fails with the following error:
{'code': 'rest_upload_unknown_error', 'message': 'Sorry, this file type is not permitted for security reasons.', 'data': {'status': 500}}
Originally I had the following code, which worked on my local machine. However, because I want to run this on Google Cloud Functions, I do not have access to the file system. Thus the process of open/close will not work.
def upload_media(self, media_url):
# Get the last path component
filename = media_url.split('/')[-1]
response = requests.get(media_url)
if response.status_code == 200:
with open(filename, 'wb') as file:
file.write(response.content)
upload_url = self.__upload_media_url()
headers = { "Content-Disposition": f'attachment; filename={filename}'}
files = { 'file': open(filename, 'rb')}
request = requests.post(upload_url, auth=(self.username, self.password), files=files, headers=headers)
os.remove(filename)
return request
Is there a way to do this?
According to https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.tobytes image.tobytes() produces RAW image data, not PNG or JPG compressed data - so, what you're trying to upload is not one of the allowed types.
Just drop this line:
image = Image.open(BytesIO(response.content))
and then use:
files = { 'file': BytesIO(response.content) }
I believe this should fix your problem.
Ultimately I had to make 2 changes to my original function.
Add a Content-type: image/jpeg
Use data= instead of file=
Here is the working function which requires no open/closing of any files:
def upload_media(self, media_url):
# Get the last path component
filename = media_url.split('/')[-1]
response = requests.get(media_url)
if response.status_code == 200:
upload_url = self.__upload_media_url()
headers = { "Content-Disposition": f"attachment; filename={filename}" , "Content-Type": "image/jpeg" }
return requests.post(upload_url, auth=(self.username, self.password), headers=headers, data=response.content)