3

this is my first post on here so I'm happy to be a part of the community. I have a fairly mundane question to ask, but it's been a fairly annoying problem so I'm hoping to find answers.

So I'm trying to use Python's FTPLIB module to retrieve a binary file.

The code entered directly into the interpreter looked like this:

>>> from ftplib import FTP 

>>> ftp = FTP('xxx.xx.xx.x')  # IP of target device

>>> ftp.login()

>>> file = "foobar.xyz" # target file 

>>> ftp.retrbinary("RETR " + file, open('filez.txt', 'wb').write)

While certain functions are working (I can view all files on the device, get the welcome message from the FTP server application, and even rename files), when I try to execute the last command above, I get

    error_perm                                Traceback (most recent call last)

/Users/user_name/<ipython console> in <module>()

/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in retrlines(self, cmd, callback)
    419         if callback is None: callback = print_line
    420         resp = self.sendcmd('TYPE A')
--> 421         conn = self.transfercmd(cmd)
    422         fp = conn.makefile('rb')
    423         while 1:

/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in transfercmd(self, cmd, rest)
    358     def transfercmd(self, cmd, rest=None):
    359         """Like ntransfercmd() but returns only the socket."""
--> 360         return self.ntransfercmd(cmd, rest)[0]
    361 
    362     def login(self, user = '', passwd = '', acct = ''):

/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in ntransfercmd(self, cmd, rest)
    327             if rest is not None:
    328                 self.sendcmd("REST %s" % rest)
--> 329             resp = self.sendcmd(cmd)
    330             # Some servers apparently send a 200 reply to

    331             # a LIST or STOR command, before the 150 reply


/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in sendcmd(self, cmd)
    241         '''Send a command and return the response.'''
    242         self.putcmd(cmd)
--> 243         return self.getresp()
    244 
    245     def voidcmd(self, cmd):

/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in getresp(self)
    216             raise error_temp, resp
    217         if c == '5':
--> 218             raise error_perm, resp
    219         raise error_proto, resp
    220 

error_perm: 502 Command not implemented.

I've looked through the ftplib source but my programming experience with these sorts of task is pretty limited, as I normally use Python for math and haven't had to work with FTP before.

So, if anyone could give me some ideas on a solution, that would be a huge help. Alternatively, if you could suggest another solution path in a different language that would be equally helpful.

mbm
  • 1,902
  • 2
  • 19
  • 28

3 Answers3

8

It looks like you are logging-in anonymously (no username/password specified in ftp.login()) thus you get permission error. Try logging-in with

ftp.login(user='foo', passwd='bar')

instead.

Edit: here is a short example of ftplib usage (simplistic, without error handling):

#!/usr/bin/env python

from ftplib import FTP

HOST   = "localhost"
UNAME  = "foo"
PASSWD = "bar"
DIR    = "pub"
FILE   = "test.test"

def download_cb(block):
    file.write(block)

ftp = FTP(HOST)
ftp.login(user=UNAME, passwd=PASSWD)
ftp.cwd(DIR)

file = open(FILE, "wb")
ftp.retrbinary("RETR " + FILE, download_cb)
file.close()
ftp.close()

Note: In case retrbinary doesn't work, you can also try setting binary mode explicitly with ftp.sendcmd("TYPE I") before calling it. This is non-typical case, however may help with some exotic ftp servers.

barti_ddu
  • 10,179
  • 1
  • 45
  • 53
  • This actually wasn't it. The default anonymous arguments were accepted. If there had been a username/password rejection, I wouldn't have been able to, eg, rename files on the device. But I was. The problem came when I tried to retrieve files. – mbm Dec 04 '10 at 21:20
  • @mbm - You need a space between the "RETR" and the filename. It's easier (i.m.o., anyway) to just use `urllib.urlretrieve` for simple cases like this, though. (Edit: nevermind just realized that you have a space there already...) – Joe Kington Dec 04 '10 at 21:27
  • @mbm: weird :) can you connect with ordinary `ftp` command-line client and retrieve same test file? – barti_ddu Dec 04 '10 at 21:28
1

If your case is really that simple, there's no need to use the ftplib modlue. urllib.urlretrieve will do what you need. E.g.:

import urllib
urllib.urlretrieve('ftp://xxx.xx.xx.x/foobar.xyz', filename='filez.txt')

Note: this would be urllib.request.urlretrieve in python 3.x, by the way...

And if the ftp server isn't anonymous, you just specify the username and password in the url string as you normally would (e.g. ftp://user:password@server/path/to/file). Of course, the ftp protocol sends the password in plain text, and you've stored the username and password in the script, so this is insecure for multiple reasons, but that's true regardless of which library you use.

Joe Kington
  • 275,208
  • 71
  • 604
  • 463
0

Works fine for me, try with other server (i've tested with a debian ftp server.):

ftp = FTP('xx.xx.xx.xx')'

ftp.login()

'230 Login successful.'

file="robots.txt"

ftp.retrlines("RETR "+file, open('filez.txt', 'wb').write)

'226 File send OK.'

Community
  • 1
  • 1