Send file from client to server using XMLRPC? - python

I want to write Python code to send a file from client to server. server needs to save the file sent from the client. But my code have some bugs which I cannot fix. Below is my server code:
# server.py
from SimpleXMLRPCServer import SimpleXMLRPCServer
import os
server = SimpleXMLRPCServer(('localhost', 9000))
def save_data(data):
handle = open("x123.dat", "wb")
handle.write(data)
handle.close()
server.register_function(save_data, 'save_data')
server.serve_forever()
And the client code:
# client.py
import sys, xmlrpclib
proxy = xmlrpclib.Server('http://localhost:9000')
handle = open(sys.argv[1], "rb")
proxy.save_data(handle.read())
handle.close()
But then I run my code, the client returns the following error (this is on Windows):
Traceback (most recent call last):
File "client.py", line 6, in <module> proxy.save_data(handle.read())
File "c:\python27\lib\xmlrpclib.py", line 1224, in __call__
return self.__send(self.__name, args)
File "c:\python27\lib\xmlrpclib.py", line 1575, in __request
verbose=self.__verbose
File "c:\python27\lib\xmlrpclib.py", line 1264, in request
return self.single_request(host, handler, request_body, verbose)
File "c:\python27\lib\xmlrpclib.py", line 1297, in single_request
return self.parse_response(response)
File "c:\python27\lib\xmlrpclib.py", line 1473, in parse_response
return u.close()
File "c:\python27\lib\xmlrpclib.py", line 793, in close
raise Fault(**self._stack[0])
xmlrpclib.Fault: <Fault 1: "<class 'xml.parsers.expat.ExpatError'>:not well-formed (invalid token): line 7, column 1">
I have some questions:
How to fix the above bug?
My code needs to transfer some big files sometimes. Since my method is so simple, I doubt that it is efficient for moving big data. Could anybody please suggest a better method to move big files? (Of course it is better to use XMLRPC on Python)

Server side:
def server_receive_file(self,arg):
with open("path/to/save/filename", "wb") as handle:
handle.write(arg.data)
return True
Client side:
with open("path/to/filename", "rb") as handle:
binary_data = xmlrpclib.Binary(handle.read())
client.server_receive_file(binary_data)
This worked for me.

You want to look into the xmlrpclib Binary object. With this class you can encode and decode to/from a base64 string.

Here is how you do it:
#!/usr/bin/env python3.7
# rpc_server.py
# Fix missing module issue: ModuleNotFoundError: No module named 'SimpleXMLRPCServer'
#from SimpleXMLRPCServer import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCServer
import os
# Put in your server IP here
IP='10.198.16.73'
PORT=64001
server = SimpleXMLRPCServer((IP, PORT))
def server_receive_file(arg, filename):
curDir = os.path.dirname(os.path.realpath(__file__))
output_file_path = curDir + '/' + filename
print('output_file_path -> ({})'.format(output_file_path))
with open(output_file_path, "wb") as handle:
handle.write(arg.data)
print('Output file: {}'.format(output_file_path))
return True
server.register_function(server_receive_file, 'server_receive_file')
print('Control-c to quit')
server.serve_forever()
### rpc_client.py
#!/usr/bin/env python3.7
import os
# client.py
import sys
# The answer is that the module xmlrpc is part of python3
import xmlrpc.client
#Put your server IP here
IP='10.198.16.73'
PORT=64001
url = 'http://{}:{}'.format(IP, PORT)
###server_proxy = xmlrpclib.Server(url)
client_server_proxy = xmlrpc.client.ServerProxy(url)
curDir = os.path.dirname(os.path.realpath(__file__))
filename = sys.argv[1]
fpn = curDir + '/' + filename
print(' filename -> ({})'.format(filename))
print(' fpn -> ({})'.format(fpn))
if not os.path.exists(fpn):
print('Missing file -> ({})'.format(fpn))
sys.exit(1)
with open(fpn, "rb") as handle:
binary_data = xmlrpc.client.Binary(handle.read())
client_server_proxy.server_receive_file(binary_data, filename)

