파이썬 실습/GUI,윈도우 FTP(파일전송)프로그램 만들기

파이썬 GUI/윈도우 FTP(파일전송) 프로그램 만들기 - 5.부가기능-1)파일이름변경&파일삭제기능(리모트)

파기차차 2022. 12. 23. 21:10
728x90
반응형
SMALL
728x90
반응형

ㅁ 개요

 

O 프로그램 소개

 

 

 - 이번 글은 이전글(2022.12.02 - [파이썬 실습/GUI,윈도우 FTP(파일전송)프로그램 만들기] - 파이썬 GUI/윈도우 FTP(파일전송) 프로그램 만들기 - 4.기본기능-5)상하위폴더이동(로컬))에 이은 12번째 글로 FTP서버에 로그인 후 부가 기능인 리모트서버에서 파일이름을 변경하고, 파일을 삭제하는 방법에 대하여 설명합니다.

(본 블로그의 내용은 유튜브 동영상(파이썬 GUI Window FTP(파일전송) 프로그램 만들기-5.부가기능-1)파일이름변경&삭제(GUI/Window FTP program by Python-5.rename-1))에서 더욱 자세히 보실 수 있습니다.)

 

 

 

 

**본 프로그램은 아래 깃허브 사이트의 소스를 참고하여 작성하였으며, 세부내용은 아래 사이트를 참고하여 주시기 바랍니다.
[출처] 깃허브, f.tea.p | 작성자 armut
https://github.com/armut/f.tea.p

 

 

 

O 완성된 프로그램 실행 화면

 

 

 - 최종 완성된 프로그램의 결과화면은 아래와 같습니다.

 

1. 먼저 리모트 서버의 파일 이름은 변경하는 기능입니다.

 

처음 서버 로그인 후 아래와 같은 화면이 나타납니다.

 

 

 

아래와 같이 순서대로 (1)파일선택(2)이름변경 버튼을 클릭하면 왼쪽 하단에 라인에디트/푸시버튼이 나타나는데, 여기에 (3)변경하고자 하는 이름을 입력하고 (4)'OK' 버튼을 클릭하면

 

 

 

 

아래 (3)과 같이 이름이 정상적으로 변경된 것을 확인 할 수 있으며, (1)왼쪽 하단 레이블에 서버쪽 결과값을 보여줍니다.

 

 

 

 

 

2. 다음은 리모트 서버의 파일을 삭제하는 기능입니다.

 

아래와 같이 파일이 아닌 폴더를 선태 후 '파일삭제' 버튼 클릭 시 '권한관련 에러 발생' 으로 오류 메시지를 띄워 줍니다.

 

 

 

 다시 정상적으로 파일을 선택 후 '파일 삭제' 버튼 클릭 시

 

 

 

 

 'aa.txt' 파일이 정상적으로 삭제된 것을 확인할 수 있습니다.

 

 

 

 


 

O 시작전 준비 사항

 

 

 - 이번글을 실습하기 위해서는 이전글(2022.11.29 - [분류 전체보기] - 파이썬 GUI/윈도우 FTP(파일전송) 프로그램 만들기 - 2.사전 준비 작업)에서 설명한 ftp서버를 준비하셔야 합니다.

 


 

ㅁ 세부 내용

 

O 완성된 소스

 

# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic, QtCore, QtGui, QtWidgets
import os
import time
from PyQt5.QtGui import *

import ftplib
from ftplib import FTP
from ftplib import error_perm

form_class = uic.loadUiType("1.FTPClient2_C.ui")[0]

class FTPClass():
    __instance = None

    @staticmethod
    def tea():
        if not FTPClass.__instance:
            raise Exception("FTPClass를 먼저 인스턴스화 해야 합니다.")
        return FTPClass.__instance

    @staticmethod
    def reset():
        FTPClass.__instance = None

    def __init__(self, banana):
        if FTPClass.__instance:
            raise Exception("인스턴스가 이미 존재합니다.")
        FTPClass.__instance = banana


