I'm want to POST a file from a python3 client to cherrypy. I'm using the requests library.
My client code:
import requests
url = 'http://127.0.0.1:8080/upload'
files = {'file.zip': open('file.zip', 'rb')}
r = requests.post(url, files=files)
My server code:
import os
import tempfile
import shutil
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8,
'server.max_request_body_size' : 0,
'server.socket_timeout' : 60
}
}
class App:
#cherrypy.config(**{'response.timeout': 3600})
#cherrypy.expose()
def upload(self):
'''Handle non-multipart upload'''
destination = os.path.join('/home/uvv/upload')
with open(destination, 'wb') as f:
shutil.copyfileobj(cherrypy.request.body, f)
return 'Okay'
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
The server returns an error:
127.0.0.1 - - [17/Aug/2016:11:38:49] "POST /upload HTTP/1.1" 400 2083 "" "python-requests/2.10.0"
It's usefull to get information from response. When you send a request, you receive a response. From this response you can get information about HTTP code where 200 means OK and 400 means bad request. That's the text you can see in your cherrypy log: POST /upload HTTP/1.1" 400. To get more information, print the response text using print(r.text)
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import requests
url = 'http://127.0.0.1:9090/upload'
files = {'ufile': open('file.txt', 'rb')}
r = requests.post(url, files=files)
print(r)
print(r.text)
If you use the code above with the code bellow, it's working example of uploading file to cherrypy server.
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 9090,
'server.thread_pool' : 8,
'server.max_request_body_size' : 0,
'server.socket_timeout' : 60
}
}
class App:
#cherrypy.expose
def upload(self, ufile):
upload_path = os.path.normpath('/path/to/project/data/')
upload_file = os.path.join(upload_path, ufile.filename)
size = 0
with open(upload_file, 'wb') as out:
while True:
data = ufile.file.read(8192)
if not data:
break
out.write(data)
size += len(data)
out = '''
length: {}
filename: {}
mime-type: {}
''' .format(size, ufile.filename, ufile.content_type, data)
return out
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
Replace the path /path/to/project/data/ to path that fits your project.
Related
i'am trying to port to python 3 this script :
import re
from os.path import basename
import os
from urllib.parse import urlparse,urlsplit
from urllib.request import urlopen,Request
import urllib
def url2name(url):
return basename(urlsplit(url)[2])
def download(url, out_path="."):
localName = url2name(url)
req = Request(url)
r = urlopen(req)
if r.info().has_key('Content-Disposition'):
# If the response has Content-Disposition, we take file name from it
localName = r.info()['Content-Disposition'].split('filename=')[1]
if localName[0] == '"' or localName[0] == "'":
localName = localName[1:-1]
elif r.url != url:
# if we were redirected, the real file name we take from the final URL
localName = url2name(r.url)
localName = os.path.join(out_path, localName)
f = open(localName, 'wb')
f.write(r.read())
f.close()
but i have a :
'HTTPMessage' object is not callable
r.info() seems to have problems
how to get the header info in python 3 ?
Try with this, you should use context managers:
def download(url, out_path="."):
localName = url2name(url)
req = Request(url)
with urlopen(req) as f:
content_disp = f.getheader('Content-Disposition')
if content_disp:
# If the response has Content-Disposition, we take file name from it
localName = content_disp.split('filename=')[1]
if localName[0] == '"' or localName[0] == "'":
localName = localName[1:-1]
elif f.url != url:
# if we were redirected, the real file name we take from the final URL
localName = url2name(f.url)
localName = os.path.join(out_path, localName)
with open(localName, 'wb') as fp:
fp.write(f.read())
I am trying to send a compressed numpy array (compressed with zlib) to the flask server with post request, but the compressed bytes object is getting changed in the server end. How to properly send the bytes object with requests post request so that I can decompress on the server end?
server.py
from flask import Flask
from flask_restful import Resource, Api, reqparse
import json
import numpy as np
import base64
# compression
import zlib
app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('imgb64')
class Predict(Resource):
def post(self):
data = parser.parse_args()
if data['imgb64'] == "":
return {
'data':'',
'message':'No file found',
'status':'error'
}
img = data['imgb64']
print('rec')
# decompress
print(type(img))
print(img)
dec = zlib.decompress(img) # this gives me error
if img:
pass
return json.dumps({
'data': 'done',
'message':'darknet processed',
'status':'success'
})
return {
'data':'',
'message':'Something when wrong',
'status':'error'
}
api.add_resource(Predict,'/predict')
if __name__ == '__main__':
app.run(debug=True, host = '0.0.0.0', port = 5000, threaded=True)
client.py
import numpy as np
import base64
import zlib
import requests
frame = np.random.randint(0,255,(5,5,3)) # dummy rgb image
# compress
data = zlib.compress(frame)
print('b64 encoded')
print(data)
print(len(data))
print(type(data))
r = requests.post("http://127.0.0.1:5000/predict", data={'imgb64' : data}) # sending compressed numpy array
This gives me the following error:
TypeError: a bytes-like object is required, not 'str'
So, I tried to convert the string to bytes object:
dec = zlib.decompress(img.encode()) # this gives me error
But, this one also gives me an error:
zlib.error: Error -3 while decompressing data: incorrect header check
I tried with other encodings, they also failed.
One thing I noticed is, when I print the compressed bytes in the client end, it reads:
b'x\x9c-\xcf?J\x82q\x00\x06\xe0\x0ftrVP\\C\xf4\x06\x9268\t\xcdR\x8ej.F\xa0\xe0\xd0\xa6\xa3\xe0V\x07\x10\x1cL\xc8\xd1\x03\xd4\xe4\t\x0c\x12\x84\xb6D\x0c#\xbc\x80O\xf0\x1b\x9e\xf5\xfdS\x89\xa2h\xcf\x9a\x03\xef\xc4\xf8cF\x92\r\xbf4i\x11g\xc83\x0f\x8c\xb9\xa2#\x8e\x1bn\xd91g\xc0\x91%\xd7\xdc\xf3M\x83<i:L\xa8\xf1\x19\xfa\xffw\xfd\xf0\xc5\x94:O\x9cH\x85\xcc6#\x1e\xc3\xf6\x05\xe5\xa0\xc7\x96\x04]J\\\x90\xa1\x1f~Ty\xe1\x8d\x15w|P\xe4\x95K\xb2!\xe3\x0cw)%I'
But on the server end, the received string is completely different:
�4ig�3���#�n�1g��%���M�<i:L����w��Ŕ:O�H��6#���ǖ]J\��~Ty�w|P�K�!�w)%I
I also tried to send the bytes as string, by
r = requests.post("http://127.0.0.1:5000/predict", data={'imgb64' : str(data)})
But, I can't decompress the data on the server end.
It seems, I can't send the zlib compressed bytes directly, so I used base64 to encode the data into ascii string.
So, in summary this worked for me, numpy array/any non-string data -> zlib compression -> base64 encode -> post request -> flask -> base64 decode -> zlib decompress
client.py
import numpy as np
import base64
import zlib
import requests
frame = np.random.randint(0,255,(5,5,3)) # dummy rgb image
# compress
data = zlib.compress(frame)
data = base64.b64encode(data)
data_send = data
data2 = base64.b64decode(data)
data2 = zlib.decompress(data2)
fdata = np.frombuffer(data2, dtype=np.uint8)
print(fdata)
r = requests.post("http://127.0.0.1:5000/predict", data={'imgb64' : data_send})
server.py
from flask import Flask
from flask_restful import Resource, Api, reqparse
import json
import numpy as np
import base64
# compression
import zlib
import codecs
app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('imgb64', help = 'type error')
class Predict(Resource):
def post(self):
data = parser.parse_args()
#print(data)
if data['imgb64'] == "":
return {
'data':'',
'message':'No file found',
'status':'error'
}
#img = open(data['imgb64'], 'r').read() # doesn't work
img = data['imgb64']
data2 = img.encode()
data2 = base64.b64decode(data2)
data2 = zlib.decompress(data2)
fdata = np.frombuffer(data2, dtype=np.uint8)
print(fdata)
if img:
return json.dumps({
'data': 'done',
'message':'darknet processed',
'status':'success'
})
return {
'data':'',
'message':'Something when wrong',
'status':'error'
}
api.add_resource(Predict,'/predict')
if __name__ == '__main__':
app.run(debug=True, host = '0.0.0.0', port = 5000, threaded=True)
I am trying to implement a python API in order to upload a file on my server but for an unknown reason, it doesn't run.
From my understanding, the app.py is not recognised
Here is my API.py
from flask_cors import CORS
from flask_restful import Api, Resource, reqparse
import sqlite3
import uuid
import os
import csv
import urllib.request
import threading
import queue as Queue
import subprocess
import json
import re
import datetime
app = Flask(__name__)
api = Api(app)
CORS(app)
class upload(Resource):
def post(self):
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def upload_file():
# check if the post request has the file part
if 'file' not in request.files:
resp = jsonify({'message' : 'No file part in the request'})
resp.status_code = 400
return resp
file = request.files['file']
int = str(request.form['int']) #true or false
if file.filename == '':
resp = jsonify({'message' : 'No file selected for uploading'})
resp.status_code = 400
return resp
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return json.dumps(data), 200
else:
resp = jsonify({'message' : 'Allowed file types are doc, etc.'})
resp.status_code = 400
return resp
api.add_resource(upload, "/api/v1/upload")
app.run(host='0.0.0.0', debug=True)
Here is my app.py
UPLOAD_FOLDER = '/home/xxxx/xxx/upload'
app = Flask(__name__)
#app.secret_key = "secret key"
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
Can you please help? Any suggestions?
As per the comment, it looks like you have two sepearate applications here.
I would just stick with the first one API.py, but you'll need to move the lines where you set the config variables into API.py:
So after this line in API.py:
app = Flask(__name__)
Immediately set the config values:
UPLOAD_FOLDER = '/home/xxxx/xxx/upload'
#app.secret_key = "something super secret"
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
Then execute with:
python API.py
I am trying to learn some Http Server in an udacity online academy. The thing is that the folllowing code is triggering the error Message: Unsupported method ('POST'). Error 501 Python:
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import parse_qs
class MessageHandler(BaseHTTPRequestHandler):
def do_POST(self):
# 1. How long was the message?
length = int(self.headers.get('Content-length', 0))
# 2. Read the correct amount of data from the request.
data = self.rfile.read(length).decode()
# 3. Extract the "message" field from the request data.
message = parse_qs(data)["message"][0]
# Send the "message" field back as the response.
self.send_response(200)
self.send_header('Content-type', 'text/plain; charset=utf-8')
self.end_headers()
self.wfile.write(message.encode())
if __name__ == '__main__':
server_address = ('', 8000)
httpd = HTTPServer(server_address, MessageHandler)
httpd.serve_forever()
Which Python? Your code is correct. Tested it right now, it sends the response.
The only modification I've made is
#message = parse_qs(data)["message"][0]
message = 'hello'
Client code:
import requests
res = requests.post('http://localhost:8000/abc', data = {'key':'value'})
print(res)
Client gets 200 response
I want to use tornado to act as a proxy_server. The things are simple:
1. recieve request from some client
2. get url, param, headers from the request
3. use a proxy to make request with the things in 2
4. return response content, headers and the proxy to client
here's the code:
# -*- coding: utf-8 -*-
import json
import functools
import base64
import sys
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient
import tornado.web
import util
AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
class MainHandler(tornado.web.RequestHandler):
def handle_response(self, proxy, response):
if response.error:
self.write("error")
else:
headers = {name.lower(): value for name, value in response.headers.get_all()}
result = {
"content": base64.b64encode(response.body),
"headers": headers,
"proxy": proxy,
}
self.write(json.dumps(result))
self.finish()
#tornado.web.asynchronous
def post(self):
url = self.get_argument("url", "")
body = self.get_argument("body", "")
headers = json.loads(self.get_argument("headers", ""))
method = self.get_argument("method", "GET")
proxy = self.get_argument("proxy", "")
client = AsyncHTTPClient()
if proxy == "":
proxy = util.get_random_ip() # get a proxy string, like '118.150.101.160:18088'
proxy_info = proxy.split(":")
request_info = {
"method": method,
"headers": headers,
"connect_timeout": 2,
"request_timeout": 5,
"proxy_host": proxy_info[0],
"proxy_port": int(proxy_info[1]),
}
cf = functools.partial(self.handle_response, proxy)
if method == "POST":
request_info["body"] = body
client.fetch(url, callback=cf, **request_info)
if __name__ == "__main__":
port = sys.argv[1]
print "start at port : ", port
application = tornado.web.Application([(r"/proxy", MainHandler)])
server = HTTPServer(application)
server.listen(int(port))
IOLoop.instance().start()
when the requests growing to about 300 per min(5/s not too high), the memory start to goes wrong. I use "glances" to check system info, at the begining, the memory used is 16.6M, but 3 min after, the memory raise to 107.8M, 10 min latter, the number can up to 8518.6M, that's horrible!
I don't know why, any help?