Related

"'NoneType' object has no attribute 'sendall'" when uploading file with Python ftplib

I made an FTP client to transfer files to an FTP server, but it keeps showing me the same errors no matter how I change the storbinary function
from ftplib import FTP
import os
from pathlib import Path
ftp = FTP()
ftp.connect('127.0.0.1', 2121)
ftp.login('user', '12345')
ftp.pwd()
ftp.retrlines('LIST')
ftp.quit()
def uploadfile():
filename = 'C:\\Users\\RE\\Desktop\\Software dev\\Ftp client and server\\test.txt'
localfile = open(filename, 'rb')
ftp.storbinary('STOR %s' %os.path.basename(filename), localfile, 1024)
localfile.close()
uploadfile()
ftp.retrlines('LIST')
fetchfile()
ftp.quit()
This is my error log
Traceback (most recent call last):
File "C:\Users\RE\Desktop\Software dev\Ftp client and server\ftp-client.py", line 24, in <module>
uploadfile()
File "C:\Users\RE\Desktop\Software dev\Ftp client and server\ftp-client.py", line 21, in uploadfile
ftp.storbinary('STOR %s' %os.path.basename(filename), localfile, 1024)
File "C:\Users\RE\AppData\Local\Programs\Python\Python37\lib\ftplib.py", line 503, in storbinary
self.voidcmd('TYPE I')
File "C:\Users\RE\AppData\Local\Programs\Python\Python37\lib\ftplib.py", line 277, in voidcmd
self.putcmd(cmd)
File "C:\Users\RE\AppData\Local\Programs\Python\Python37\lib\ftplib.py", line 199, in putcmd
self.putline(line)
File "C:\Users\RE\AppData\Local\Programs\Python\Python37\lib\ftplib.py", line 194, in putline
self.sock.sendall(line.encode(self.encoding))
AttributeError: 'NoneType' object has no attribute 'sendall'
You are effectively doing this:
ftp.quit()
uploadfile()
So I believe it's quite clear, why the upload fails. You close the session before the upload.

paramiko sftp script works on test server not on production

I'm a newb at python, so please excuse the hack job I created in order to transfer the contents of a folder into an ssh server.
The problem is that it works great in my test server, but as soon as run it against the actual server that I need to upload files for I receive the error below, and I'm not sure what it means.
I've googled it, but I can't figure it out, please help.
Thanks.
import paramiko
import glob
import os
from shutil import move
host = "192.168.1.87" #hard-coded
port = 22
password ="passwd" #hard-coded
username = "administator" #hard-coded
remotepath ='' #hard-coded
localpath = 'D:\\PH/PH_PROD\\PowerConnectInterf1_WINS\\bin\\data\\Sheex\\bc\\945\\'
#build filename array
os.chdir("D:/PH/PH_PROD/PowerConnectInterf1_WINS/bin/data/Sheex/bc/945")
filelist=[]
for files in glob.glob( "2016*" ):
f = open(files, 'r')
filelist.append(f.name)
f.close()
if (len(filelist)>0):
transport = paramiko.Transport((host, port))
transport.connect(username=username, password=password)
sftp = paramiko.SFTPClient.from_transport(transport)
for s in filelist:
#print remotepath+s
sftp.put(localpath+s,remotepath+s)
#os.rename(localpath+s,localpath+"945back/"+s)
sftp.close()
transport.close()
#print 'Upload done.'
Error:
D:\Scripts>python mysftp.py
Traceback (most recent call last):
File "mysftp.py", line 37, in <module>
sftp.put(localpath+s,remotepath+s)
File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 676, in put
return self.putfo(fl, remotepath, file_size, callback, confirm)
File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 634, in put
fo
with self.file(remotepath, 'wb') as fr:
File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 327, in ope
n
t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 730, in _re
quest
return self._read_response(num)
File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 781, in _re
ad_response
self._convert_status(msg)
File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 807, in _co
nvert_status
raise IOError(errno.ENOENT, text)
IOError: [Errno 2] Invalid file ID
It sounds like the path you are writing on the server doesn't exist. You should check and create if doesn't exist.