class LoginWindow(QtWidgets.QMainWindow, form_class):
    def __init__(self):
        super().__init__()
        self.setFixedSize(820,900)
        self.setWindowIcon(QIcon("pagichacha.png"))
        self.setupUi(self)

        self.lineEdit_5.returnPressed.connect(self.ftpConnect)
        self.pushButton.clicked.connect(self.ftpConnect)


    def ftpConnect(self):
        global result

        HOST = self.lineEdit.text()
        ID = self.lineEdit_4.text()
        PW = self.lineEdit_5.text()
        # PORT = int(self.lineEdit_6.text())

        FTPClass.reset()
        ret = FTPClass(FTP(HOST))
        print(ret,"+++++++++++++++++ 1")

        FTPClass.tea().encoding = 'utf-8'
        result = FTPClass.tea().login(ID, PW)
        print(result)

        if result and result.split(' ')[0] == '230':
            print("로그인 성공!!")
            # self.textEdit_2.setText(result+"로그인 성공!!")
            self.myWindow = MyWindow()
            self.myWindow.show()
            self.close()
        else:
            print("Something went wrong.")
            # self.textEdit_2.setText("로그인 실패!!!!, ID/PW 등을 정확히 입력했는지 확인해 보시기 바랍니다.!")


class MyWindow(QtWidgets.QMainWindow, form_class):
    def __init__(self):
        super().__init__()

        self.local_path =  None
        self.remote_path = None
        self.curr_local = None
        self.curr_remote = None

        self.setFixedSize(820,900)
        self.setWindowIcon(QIcon("pagichacha.png"))
        self.setupUi(self)

        if result and result.split(' ')[0] == '230':
            self.textEdit_2.setText(result+"로그인 성공!!")
        else:
            self.textEdit_2.setText(result+"로그인 실패!!")

        self.populate_local()
        self.populate_remote()

        self.doConnections()

    def tidy_up(self):
        self.label_8.show()
        # print(self.local_path,"++++ 1")
        self.pushButton_12.hide()
        # print(self.local_path,"++++ 1")
        self.lineEdit_3.hide()
        # print(self.local_path,"++++ 1")
        self.label_8.setText('Ready.')


    def doConnections(self):
        self.pushButton_3.clicked.connect(self.retr)
        self.pushButton_2.clicked.connect(self.stor)

        # 리모트 명령
        self.pushButton_4.clicked.connect(self.rnfr_rnto)
        self.pushButton_5.clicked.connect(self.dele)

        self.pushButton_16.clicked.connect(self.remote_enter)
        self.pushButton_15.clicked.connect(self.remote_up)


    def dele(self):
        self.tidy_up()
        print(self.curr_remote,"++++ 23")
        curr_remote = self.update_curr_remote() #<------ 여기서  부터 다시 시작
        print(curr_remote,"++++ 24")
        curr_local = self.update_curr_local()
        print(curr_local,"++++ 25")
        if self.curr_remote is not None:
            try:
                r = FTPClass.tea().delete(self.curr_remote)
                print(r,"++++ 22")
                self.label_8.setText(r + ' : ' + self.curr_remote)
                self.populate_remote()
                if '550' in r: # 퍼미션 에러가 발생하면 아래 익셉션에 rsise 한다.
                    raise Exception(r)

            except Exception as e:
                self.label_8.setText("서버쪽 권한 관련 에러가 발생하였습니다. 권한 부분을 확인해 주시기 바랍니다.(폴더선택 X): "+ str(e))
            except:
                self.label_8.setText("폴더가 아닌 파일을 선택해야 합니다.")
                import traceback
                traceback.print_exc()
        else:
            self.label_8.setText("리모트 파일 선택 후 '파일삭제' 버튼을 클릭해 주시기 바랍니다.!!")


    def remote_enter(self):
        self.tidy_up()
        if self.curr_remote:
            FTPClass.tea().cwd(os.path.join(self.remote_path, self.curr_remote))
            # print(FTPClass.tea().cwd(os.path.join(self.remote_path, self.curr_remote)))
            self.remote_path2 = FTPClass.tea().pwd()
            self.label_8.setText("현재 위치(리모트): "+self.remote_path2)
            print(self.remote_path, self.curr_remote,"++++ 16")
            self.populate_remote()
            self.curr_remote = None
        else:
            self.label_8.setText("리모트 폴더 선택 후 '폴더진입->' 버튼을 클릭해 주시기 바랍니다.")

    def remote_up(self):
        self.tidy_up()
        # Waffle.tea().cwd((os.sep).join(self.remote_path.split(os.sep)[:-1]))
        FTPClass.tea().cwd('..')
        # print(os.sep, self.remote_path,"++++ 17")
        self.remote_path2 = FTPClass.tea().pwd()
        self.label_8.setText("현재 위치(리모트): "+self.remote_path2)
        self.populate_remote()


    def rnfr_rnto(self):
        if self.curr_remote is not None:
            try: self.pushButton_12.clicked.disconnect()
            except: pass
            self.pushButton_12.clicked.connect(self.apply_rn)
            self.lineEdit_3.show()
            self.pushButton_12.show()
            self.label_8.hide()
        else:
            self.label_8.setText("리모트 사이트의 파일을 선택 하셔야 합니다.")

    def apply_rn(self):
        new_name = self.lineEdit_3.text()
        if new_name != '':
            try:
                r = FTPClass.tea().rename(self.curr_remote, new_name)
                self.tidy_up()
                self.label_8.setText(r + ': ' + new_name)
                self.populate_remote()
            except error_perm:
                self.label_8.setText("서버쪽 권한 관련 에러가 발생하였습니다. 권한 부분을 확인해 주시기 바랍니다.")
            except:
                self.label_8.setText("폴더가 아닌 파일을 선택해야 합니다.")
                import traceback
                traceback.print_exc()
        else:
            self.tidy_up()
            self.label_8.setText("리모트 파일의 변경할 이름을 입력해야 합니다.")


    def retr(self): 
        self.tidy_up()
        print(self.curr_remote,"++++ 18")
        if self.curr_remote is not None:
            try:
                r = FTPClass.tea().retrbinary('RETR' + ' ' + self.curr_remote, open(self.curr_remote, 'wb').write)
                print(r,"++++ 19")
                self.label_8.setText(r + ' ' + self.curr_remote)
            except:
                self.label_8.setText("폴더가 아닌 파일을 선택해야 합니다.")
                try:
                    os.remove(self.curr_remote)
                except:
                    pass
        else:
            self.label_8.setText("리모트 사이트의 파일을 선택한 후 '<='을 클릭하셔야 합니다.")

    def stor(self):
        self.tidy_up()
        print(self.curr_local,"++++ 20")
        if self.curr_local is not None:
            try:
                r = FTPClass.tea().storbinary('STOR ' + self.curr_local, open(self.curr_local, 'rb'))
                print(r,"++++ 21")
                self.label_8.setText(r + ' ' + self.curr_local)
                self.populate_remote()
            except error_perm:
                self.label_8.setText("서버쪽 권한 관련 에러가 발생하였습니다. 권한 부분을 확인해 주시기 바랍니다.")
            except:
                self.label_8.setText("폴더가 아닌 파일을 선택해야 합니다.")
                import traceback
                traceback.print_exc()
        else:
            self.label_8.setText("로컬 사이트의 파일을 선택한 후 '=>'을 클릭해야 합니다.")


    def populate_local(self):
        # self.tidy_up()
        self.local_path = os.getcwd() # 현재 디렉토리를 가져옴
        print(self.local_path,"++++ 1")
        self.fs_model = QtWidgets.QFileSystemModel() #파일시스템 모델의 객체 생성
        print(self.fs_model,"++++ 2")
        self.fs_model.setRootPath(self.local_path) # 현재 디렉토리를 루트로 설정
        print(self.fs_model.setRootPath(self.local_path),"++++ 3")
        self.treeView.setModel(self.fs_model) #트리뷰 설정
        print(self.treeView.setModel(self.fs_model),"++++ 4")
        self.treeView.setRootIndex(self.fs_model.index(self.local_path)) # 트리뷰의 루트를 현재 디렉토리로 지정
        print(self.treeView.setRootIndex(self.fs_model.index(self.local_path)),"++++ 5")
        print('local_path:', self.local_path)
        self.treeView.selectionModel().selectionChanged.connect(self.update_curr_local)


    def update_curr_local(self):
        # global curr_local
        self.tidy_up() # 화면 아래 레이블을 Ready로 바꿈
        try:
            index = self.treeView.selectedIndexes()[0]
            print(index,"++++ 6")
            # print(self.tree_l.selectedIndexes()[1],"++++ 6-1")
            self.curr_local = index.model().itemData(index).get(0) # 딕셔너리의 첫번째 값(선택한 폴더 또는 파일)
            print(index.model().itemData(index),"++++ 7")
            print('curr_local: ', self.curr_local)
        except:
            print("curr_local 변수에 로컬 파일이 선택되지 않았습니다.")
            self.curr_local = None
        return self.curr_local

    def populate_remote(self):
        self.remote_path = FTPClass.tea().pwd()
        print(self.remote_path,"++++ 8")
        remote_listing = FTPClass.tea().nlst()
        print(remote_listing,"++++ 9")
        self.dir_model = QtGui.QStandardItemModel()
        print(self.dir_model,"++++ 10")
        for ls in remote_listing:
            self.dir_model.appendRow(QtGui.QStandardItem(ls))
            # print(self.dir_model.appendRow(QtGui.QStandardItem(ls)),"++++ 11")
            # print(type(self.dir_model.appendRow(QtGui.QStandardItem(ls))),"++++ 11")
        self.listView.setModel(self.dir_model)
        print(self.listView.setModel(self.dir_model),"++++ 12")
        print('remote_path: ', self.remote_path)
        self.listView.selectionModel().selectionChanged.connect(self.update_curr_remote)

    def update_curr_remote(self):
        # global curr_remote
        self.tidy_up()
        try:
            index = self.listView.selectedIndexes()[0]
            print(index,"++++ 13")
            self.curr_remote = index.model().itemData(index).get(0)
            print(index.model().itemData(index),"++++ 14")
            print('curr_remote: ', self.curr_remote)
        except:
            print("curr_remote 변수에 리모트 파일이 선택되지 않았습니다.")
            self.curr_remote = None
        return self.curr_remote



