I want to send a file with python ftplib, from one ftp site to another, so to avoid file read/write processees.
I create a BytesIO stream:
myfile=BytesIO()
And i succesfully retrieve a image file from ftp site one with retrbinary:
ftp_one.retrbinary('RETR P1090080.JPG', myfile.write)
I can save this memory object to a regular file:
fot=open('casab.jpg', 'wb')
fot=myfile.readvalue()
But i am not able to send this stream via ftp with storbinary. I thought this would work:
ftp_two.storbinary('STOR magnafoto.jpg', myfile.getvalue())
But doesnt. i get a long error msg ending with 'buf = fp.read(blocksize) AttributeError: 'str' object has no attribute 'read'
I also tried many absurd combinations, but with no success. As an aside, I am also quite puzzled with what I am really doing with myfoto.write. Shouldnt it be myfoto.write() ?
I am also quite clueless to what this buffer thing does or require. Is what I want too complicated to achieve? Should I just ping pong the files with an intermediate write/read in my system? Ty all
EDIT: thanks to abanert I got things straight. For the record, storbinary arguments were wrong and a myfile.seek(0) was needed to 'rewind' the stream before sending it. This is a working snippet that moves a file between two ftp addresses without intermediate physical file writes:
import ftplib as ftp
from io import BytesIO
ftp_one=ftp.FTP(address1, user1, pass1)
ftp_two=ftp.FTP(address2, user2, pass2)
myfile=BytesIO()
ftp_one.retrbinary ('RETR imageoldname.jpg', myfile.write)
myfile.seek(0)
ftp_two.storbinary('STOR imagenewname.jpg', myfile)
ftp_one.close()
ftp_two.close()
myfile.close()
The problem is that you're calling
getvalue()
. Just don't do that:storbinary
requires a file-like object that it can callread
on.Fortunately, you have just such an object,
myfile
, aBytesIO
. (It's not entirely clear from your code what the sequence of things is here—if this doesn't work as-is, you may need tomyfile.seek(0)
or create it in a different mode or something. But aBytesIO
will work withstorbinary
unless you do something wrong.)But instead of passing
myfile
, you passmyfile.getvalue()
. Andgetvalue
"Returnsbytes
containing the entire contents of the buffer."So, instead of giving
storbinary
a file-like object that it can callread
on, you're giving it abytes
object, which is of course the same asstr
in Python 2.x, and you can't callread
on that.For your aside:
Look at the docs. The second parameter isn't a file, it's a callback function.
What you want is a function that appends each block of data to the end of
myfoto
. While you could write your own function to do that:… it should be pretty obvious that this function does exactly the same thing as the
myfoto.write
method. So, you can just pass that method itself.If you don't understand about bound methods, see Method Objects in the tutorial.
This flexibility, as weird as it seems, lets you do something even better than downloading the whole file into a buffer to send to another server. You can actually open the two connections at the same time, and use callbacks to send each buffer from the source server to the destination server as it's received, without ever storing anything more than one buffer.
But, unless you really need that, you probably don't want to go through all that complexity.
In fact, in general,
ftplib
is kind of low-level. And it has some designs (like the fact thatstorbinary
takes a file, whileretrbinary
takes a callback) that make total sense at that low level but seem very odd from a higher level. So, you may want to look at some of the higher-level libraries by doing a search at PyPI.