[Python] FTP에 접속하여 파일 다운로드, 업로드하는 방법(ftplib)


Development note/Python  2020. 2. 19. 09:00

안녕하세요. 명월입니다.


이 글은 Python에서 FTP에 접속하여 파일 다운로드, 업로드하는 방법(ftplib)에 대한 글입니다.


이전에 제가 Window환경에서 FTP Server를 만드는 방법에 대하 소개한 적이 있습니다.

링크 - [CentOS] FTP 설정, vsftpd 설정

링크 - [Window] FTP 서버를 구축하는 방법


FTP 프로토콜을 이전 만큼은 아니지만, 여러가지 파일 전송 프로토콜로써 아직 사용하는 곳이 있기에 소개하겠습니다.

C#으로 FTP 클라이언트를 설명했었는데 비슷한 흐름으로 작성하겠습니다.

링크 - [C#] FTP에 접속해서 파일 다운로드, 업로드하는 방법

# FTP 모듈
from ftplib import FTP
# os 모듈(디렉토리를 다루기 위해)
import os

# ftp의 파일 리스트와 디렉토리 리스트를 구하는 함수
def get_list_ftp(ftp, cwd, files = [], directories = []):
  data = []
  # 파라미터로 지정된 위치로 디렉토리 이동.
  ftp.cwd(cwd)
  # 파일 정보를 읽어 드림(\r\n 구분)
  ftp.dir(data.append)
  # 파일 정보를 읽어 드린 정보를 사용.
  # MM-DD-YY HH:MM <DIR> 디렉토리이름
  for item in data:
    # 위에서 공백으로 디렉토리 이름까지의 위치를 찾음.
    pos = item.rfind(' ')
    # 이름만 구별해냄.
    name = cwd + item[pos+1:]
    # string에 <DIR>이 포함되어 있는지 확인(포함되어 있으면 디렉토리임)
    if item.find('<DIR>') > -1:
      # 디렉토리 리스트에 추가.
      directories.append(name)
      # 재귀적 함수 요청으로 아래 디렉토리 탐색
      get_list_ftp(ftp, name+'/', files, directories)
    else:
      # 파일의 경우는 파일 리스트에 추가
      files.append(name)
  # 파일과 디렉토리 리스트를 리턴.
  return files, directories

# 로컬 드라이브의 파일 리스트와 디렉토리 리스트를 구하는 함수.
def get_list_local(path, files = [], directories = []):
  # 파일 리스트를 구함.
  for file in os.listdir(path):
    # 파일의 전체 패스는 ath + file 명으로 설정.
    item = path + file
    # item이 디렉토리인지 파일인지 확인.
    if os.path.isdir(item):
      # 디렉토리 리스트에 추가
      directories.append(item + "\\")
      # 디렉토리의 경우는 하위 디렉토리를 탐색.
      get_list_local(item + "\\", files, directories)
    else :
      # 파일 리스트에 추가.
      files.append(item)
  # 파일과 디렉토리 리스트를 리턴.
  return files, directories

##************FTP 모두 삭제***************
# FTP 커넥션을 얻음.
with FTP('localhost') as ftp:
  # 디버그 모드로 설정 (설정하면 FTP 서버와 통신하는 내용이 콘솔에 표시됨)
  ftp.set_debuglevel(1)
  # 로그인 한다.
  ftp.login('FTPUser', 'password')
  # ftp의 파일과 디릭토리 구조를 취득한다.
  files, directories = get_list_ftp(ftp, '/')
  # 디렉토리의 경우 상위 폴더가 빠른 인덱스에 있기 때문에 리스트를 reverse한다.
  directories = directories[::-1]
  # 파일 삭제한다.
  for file in files:
    ftp.delete(file)
  # 폴더 삭제한다.
  for dir in directories:
    ftp.rmd(dir)

##************업로드***************
# FTP 커넥션을 얻음. (왜인지 같은 커넥션에서 폴더을 삭제하고 같은 이름으로 다시 생성하면 에러가 발생한다.)
with FTP('localhost') as ftp:
  # 디버그 모드로 설정 (설정하면 FTP 서버와 통신하는 내용이 콘솔에 표시됨)
  ftp.set_debuglevel(1)
  # 로그인 한다.
  ftp.login('FTPUser', 'password')
  # ftp 서버에 파일을 업로드할 로컬 디렉토리.
  upload_path = "D:\\ftptest\\upload\\"
  # 로컬 디렉토리의 파일과 디렉토리 구조를 취득한다.
  files, directories = get_list_local(upload_path)
  # 하위 디렉토리의 전부를 ftp에 작성한다.
  for directory in directories:
    # 절대 패스에서 상대 패스로 변경한다.
    # Window의 Path 구분자(\\)와 FTP의 Path 구분자는(/) 다르기 때문에 변경한다.
    path = directory.replace(upload_path,"").replace("\\","/")
    # ftp에 디렉토리를 작성한다.
    ftp.mkd(path)
    # 콘솔 출력
    print('Make directory is ' + path)
  # 파일을 업로드한다.
  for file in files:
    # 절대 패스에서 상대 패스로 변경한다.
    # Window의 Path 구분자(\\)와 FTP의 Path 구분자는(/) 다르기 때문에 변경한다.
    path = file.replace(upload_path,"").replace("\\","/")
    # 파일을 절대 패스로 읽어 온다.
    with open(file, 'rb') as localfile:
      # ftp로 업로드한다.
      ftp.storbinary('STOR ' + path, localfile)
    # 콘솔 출력
    print('Upload file is ' + path)

##************다운 로드***************
  # ftp 서버에서 파일을 다운로드 할 로컬 디렉토리.
  download_path = "D:\\ftptest\\download\\"
  # ftp의 파일과 디릭토리 구조를 취득한다.
  files, directories = get_list_ftp(ftp, '/')
  # 로컬 드라이브의 디렉토리를 생성한다.
  for directory in directories:
    # 상대 패스에서 절대 패스로 변경한다.
    dir = download_path + directory
    # 디렉토리가 존재하지 않으면 생성한다.
    if os.path.isdir(dir) is False:
      os.mkdir(dir)
  # 파일을 다운로드한다.
  for file in files:
    # IO로 파일 커넥션을 얻는다.
    with open(download_path + file, 'wb') as localfile:
      # ftp로 다운로드한다.
      ftp.retrbinary('RETR ' + file, localfile.write)
    # 콘솔 출력
    print('Download file is ' + file)

위의 소스는 먼저 ftp환경의 파일과 디렉토리를 모두 삭제하고 tptest/upload의 디렉토리의 파일과 하위 디렉토리, 파일을 전부 업로드를 하고, ftptest/download로 전부 다운로드하는 소스의 예제입니다.

위처럼 있는 파일들을 ftp://localhost(ftptest/ftp)로 업로드가 될 것입니다.

그리고 다시 위의 파일을 ftptest/download로 다운로드하는 것입니다.

프로그램을 실행하겠습니다.

다시 ftp://localhost(ftptest/ftp)로 이동하여 확인합니다.

ftp 서버에 제대로 업로드가 되었습니다.

다운로드도 잘 되었습니다.


ftp 소스가 java와 C#에 이어 3번째이네요. 패턴도 거의 비슷하니 FTP 프로토콜을 거의 외울 것같습니다.

특히나 python의 경우는 debug모드로 실행하면 FTP 통신하는 게 보이니 protocol의 상황을 더 자세히 볼 수 있어서 편하네요.


참조 - https://docs.python.org/3/library/ftplib.html

참조 - https://pythonprogramming.net/ftp-transfers-python-ftplib/

참조 - https://stackoverflow.com/questions/663171/how-do-i-get-a-substring-of-a-string-in-python


여기까지 Python에서 FTP에 접속하여 파일 다운로드, 업로드하는 방법(ftplib)에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.