I am trying to upload a local .csv file to a hipchat room using the requests and email libraries and the HipChat API. This is the code that I am using:
import os
import re
import sys
import requests
from email.mime.multipart import MIMEMultipart
from email.mime.multipart import MIMEBase
from email import encoders
def hipchat_file(token, room, filepath, host='api.hipchat.com'):
""" Send file to a HipChat room via API version 2
Parameters
----------
token : str
HipChat API version 2 compatible token - must be token for active user
room: str
Name or API ID of the room to notify
filepath: str
Full path of file to be sent
host: str, optional
Host to connect to, defaults to api.hipchat.com
"""
if not os.path.isfile(filepath): raise ValueError("File '{0}' does not exist".format(filepath))
url = "https://{0}/v2/room/{1}/share/file".format(host, room)
headers = {'Authorization': 'Bearer {}'.format(token),
'Accept-Charset': 'UTF-8',
'Content-Type': 'multipart/related'}
related = MIMEMultipart('related')
part = MIMEBase('application', "octet-stream")
part.set_payload(open(filepath, "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="{}"'.format(os.path.basename(filepath)))
related.attach(part)
raw_headers, body = related.as_string().split('\n\n', 1)
boundary = re.search('boundary="([^"]*)"', raw_headers).group(1)
headers['Content-Type'] = 'multipart/related; boundary="{}"'.format(boundary)
# r = requests.post(url, data = body, headers = headers)
r = requests.put(url, data = body, headers = headers)
r.raise_for_status()
print('done')
my_token = 'f0rh4r4mb3'
my_room = 'test'
my_file = r"L:\data\data\0\my_file.csv"
my_message = 'Check out this cool file' # optional
try:
hipchat_file(my_token, my_room, my_file)
except Exception as e:
msg = "[ERROR] HipChat file failed: '{0}'".format(e)
print(msg, file=sys.stderr)
sys.exit(1)
But I keep receiving the error code:
'405 Client Error: Method Not Allowed for url: https://api.hipchat.com/v2/room/test/share/file'
And when I go to that link I see this:
{
"error": {
"code": 405,
"message": "<p>The method is not allowed for the requested URL.</p>",
"type": "Method Not Allowed"
}
}
I think I've treied everything I could, including changing post to get and to put, but nothing worked. What can I try?
Related
This is my code for uploading to google drive with python requests using google-drive-api.
import sys
import json
import requests
from tqdm import tqdm
import requests_toolbelt
from requests.exceptions import JSONDecodeError
class ProgressBar(tqdm):
def update_to(self, n: int) -> None:
self.update(n - self.n)
def upload_file(access_token:str, filename:str, filedirectory:str):
metadata = {
"title": filename,
}
files = {}
session = requests.session()
with open(filedirectory, "rb") as fp:
files["file"] = fp
files["data"] = ('metadata', json.dumps(metadata), 'application/json')
encoder = requests_toolbelt.MultipartEncoder(files)
with ProgressBar(
total=encoder.len,
unit="B",
unit_scale=True,
unit_divisor=1024,
miniters=1,
file=sys.stdout,
) as bar:
monitor = requests_toolbelt.MultipartEncoderMonitor(
encoder, lambda monitor: bar.update_to(monitor.bytes_read)
)
r = session.post(
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart",
data=monitor,
allow_redirects=False,
headers={"Authorization": "Bearer " + access_token},
)
try:
resp = r.json()
print(resp)
except JSONDecodeError:
sys.exit(r.text)
upload_file("access_token", "test.txt", "test.txt")
When i am trying send file with data attribute in post request then file name did not send and with files attribute in post request then requests-toolbelt not working. How to fix this error ?
When I saw your script, I thought that the content type is not included in the request header. In this case, I think that the request body is directly shown in the uploaded file. I thought that this might be the reason for your current issue. In order to remove this issue, how about the following modification?
From:
r = session.post(
url,
data=monitor,
allow_redirects=False,
headers={"Authorization": "Bearer " + access_token},
)
To:
r = session.post(
url,
data=monitor,
allow_redirects=False,
headers={
"Authorization": "Bearer " + access_token,
"Content-Type": monitor.content_type,
},
)
In this case, from metadata = { "title": filename }, it supposes that url is https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart. Please be careful about this.
When you want to use Drive API v3, please modify metadata = { "title": filename } to metadata = { "name": filename }, and use the endpoint of https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart.
When the file is uploaded with Drive API v3, the value of {'kind': 'drive#file', 'id': '###', 'name': 'test.txt', 'mimeType': 'text/plain'} is returned.
By the way, when an error like badContent occurs in your testing, please try to test the following modification. When in the request body of multipart/form-data the file content is put before the file metadata, it seems that an error occurs. I'm not sure whether this is the current specification. But, I didn't know the order of request body is required to be checked.
From
files = {}
files["file"] = fp
files["data"] = ('metadata', json.dumps(metadata), 'application/json')
To
files = collections.OrderedDict(data=("metadata", json.dumps(metadata), "application/json"), file=fp)
Note:
I thought that in your script, an error might occur at file_size = os.path.getsize(filename). Please confirm this again.
When I tested your script by modifying the above modifications, I could confirm that a test file could be uploaded to Google Drive with the expected filename. In this case, I also modified it as follows.
files = collections.OrderedDict(data=("metadata", json.dumps(metadata), "application/json"), file=fp)
References:
Files: insert of Drive API v2
Files: create of Drive API v3
Upload file data
Metadata needs to be sent in the post body as json.
Python Requests post() Method
data = Optional. A dictionary, list of tuples, bytes or a file object to send to the specified url
json = Optional. A JSON object to send to the specified url
metadata = {
"name": filename,
}
r = session.post(
url,
json=json.dumps(metadata),
allow_redirects=False,
headers={"Authorization": "Bearer " + access_token},
)
Future readers can find below a complete script that also contains details on how to get access to the bearer token for HTTP authentication.
Most of the credit goes to the OP and answers to the OPs question.
"""
Goal: For one time upload of a large file (as the GDrive UI hangs up)
Step 1 - Create OAuth 2.0 Client ID + Client Secret
- by following the "Authentication" part of https://pythonhosted.org/PyDrive/quickstart.html
Step 2 - Get Access Token
- from the OAuth playground -> https://developers.google.com/oauthplayground/
--> Select Drive API v3 -> www.googleapis.com/auth/drive --> Click on "Authorize APIs"
--> Click on "Exchange authorization code for tokens" --> "Copy paste the access token"
--> Use it in the script below
Step 3 - Run file as daemon process
- nohup python -u upload_gdrive.py > upload_gdrive.log 2>&1 &
- tail -f upload_gdrive.log
"""
import sys
import json
import requests
from tqdm import tqdm
import requests_toolbelt # pip install requests_toolbelt
from requests.exceptions import JSONDecodeError
import collections
class ProgressBar(tqdm):
def update_to(self, n: int) -> None:
self.update(n - self.n)
def upload_file(access_token:str, filename:str, filepath:str):
metadata = {
"name": filename,
}
files = {}
session = requests.session()
with open(filepath, "rb") as fp:
files = collections.OrderedDict(data=("metadata", json.dumps(metadata), "application/json"), file=fp)
encoder = requests_toolbelt.MultipartEncoder(files)
with ProgressBar(
total=encoder.len,
unit="B",
unit_scale=True,
unit_divisor=1024,
miniters=1,
file=sys.stdout,
) as bar:
monitor = requests_toolbelt.MultipartEncoderMonitor(
encoder, lambda monitor: bar.update_to(monitor.bytes_read)
)
r = session.post(
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart",
data=monitor,
allow_redirects=False,
headers={
"Authorization": "Bearer " + access_token
, "Content-Type": monitor.content_type
},
)
try:
resp = r.json()
print(resp)
except JSONDecodeError:
sys.exit(r.text)
upload_file("<access_token>"
, "<upload_filename>", "<path_to_file>")
I have a model endpoint running in Azure Kubernetes Service and I am not using Django or Flask. I am sending local png files to score as follows:
import base64
import json
import cv2
import requests
img_path = 'C:/path/to/exampleImage.png'
link = aks_service.scoring_uri
api_key = aks_service.get_keys()[0]
def send2score(img_path, score_url, api_key):
headers = {'Content-Type': 'application/json',
'Authorization': ('Bearer ' + api_key)
}
img = cv2.imread(img_path)
string = base64.b64encode(cv2.imencode('.png', img)[1]).decode()
dict = {
'img': string
}
jsonimg2 = json.dumps(dict, ensure_ascii=False, indent=4)
resp = requests.post(url=link, data=jsonimg2, headers=headers)
print(resp.text)
send2score(img_path=img_path, score_url=link, api_key=api_key)
My question is: how can I get the file name (exampleImage.png) in the score script in Azure Kubernetes after I do the request.post? Please no Django or Flask specific methods
Bonus question: Feel free to suggest improvements in the way I am uploading the data (send2score function), this function is working, I get the score back, I just can't get the file name in the score script. Thank you!
According to your code, you send your image as base64 string. It cannot contain the file name. I think you need to define a parameter to store the file name in request body. Besides, you also can post the file as a Multipart-Encoded File with the requests module.
For example
send file
import requests
import magic
import os
url = ''
path = "D:/sample/image/faces.jpg"
mime = magic.Magic(mime=True)
headers = {
'Authorization': ('Bearer ' + 'cdff')
}
files = {'file': (os.path.basename(path), open(path, 'rb'), mime.from_file(path), {'Expires': '0'})}
res = requests.post(url, files=files, headers=headers)
print(res.content.decode('utf-8'))
My backend
from http.server import BaseHTTPRequestHandler, HTTPServer
import cgi
hostName =
hostPort =
class MyServer(BaseHTTPRequestHandler):
def do_POST(self):
try:
// use cgi to read file
form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ={
'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers['Content-Type'], })
file = form.list[0]
data =file.file.read()
#process data
.............
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes(
f"<html><head></head><body><h2>fileName : {file.filename}</h2>/html>", "utf-8"))
except Exception as e:
httperror = e.httperror if hasattr(e, "httperror") else 500
self.send_error(httperror, str(e)) # Send an error response
myServer = HTTPServer((hostName, hostPort), MyServer)
myServer.serve_forever()
I was overcomplicating things, I realized that I am sending the encoded image as a json in a dictionary. I can include other information, like the file name, in the dictionary:
import base64
import json
import cv2
import requests
img_path = 'C:/path/to/exampleImage.png'
link = aks_service.scoring_uri
api_key = aks_service.get_keys()[0]
def send2score(img_path, score_url, api_key):
headers = {'Content-Type': 'application/json',
'Authorization': ('Bearer ' + api_key)
}
img = cv2.imread(img_path)
string = base64.b64encode(cv2.imencode('.png', img)[1]).decode()
dict = {
'imgname': os.path.basename(img_path),
'img': string
}
jsonimg2 = json.dumps(dict, ensure_ascii=False, indent=4)
resp = requests.post(url=link, data=jsonimg2, headers=headers)
print(resp.text)
send2score(img_path=img_path, score_url=link, api_key=api_key)
And I can get the image and file name in the score script:
# Lots of code before
response = json.loads(path)
string = response['img']
jpg_original = base64.b64decode(string) # decode
jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8)
img0 = cv2.imdecode(jpg_as_np, flags=1) # image
img0name = response['imgname'] # file name
# Lots of code after
I am trying to connect to an api for which I was provided with link, certificate(.p12) and subscription key.
Having some issue while giving the certificate details. I am trying in the following 2 ways:
1.
import json
from requests_pkcs12 import get,post
url = 'https://....'
pkcs12_filename = '<certificate file path>'
pkcs12_password = '<certificate password>'
headers = {
# Request headers
'Subscription-Key': '<subscription key>',}
response = post(url, headers=headers, verify=False, pkcs12_filename=pkcs12_filename,pkcs12_password=pkcs12_password)
print(response.status_code)
import http.client, urllib.request, urllib.parse, urllib.error, base64
#file = "<certificate path>"
headers = {
'Subscription-Key': '<subscriptionkey>',
#'cert' : crypto.load_pkcs12(open(file, 'rb').read(), "<password>").get_certificate(),
'file' : '<certificate path>',
'password':'<certificate password>',}
params = urllib.parse.urlencode({
})
conn = http.client.HTTPSConnection('<api main link>')
conn.request("GET", "<api remaining link>" , params, headers)
response = conn.getresponse()
data = response.read()
print("Status: {} and reason: {}".format(response.status, response.reason))
conn.close()
I am new to API concept. Will someone help me over this?
Refered to this link: How to use .p12 certificate to authenticate rest api
But didn't get what i need to put in data variable
I am getting the error message "Bad Gateway
The proxy server received an invalid
response from an upstream server" with the following code:
import requests
url = "https://apis.company.com/v3/media"
attachments = 'media': ('x.mp3', open('x.mp3', 'r'))}
headers = {'content-type': "multipart/form-data",'cache-control': "no-cache"
'Authorization':"Bearer zzz" }
response = requests.post(url, files=attachments, headers = headers)
print response.text
I'm following the example in the requests Quickstart documentation, where it says "You can also pass a list of tuples to the data argument": http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file
What is causing this error and how can I fix it?
The main problem was that I set the content-type in the header. This code works:
import requests
url = 'https://apis.company.com/v3/media'
token = 'token-goes-here'
headers = { 'Authorization' : 'Bearer ' + token }
filename = 'x.mp3'
with open(filename, 'rb') as media_file:
attachments = {
'media': (filename, media_file, 'application/octet-stream')
}
response = requests.post(url, files = attachments, headers = headers)
print response.text
How can I send a file from my local computer to hipchat using a python API? I am currently using Hypchat but it is not well documented.
Here is my code so far:
import hypchat
hc = hypchat.HypChat("myKey")
room = hc.get_room('bigRoom')
I'm not sure how to proceed. I tried other methods such as this one but I keep getting the error:
[ERROR] HipChat file failed: '405 Client Error: Method Not Allowed for url: https://api.hipchat.com/v2/room/bigRoom/share/file'
This code allows me to send any file to a hipchat room:
# do this:
# pip install requests_toolbelt
from os import path
from sys import exit, stderr
from requests import post
from requests_toolbelt import MultipartEncoder
class MultipartRelatedEncoder(MultipartEncoder):
"""A multipart/related encoder"""
#property
def content_type(self):
return str('multipart/related; boundary={0}'.format(self.boundary_value))
def _iter_fields(self):
# change content-disposition from form-data to attachment
for field in super(MultipartRelatedEncoder, self)._iter_fields():
content_type = field.headers['Content-Type']
field.make_multipart(content_disposition = 'attachment',
content_type = content_type)
yield field
def hipchat_file(token, room, filepath, host='api.hipchat.com'):
if not path.isfile(filepath):
raise ValueError("File '{0}' does not exist".format(filepath))
url = "https://{0}/v2/room/{1}/share/file".format(host, room)
headers = {'Content-type': 'multipart/related; boundary=boundary123456'}
headers['Authorization'] = "Bearer " + token
m = MultipartRelatedEncoder(fields={'metadata' : (None, '', 'application/json; charset=UTF-8'),
'file' : (path.basename(filepath), open(filepath, 'rb'), 'text/csv')})
headers['Content-type'] = m.content_type
r = post(url, data=m, headers=headers)
if __name__ == '__main__:
my_token = <my token>
my_room = <room name>
my_file = <filepath>
try:
hipchat_file(my_token, my_room, my_file)
except Exception as e:
msg = "[ERROR] HipChat file failed: '{0}'".format(e)
print(msg, file=stderr)
exit(1)
Shout out to #Martijn Pieters