Python ftplib.error_perm 550: No such file or directory?

I've written a Python script that is part of my attempt to automate daily ftp transfers from my server. I've tested the script with a number of files and file types (html, mp3, png, jpg, etc.) and everything seems to work out fine so far.
However, when I try to download a simple text file, 'file.txt' (9 kb), the download fails, although I account for text files and switch from binary to text mode for the transfer. The following exception is thrown by ftplib:
ftplib.error_perm: 550 file.txt: No such file or directory
Here's my script:
from ftplib import FTP_TLS, error_perm
import os
def open_connection(server, user, pwd, work_dir=None):
global ftps
try:
ftps = FTP_TLS(host=server)
ftps.login(user=user, passwd=pwd)
ftps.prot_p() # switch to secure data connection
if work_dir != None:
ftps.cwd(work_dir)
else:
pass
except:
pass
def download_file(remote_path, local_path):
remote_file = os.path.basename(remote_path)
local_file_path = os.path.join(local_path, remote_file)
# differentiate between text and binary files
file_type, encoding = guess_type_and_encoding(remote_file)
# possibly needs a permission exception catch
if file_type.split("/")[0] == "text" and encoding == None:
# use text mode for transfer
local_file = open(local_file_path, 'w')
def callback(line): local_file.write(line + "\n")
ftps.retrlines("RETR " + remote_file, callback)
local_file.close()
else:
# use binary mode for transfer
local_file = open(local_file_path, 'wb')
ftps.retrbinary("RETR " + remote_file, local_file.write)
local_file.close()
return
def guess_type_and_encoding(filename):
from mimetypes import guess_type, add_type
add_type('text/x-python-win', '.pyw') # not in tables
mimetype, encoding = guess_type(filename, False) # allow extras
mimetype = mimetype or "?/?" # type unknown
return mimetype, encoding
open_connection(server, user, pwd, work_dir)
download_file("/files/dir/file.txt", "/Users/username/Desktop")
ftps.close()
I don't get why the error is raised!? The arguments 'remote_path' and 'local_path' are correctly provided. Both paths exist! 'file.txt' exists on the server under /files/dir and /Users/username/Desktop points to my desktop on OS X.
Here's the detailed ftplib error:
Traceback (most recent call last):
File "ftp2.py", line 138, in <module>
download_file("/files/dir/file.txt", "/Users/username/Desktop")
File "ftp2.py", line 93, in download_file
ftps.retrlines("RETR " + remote_file, callback)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ftplib.py", line 735, in retrlines
conn = self.transfercmd(cmd)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ftplib.py", line 376, in transfercmd
return self.ntransfercmd(cmd, rest)[0]
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ftplib.py", line 710, in ntransfercmd
conn, size = FTP.ntransfercmd(self, cmd, rest)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ftplib.py", line 339, in ntransfercmd
resp = self.sendcmd(cmd)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ftplib.py", line 249, in sendcmd
return self.getresp()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ftplib.py", line 224, in getresp
raise error_perm, resp
ftplib.error_perm: 550 file.txt: No such file or directory
Any help is greatly appreciated.
Thanks. :)
Try to
replace remote_file
in ftps.retrlines("RETR " + remote_file, callback)
with remote_path.

Python ftp download and archive