app=QApplication(sys.argv)
# window = MyWindow()
window = LoginWindow()
window.show()
print("Before event loop")
app.exec_()
print("After event loop")

 

O 소스 다운로드 및 실행

 
 - 소스파일 다운로드 후 cmd 또는 파워쉘 등에서 아래와 같이 실행하시기 바랍니다.
( '1.FTPClient2_C.ui' 파일은 실행 파일(1.testConnect13.py)과 동일한 경로에 위치 시켜야 합니다.)
 > python 1.testConnect13.py

 

 


 
 

O 주요 내용

 

1. 먼저 리모트 서버의 파일 이름은 변경하는 기능입니다.

 

처음 서버 로그인 후 아래와 같은 화면이 나타납니다.

 

 

 

아래와 같이 순서대로 (1)파일선택(2)이름변경 버튼을 클릭하면 왼쪽 하단에 라인에디트/푸시버튼이 나타나는데, 여기에 (3)변경하고자 하는 이름을 입력하고 (4)'OK' 버튼을 클릭하면

 

 

 

 

아래 (3)과 같이 이름이 정상적으로 변경된 것을 확인 할 수 있으며, (1)왼쪽 하단 레이블에 서버쪽 결과값을 보여줍니다.

 

 

 

pushButton_4('이름변경') 버튼 클릭시 rnfr_rnto()함수를 호출합니다.