I have been trying to script a code with python to grade the main directory of that ftp and archive it into a the local pc. I am not an amateur coder and python is fairly new to me.
What I am getting as an error right now is.
File "C:\Users\Ali\Desktop\ftp_archiving_script.py", line 24, in <module>
ftpDownload = ftp.retrbinary('RETR', filename)
Code:
from ftplib import FTP
import zipfile
import os
try:
import zlib
compression = zipfile.ZIP_DEFLATED
except:
compression = zipfile.ZIP_STORED
modes = { zipfile.ZIP_DEFLATED: "deflated",
zipfile.ZIP_STORED: "stored",
}
#print "Logging in..."
with FTP('xxx.xxx.xxx') as ftp: #hostname goes here
ftp.login('xxxx','xxxx') #user followed by pass
#print "changing to root directory"
ftp.mlsd('//')
#print "Accessing files"
filenames = []
#print filenames
ftp.retrlines('NLST', filenames.append)
try:
for filename in filenames:
ftpDownload = ftp.retrbinary('RETR', filename)
with ZipFile(os.path.join('C:\\','DC_archive.zip'), 'w') as myzip:
myzip.write(ftpDownload, compress_type=compression)
myzip.close()
finally:
#print "closing"
ftp.close()
ftp.quit()
Can anyone enlighten me on this problem.
Thank you,
Update
try:
for filename in filenames:
with io.StringIO() as fObject:
ftp.retrbinary('RETR %s' %filename, fObject.write)
with ZipFile(os.path.join('C:\\','DC_archive.zip'), 'w') as myzip:
myzip.write(fObject, compress_type=compression)
myzip.close()
updated Traceback for #fals... Also this is using your code below and not the one I have at the top.
Traceback (most recent call last):
File "C:\Users\Ali\Desktop\ftp_archive2.py", line 20, in <module>
ftpDownload = ftp.retrbinary('RETR ' + filename, f.write)
File "C:\Python33\lib\ftplib.py", line 424, in retrbinary
with self.transfercmd(cmd, rest) as conn:
File "C:\Python33\lib\ftplib.py", line 386, in transfercmd
return self.ntransfercmd(cmd, rest)[0]
File "C:\Python33\lib\ftplib.py", line 352, in ntransfercmd
resp = self.sendcmd(cmd)
File "C:\Python33\lib\ftplib.py", line 259, in sendcmd
return self.getresp()
File "C:\Python33\lib\ftplib.py", line 233, in getresp
raise error_perm(resp)
ftplib.error_perm: 550 File not found
From the Python documentation for ftplib.retrbinary:
FTP.retrbinary(command, callback[, maxblocksize[, rest]])
Retrieve a file in binary transfer mode. command should be an
appropriate RETR command: 'RETR filename'. The callback function is
called for each block of data received, with a single string argument
giving the data block.
Nowhere does it indicate that it returns a file-like object or string.
Instead, you have to create your own callback to write to a file object.
with open('my-downloaded-file', 'wb') as f:
ftp.retrbinary('RETR %s' % filename, f.write)
Here, f.write is the callback which will receive data as it arrives from the socket. If you don't want to save the file to disk using open, you can use the StringIO module to simulate a file in memory.
Try following code:
import ftplib
from io import BytesIO
import os
import zipfile
REMOTE_HOST = 'xxx.xxx.xxx'
REMOTE_USER = '...'
REMOTE_PASS = '...'
REMOTE_DIR_PATH = '//'
LOCAL_ZIP_PATH = os.path.join(os.path.expanduser('~'), 'Desktop', 'DC_archive.zip')
ftp = ftplib.FTP(REMOTE_HOST)
try:
ftp.login(REMOTE_USER, REMOTE_PASS)
ftp.cwd(REMOTE_DIR_PATH)
filenames = ftp.nlst()
with zipfile.ZipFile(LOCAL_ZIP_PATH, 'w') as zf:
for filename in filenames:
with BytesIO() as f:
try:
ftpDownload = ftp.retrbinary('RETR ' + filename, f.write)
zf.writestr(filename, f.getvalue())
except ftplib.Error as e:
print('Skip {}: {}'.format(filename, e))
finally:
ftp.quit()

python simple wsgi file upload script - What is wrong?