self.pushButton_4.clicked.connect(self.rnfr_rnto)

rnfr_rnto()함수에서는 파일(또는 폴더)을 선택했으면 if문 실행(if self.curr_remote is not None: )하고, 그렇지 않으면 else문에서 파일을 선택하라는 메시지를 보여줍니다.

 

왼쪽 하단의 변경할 파일의 이름을 입력 후  'OK'버튼 클릭 시 apply_rn()함수를 실행합니다.
apply_rn()함수는 lineEdit_3의 값(사용자가 입력한 변경할 파일이름)을 읽어서 실제 서버쪽 이름변경 명령인 rename()를 실행하여 이름을 변경 후 r변수에 실행결과 값을 저장합니다.

r = FTPClass.tea().rename(self.curr_remote, new_name)

 

 

 

 

 

 

 

2. 다음은 리모트 서버의 파일을 삭제하는 기능입니다.

 

아래와 같이 파일이 아닌 폴더를 선태 후 '파일삭제' 버튼 클릭 시 '권한관련 에러 발생' 으로 오류 메시지를 띄워 줍니다.

 

 

 

 다시 정상적으로 파일을 선택 후 '파일 삭제' 버튼 클릭 시

 

 

 

 

 'aa.txt' 파일이 정상적으로 삭제된 것을 확인할 수 있습니다.

 

 

pushButton_5('파일삭제') 버튼 클릭시 dele()함수를 호출합니다.

파일(또는 폴더)을 선택했으면 if문 실행하고, 그렇지 않으면 파일을 선택하라는 메시지를 띄워 줍니다.

if문에서는 실제 ftp서버의 delete()명령 실행하여 파일 삭제를 수행합니다.

r = FTPClass.tea().delete(self.curr_remote)

 

그리고 디버깅을 위해 서버쪽 명령 실행시 발생한 에러를 try~raise 구문으로 의도적으로 아래와 같이 에러를 일으킵니다.

if '550' in r: <-- 서버쪽 명령 실행시 퍼미션 에러가 발생하면 아래 익셉션에 rsise 한다.
   raise Exception(r)
except Exception as e: <-- 위에 에러가 발생하면 그 에러 메시지를 보여주기위해서 이렇게 표현한 것임
   self.label_8.setText("서버쪽 권한 관련 에러가 발생하였습니다. 권한 부분을 확인해 주시기 바랍니다.(폴더선택 X): "+ str(e)) <-- 문자열함수인 str()로 e를 감싸 줘야 함

 

 

 

 

 


 

 

O 소스 분석

(내용이 많으므로 필요한 부분만 보시면 좋습니다.)

 

- 아래 소스 분석 내용 중 1~50번 항목은 이전글과 동일하며, 51~57 항목만 새로 추가 되었습니다.

 

1.form_class = uic.loadUiType("1.FTPClient2_C.ui")[0] <-- qt disigner로 만든 UI를 사용하겠다.
2.class FTPClass(): <-- ftp서버 연결을 위한 클래스 선언
3.@staticmethod <-- 다른 클래스에서 사용 가능하도록 staticmethod 키워드를 사용(이 키워드 아래에 정의된 함수를 다른 클래스에서 사용 가능)

4.
@staticmethod
def tea(): <-- 인스턴스를 리턴
    if not FTPClass.__instance:
        raise Exception("FTPClass를 먼저 인스턴스화 해야 합니다.")
    return FTPClass.__instance

 

5.
@staticmethod
def reset(): <-- 인스턴스를 'None'로 설정
    FTPClass.__instance = None

 

6.
def __init__(self, banana): <-- 클래스의 인스턴스 초기화
    if FTPClass.__instance:
        raise Exception("인스턴스가 이미 존재합니다.")
    FTPClass.__instance = banana

 