import os, cgi
#self_hosting script
tags = """<form enctype="multipart/form-data" action="save_file.py" method="post">
<p>File: <input type="file" name="file"></p>
<p><input type="submit" value="Upload"></p>
</form>"""
def Request(environ, start_response):
# use cgi module to read data
form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ, keep_blank_values=True)
try:
fileitem = form['file']
except KeyError:
fileitem = None
if fileitem and fileitem.file:
fn = os.path.basename(fileitem.filename)
with open(fn, 'wb') as f:
data = fileitem.file.read(1024)
while data:
f.write(data)
data = fileitem.file.read(1024)
message = 'The file "' + fn + '" was uploaded successfully'
else :
message = 'please upload a file.'
start_response('200 OK', [('Content-type','text/html')])
return [message + "<br / >" + tags]
Above is my python wsgi script that receives a file and writes it to the disk. However, upon executing (with a file selected):
Internal Server Error
An error occurred processing this request.
Request handler failed
Traceback (most recent call last):
File "C:\Python26\Http\Isapi.py", line 110, in Request
return Handler(Name)
File "C:\Python26\Http\Isapi.py", line 93, in
"/apps/py/" : lambda P: RunWSGIWrapper(P),
File "C:\Python26\Http\Isapi.py", line 86, in RunWSGIWrapper
return RunWSGI(ScriptHandlers[Path])
File "C:\Python26\Http\WSGI.py", line 155, in RunWSGI
Result = Application(Environ, StartResponse)
File "\\?\C:\Python26\html\save_file.py", line 13, in Request
form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ, keep_blank_values=True)
File "C:\Python26\Lib\cgi.py", line 496, in __init__
self.read_multi(environ, keep_blank_values, strict_parsing)
File "C:\Python26\Lib\cgi.py", line 620, in read_multi
environ, keep_blank_values, strict_parsing)
File "C:\Python26\Lib\cgi.py", line 498, in __init__
self.read_single()
File "C:\Python26\Lib\cgi.py", line 635, in read_single
self.read_lines()
File "C:\Python26\Lib\cgi.py", line 657, in read_lines
self.read_lines_to_outerboundary()
File "C:\Python26\Lib\cgi.py", line 685, in read_lines_to_outerboundary
line = self.fp.readline(1<<16)
AttributeError: 'module' object has no attribute 'readline'
Being pretty daft at wsgi and cgi module I have no idea to progress at this moment. any clues?
environ['wsgi.input'] is a stream like object. You need to cache it firstly to file like object, eg: tempfile.TemporaryFile or StringIO (io.BytesIO in python3):
from tempfile import TemporaryFile
import os, cgi
def read(environ):
length = int(environ.get('CONTENT_LENGTH', 0))
stream = environ['wsgi.input']
body = TemporaryFile(mode='w+b')
while length > 0:
part = stream.read(min(length, 1024*200)) # 200KB buffer size
if not part: break
body.write(part)
length -= len(part)
body.seek(0)
environ['wsgi.input'] = body
return body
def Request(environ, start_response):
# use cgi module to read data
body = read(environ)
form = cgi.FieldStorage(fp=body, environ=environ, keep_blank_values=True)
# rest of your code
For safety reason consider to mask environ value which you pass to FieldStorage
Self - answer :
Sorry I didn't said that This was PyISAPIe-specific problem. the file-like object environ['wsgi.input'] does not have readline() method like other environ varibles in different wsgi implementations would do.
the (inefficient workaround) is saving everything from environ['wsgi.input'] into a tempfile and pass it to FieldStorage.
So :
import tempfile, cgi
def some_wsgi_app(environ, start_response):
temp_file = tempfile.TemporaryFile()
temp_file.write(environ['wsgi.input'].read()) # or use buffered read()
temp_file.seek(0)
form = cgi.FieldStorage(fp=temp_file, environ=environ, keep_blank_values=True)
# do_something #
temp_file.close() #close and destroy temp file
# ... start_response, return ... #
However above example will fail to operate properly if uploaded data from user is too big.

Categories