7.
class LoginWindow(QtWidgets.QMainWindow, form_class):
    self.setupUi(self) <-- form_class UI를 사용하겠다

8.self.lineEdit_5.returnPressed.connect(self.ftpConnect) <-- lineEdit_5(패스워드) 입력 후 엔터 시 ftpConnect()함수 호출
9.self.pushButton.clicked.connect(self.ftpConnect) <-- pushButton(연결버튼) 클릭 시 ftpConnect()함수 호출
10. HOST = self.lineEdit.text() <-- lineEdit(호스트)의 값을 읽어서 HOST변수에 저장(아래 해설 동일함)
11.ID = self.lineEdit_4.text()
12.PW = self.lineEdit_5.text()
13.FTPClass.reset() <-- LoginWindow클래스 내에서 다른 클래스(FTPClass)의 함수를 직접 호출(@staticmethod로 선언해줘서 가능), 인스턴스를 'None'로 설정
14.ret = FTPClass(FTP(HOST)) <-- ftp서버에 연결
15.FTPClass.tea().encoding = 'utf-8' # 서버 접속 후 디렉토리 또는 파일명이 한글이면 깨지는 것을 방지
16.result = FTPClass.tea().login(ID, PW) <-- 연결된 ftp서버에 로그인 시도

17.
if result and result.split(' ')[0] == '230': <-- 서버쪽 리턴 값이 '230' 이면 로그인 성공
    print("로그인 성공!!")
    self.textEdit_2.setText(result+"로그인 성공!!") <-- 화면에 '로그인 성공' 메시지 출력
else:
    print("Something went wrong.")
    self.textEdit_2.setText("로그인 실패!!!!, ID/PW 등을 정확히 입력했는지 확인해 보시기 바랍니다.!")

 

18.

def ftpConnect(self):
    global result <-- 다른 클래스에서 result변수를 사용하기 위해 global 키워드 사용

 

19.

if result and result.split(' ')[0] == '230':
    print("로그인 성공!!")
    # self.textEdit_2.setText(result+"로그인 성공!!")
    self.myWindow = MyWindow() <-- MyWindow클래스의 myWindow인스턴스 생성
    self.myWindow.show() <-- myWindow인스턴스의 show()함수로 새창을 띄움
    self.close() <-- 기존 창은 close

 

20.
class MyWindow(QtWidgets.QMainWindow, form_class):
    if result and result.split(' ')[0] == '230':
        self.textEdit_2.setText(result+"로그인 성공!!") <-- 새창에 로그인 메시지를 보여주기 위해 MyWindow클래스에 넣어줌
    else:
        self.textEdit_2.setText(result+"로그인 실패!!")

 

21.self.populate_local() <-- 초기화 함수에서 populate_local()함수 호출

 

22.
def populate_local(self):
    self.local_path = os.getcwd() <-- 현재 디렉토리를 가져옴
    self.fs_model = QtWidgets.QFileSystemModel() <-- 파일시스템 모델의 객체 생성
    self.fs_model.setRootPath(self.local_path) <-- 현재 디렉토리를 루트로 설정
    self.treeView.setModel(self.fs_model) <-- 트리뷰 설정
    self.treeView.setRootIndex(self.fs_model.index(self.local_path)) <-- 트리뷰의 루트를 현재 디렉토리로 지정
    print('local_path:', self.local_path)

23.
def tidy_up(self):
    self.label_8.show() <-- label_8을 보여줌
    self.pushButton_12.hide() <-- pushButton_12을 숨김
    self.lineEdit_3.hide() <-- lineEdit_3 을 숨김
    self.label_8.setText('Ready.') <-- label_8의 내용을 'Ready.' 로 설정

24.self.treeView.selectionModel().selectionChanged.connect(self.update_curr_local) <-- 트리뷰의 선택 이벤트 발생시 update_curr_local()함수 호출

25.
def update_curr_local(self):
    self.tidy_up() <-- 화면 아래 레이블을 Ready로 바꿈
    try:
        index = self.treeView.selectedIndexes()[0] <-- 트리뷰 인덱스 설정
        self.curr_local = index.model().itemData(index).get(0) <-- 딕셔너리의 첫번째 값(선택한 폴더 또는 파일)을 curr_local변수에 할당
        print('curr_local: ', self.curr_local)
    except:
        print("curr_local 변수에 로컬 파일이 선택되지 않았습니다.")
        self.curr_local = None
    return self.curr_local

26.self.populate_remote() <-- 초기화 함수에서 리모트 서버의 파일/폴더목록을 보여주기 위해 populate_remote()함수 호출
27.self.remote_path = FTPClass.tea().pwd() <-- 리모트 서버의 현재 경로('/') 획득
28.remote_listing = FTPClass.tea().nlst() <-- 리모트 서버의 파일과 폴더 목록 획득
29.self.dir_model = QtGui.QStandardItemModel() <-- 모델객체 생성

30.

for ls in remote_listing: <-- 파일/폴더 목록을 하나씩 돌면서
    self.dir_model.appendRow(QtGui.QStandardItem(ls)) <--모델객체에 append
31.self.listView.setModel(self.dir_model) <-- 모델객체를 리스트뷰에 적용(여기서 실제 리스트뷰에 파일과 폴더 목록이 나타남)

32.self.listView.selectionModel().selectionChanged.connect(self.update_curr_remote) <-- 리스트뷰에서 파일 또는 폴더 선택시 update_curr_remote()함수 호출
33.index = self.listView.selectedIndexes()[0] <-- 리스트뷰 인덱스 설정
34.self.curr_remote = index.model().itemData(index).get(0) <-- 딕셔너리의 첫번째 값(선택한 폴더 또는 파일)을 curr_remote변수에 할당

35.self.doConnections() <-- doConnections()함수를 초기화 함수에 포함
36.
def doConnections(self): <-- doConnections()함수내에  ftp서버의 다양한 기능을 하는 함수를 추가해 줌
        self.pushButton_3.clicked.connect(self.retr) <-- pushButton_3(다운로드 버튼) 클릭 시 retr()함수 호출
37.if self.curr_remote is not None: <-- 파일 또는 폴더가 선택되어 있지 않으면
38.r = FTPClass.tea().retrbinary('RETR' + ' ' + self.curr_remote, open(self.curr_remote, 'wb').write) <-- FTPClass클래스의 인스턴스 생성 후 retrbinary()함수를 이용하여 파일다운로드 수행 후 서버쪽 결과값을 r변수에 저장

39.self.pushButton_2.clicked.connect(self.stor) <-- pushButton_2(업로드 버튼) 클릭 시 stor()함수 호출
40.if self.curr_local is not None: <-- 로컬PC의 파일 또는 폴더가 선택되어 있지 않으면
41.r = FTPClass.tea().storbinary('STOR ' + self.curr_local, open(self.curr_local, 'rb'))<-- FTPClass클래스의 인스턴스 생성 후 storbinary()함수를 이용하여 파일업로드 수행 후 서버쪽 결과값을 r변수에 저장

42.self.pushButton_16.clicked.connect(self.remote_enter) <-- pushButton_16 버튼('선택 폴더진입->') 클릭시 remote_enter()함수 호출

43.if self.curr_remote: <-- 폴더(또는 파일) 선택 시 if문 실행
44.FTPClass.tea().cwd(os.path.join(self.remote_path, self.curr_remote)) <-- 서버쪽 cwd()함수로 경로 변경(해당 폴더로 이동)
45.self.remote_path2 = FTPClass.tea().pwd() <-- 왼쪽 하단 레이블에 이동한 경로를 표시해 주기 위해 현재 위치(pwd())를 가져옴
46.self.label_8.setText("현재 위치(리모트): "+self.remote_path2) <-- 위에서 가져온 현재 위치를 레이블에 보여줌47.self.pushButton_13.clicked.connect(self.local_enter) <-- pushButton_13('선택 폴더진입->' 버튼) 버튼 클릭 시 local_enter()함수 호출
48.if self.curr_local: <-- 폴더(또는 파일)이 선택되었으면 if문 실행, 그렇지 않으면 else문 실행
49.os.chdir(os.path.join(self.local_path, self.curr_local)) <-- 로컬의 디렉토리를 변경(부모디렉토리경로+현재 선택한 폴더를 join으로 합쳐서 그 경로로 이동)
50.self.pushButton_14.clicked.connect(self.local_up) <-- pushButton_14('^' 버튼) 버튼 클릭 시 local_up()함수 호출
os.chdir('..') <-- 로컬의 상위 디렉토리로 이동


51.self.pushButton_5.clicked.connect(self.dele) <-- pushButton_5('파일삭제') 버튼 클릭시 dele()함수 호출
52.if self.curr_remote is not None: <-- 파일(또는 폴더)을 선택했으면 if문 실행
53.r = FTPClass.tea().delete(self.curr_remote) <-- 실제 ftp서버의 delete()명령 실행하여 파일 삭제 수행

54.

if '550' in r: <-- 서버쪽 명령 실행시 퍼미션 에러가 발생하면 아래 익셉션에 rsise 한다.
   raise Exception(r)
except Exception as e: <-- 위에 에러가 발생하면 그 에러 메시지를 보여주기위해서 이렇게 표현한 것임
   self.label_8.setText("서버쪽 권한 관련 에러가 발생하였습니다. 권한 부분을 확인해 주시기 바랍니다.(폴더선택 X): "+ str(e)) <-- 문자열함수인 str()로 e를 감싸 줘야 함
55.self.pushButton_4.clicked.connect(self.rnfr_rnto) <-- pushButton_4('이름변경') 버튼 클릭시 rnfr_rnto()함수 호출

56.

def rnfr_rnto(self):
    if self.curr_remote is not None: <-- 파일(또는 폴더)을 선택했으면 if문 실행
        self.pushButton_12.clicked.connect(self.apply_rn) <-- 왼쪽 하단의 'OK'버튼 클릭시 apply_rn()함수 실행
57.
def apply_rn(self):
    new_name = self.lineEdit_3.text() <-- lineEdit_3의 값(사용자가 입력한 변경할 파일이름)을 읽어서
    if new_name != '': <-- lineEdit_3의 값이 비어있지 않으면 if문 실행
        try:
            r = FTPClass.tea().rename(self.curr_remote, new_name) <-- 실제 서버쪽 이름변경 명령인 rename()를 실행하여 이름을 변경 후 r변수에 실행결과 값을 저장
        except error_perm: <-- 혹시라도 서버쪽 권한관련 에러가 발생하면
            self.label_8.setText("서버쪽 권한 관련 에러가 발생하였습니다. 권한 부분을 확인해 주시기 바랍니다.") <-- 에러메시지 출력

 

 

 


 

ㅁ 정리

 

O 우리가 배운 내용

 
 - 오늘은 FTP서버에 로그인 후 리모트 ftp서버에서 파일이름을 변경하고, 파일을 삭제하는 부가기능에 대하여 알아봤습니다.
 
 
 
 
 
 - 오늘 우리가 배운 내용을 두줄로 정리해 보면 아래와 같습니다.
 
 
 > 1.서버에서 파일이름을 변경하는 명령인 rename()함수를 이용하여 파일이름을 변경r = FTPClass.tea().rename(self.curr_remote, new_name)
 
 > 2.서버에서 파일을 삭제하는 명령인 delete()함수를 이용하여 파일을 삭제
r = FTPClass.tea().delete(self.curr_remote)

 

 

 

 

 

오늘은 여기까지이며, 위의 내용이 유익하셨다면, 좋아요와 구독 부탁드립니다.

 

 

감사합니다.

728x90
반응형
LIST