ezKit 1.7.5__tar.gz → 1.7.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ezkit-1.7.5/ezKit.egg-info → ezkit-1.7.7}/PKG-INFO +2 -1
- ezkit-1.7.7/ezKit/token.py +78 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/utils.py +28 -18
- ezkit-1.7.7/ezKit/xftp.py +222 -0
- {ezkit-1.7.5 → ezkit-1.7.7/ezKit.egg-info}/PKG-INFO +2 -1
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit.egg-info/SOURCES.txt +1 -0
- ezkit-1.7.7/ezKit.egg-info/requires.txt +1 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/setup.py +5 -6
- ezkit-1.7.5/ezKit/token.py +0 -44
- ezkit-1.7.5/ezKit/xftp.py +0 -194
- {ezkit-1.7.5 → ezkit-1.7.7}/LICENSE +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/MANIFEST.in +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/README.md +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/__init__.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/bottle.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/bottle_extensions.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/cipher.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/database.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/files.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/http.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/mongo.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/plots.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/qywx.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/redis.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/reports.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/sendemail.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit/zabbix.py +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit.egg-info/dependency_links.txt +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/ezKit.egg-info/top_level.txt +0 -0
- {ezkit-1.7.5 → ezkit-1.7.7}/setup.cfg +0 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
"""Token"""
|
2
|
+
import json
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
from loguru import logger
|
6
|
+
|
7
|
+
from .cipher import AESCipher
|
8
|
+
from .utils import datetime_now, datetime_offset, datetime_string_to_datetime, datetime_to_string, v_true
|
9
|
+
|
10
|
+
|
11
|
+
def generate_token(key: str = 'Fc0zXCmGKd7tPu6W', timeout: int = 3600, data: Any = None) -> (str | None):
|
12
|
+
try:
|
13
|
+
now = datetime_now()
|
14
|
+
|
15
|
+
if now is None:
|
16
|
+
return None
|
17
|
+
|
18
|
+
offset = datetime_offset(now, seconds=+timeout)
|
19
|
+
|
20
|
+
if offset is None:
|
21
|
+
return None
|
22
|
+
|
23
|
+
source = json.dumps(
|
24
|
+
obj={
|
25
|
+
"datetime": datetime_to_string(offset),
|
26
|
+
"data": data
|
27
|
+
},
|
28
|
+
default=str
|
29
|
+
)
|
30
|
+
|
31
|
+
cipher = AESCipher(key=key, algorithm='sha256')
|
32
|
+
|
33
|
+
return cipher.encrypt(source)
|
34
|
+
|
35
|
+
except Exception as e:
|
36
|
+
logger.exception(e)
|
37
|
+
return None
|
38
|
+
|
39
|
+
|
40
|
+
def parsing_token(token_string: str, key: str = 'Fc0zXCmGKd7tPu6W') -> (dict | None):
|
41
|
+
try:
|
42
|
+
if v_true(token_string, str) is False:
|
43
|
+
return None
|
44
|
+
|
45
|
+
cipher = AESCipher(key=key, algorithm='sha256')
|
46
|
+
|
47
|
+
target = cipher.decrypt(token_string)
|
48
|
+
|
49
|
+
if target is None:
|
50
|
+
return None
|
51
|
+
|
52
|
+
source: dict = json.loads(target)
|
53
|
+
|
54
|
+
source['datetime'] = datetime_string_to_datetime(source['datetime'])
|
55
|
+
|
56
|
+
return source
|
57
|
+
|
58
|
+
except Exception as e:
|
59
|
+
logger.exception(e)
|
60
|
+
return None
|
61
|
+
|
62
|
+
|
63
|
+
def certify_token(token_string: str, key: str = 'Fc0zXCmGKd7tPu6W') -> bool:
|
64
|
+
try:
|
65
|
+
|
66
|
+
result = parsing_token(token_string, key)
|
67
|
+
|
68
|
+
if result is None:
|
69
|
+
return False
|
70
|
+
|
71
|
+
if result.get('datetime') < datetime_now(): # type: ignore
|
72
|
+
return False
|
73
|
+
|
74
|
+
return True
|
75
|
+
|
76
|
+
except Exception as e:
|
77
|
+
logger.exception(e)
|
78
|
+
return False
|
@@ -793,10 +793,10 @@ def filesize(
|
|
793
793
|
# --------------------------------------------------------------------------------------------------
|
794
794
|
|
795
795
|
|
796
|
-
def resolve_path() -> str | None:
|
797
|
-
|
798
|
-
|
799
|
-
|
796
|
+
# def resolve_path() -> str | None:
|
797
|
+
# """resolve path"""
|
798
|
+
# # 获取当前目录名称
|
799
|
+
# return str(Path().resolve())
|
800
800
|
|
801
801
|
|
802
802
|
# def parent_path(
|
@@ -813,48 +813,51 @@ def resolve_path() -> str | None:
|
|
813
813
|
# return None
|
814
814
|
|
815
815
|
|
816
|
-
def
|
816
|
+
def realpath(
|
817
817
|
path: str,
|
818
818
|
debug: bool = False,
|
819
819
|
**kwargs
|
820
820
|
) -> str | None:
|
821
|
-
"""
|
821
|
+
"""获取对象真实路径"""
|
822
822
|
try:
|
823
|
+
# if v_true(debug, bool):
|
824
|
+
# logger.info(f"path: {path}")
|
825
|
+
# return os.path.realpath(path, **kwargs)
|
823
826
|
if v_true(path, str, debug=debug) is False:
|
824
827
|
return None
|
825
|
-
return str(Path(path, **kwargs).
|
828
|
+
return str(Path(path, **kwargs).resolve())
|
826
829
|
except Exception as e:
|
827
830
|
if v_true(debug, bool):
|
828
831
|
logger.exception(e)
|
829
832
|
return None
|
830
833
|
|
831
834
|
|
832
|
-
def
|
835
|
+
def current_dir(
|
833
836
|
path: str,
|
834
837
|
debug: bool = False,
|
835
838
|
**kwargs
|
836
839
|
) -> str | None:
|
837
|
-
"""
|
840
|
+
"""获取对象所在目录"""
|
838
841
|
try:
|
839
842
|
if v_true(path, str, debug=debug) is False:
|
840
843
|
return None
|
841
|
-
return str(Path(path, **kwargs).parent.
|
844
|
+
return str(Path(path, **kwargs).parent.resolve())
|
842
845
|
except Exception as e:
|
843
846
|
if v_true(debug, bool):
|
844
847
|
logger.exception(e)
|
845
848
|
return None
|
846
849
|
|
847
850
|
|
848
|
-
def
|
851
|
+
def parent_dir(
|
849
852
|
path: str,
|
850
853
|
debug: bool = False,
|
851
854
|
**kwargs
|
852
855
|
) -> str | None:
|
853
|
-
"""
|
856
|
+
"""获取对象所在目录的父目录"""
|
854
857
|
try:
|
855
|
-
if v_true(
|
856
|
-
|
857
|
-
return
|
858
|
+
if v_true(path, str, debug=debug) is False:
|
859
|
+
return None
|
860
|
+
return str(Path(path, **kwargs).parent.parent.resolve())
|
858
861
|
except Exception as e:
|
859
862
|
if v_true(debug, bool):
|
860
863
|
logger.exception(e)
|
@@ -981,7 +984,9 @@ def datetime_now(
|
|
981
984
|
"""获取当前日期和时间"""
|
982
985
|
_utc = kwargs.pop("utc", False)
|
983
986
|
try:
|
984
|
-
|
987
|
+
if _utc is True:
|
988
|
+
return datetime.datetime.now(datetime.timezone.utc)
|
989
|
+
return datetime.datetime.now(**kwargs)
|
985
990
|
except Exception as e:
|
986
991
|
if v_true(debug, bool):
|
987
992
|
logger.exception(e)
|
@@ -989,7 +994,7 @@ def datetime_now(
|
|
989
994
|
|
990
995
|
|
991
996
|
def datetime_offset(
|
992
|
-
datetime_instance: datetime.datetime,
|
997
|
+
datetime_instance: datetime.datetime | None = None,
|
993
998
|
debug: bool = False,
|
994
999
|
**kwargs
|
995
1000
|
) -> datetime.datetime | None:
|
@@ -1002,7 +1007,12 @@ def datetime_offset(
|
|
1002
1007
|
try:
|
1003
1008
|
if isinstance(datetime_instance, datetime.datetime):
|
1004
1009
|
return datetime_instance + datetime.timedelta(**kwargs)
|
1005
|
-
|
1010
|
+
|
1011
|
+
if _utc is True:
|
1012
|
+
return datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(**kwargs)
|
1013
|
+
|
1014
|
+
return datetime.datetime.now() + datetime.timedelta(**kwargs)
|
1015
|
+
|
1006
1016
|
except Exception as e:
|
1007
1017
|
if v_true(debug, bool):
|
1008
1018
|
logger.exception(e)
|
@@ -0,0 +1,222 @@
|
|
1
|
+
"""
|
2
|
+
ftplib: https://docs.python.org/3.11/library/ftplib.html
|
3
|
+
"""
|
4
|
+
import os
|
5
|
+
from ftplib import FTP
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
from loguru import logger
|
9
|
+
|
10
|
+
|
11
|
+
class XFTP:
|
12
|
+
"""XFTP"""
|
13
|
+
|
14
|
+
def __init__(self, host="127.0.0.1", port=21, username="anonymous", password="", encoding="UTF-8", debuglevel=0):
|
15
|
+
"""Initiation"""
|
16
|
+
self.ftp = FTP()
|
17
|
+
self.ftp.set_debuglevel(debuglevel)
|
18
|
+
self.host = host
|
19
|
+
self.port = port
|
20
|
+
self.username = username
|
21
|
+
self.password = password
|
22
|
+
self.encoding = encoding
|
23
|
+
self.retry = 1
|
24
|
+
|
25
|
+
def connect(self) -> bool:
|
26
|
+
"""FTP connect"""
|
27
|
+
try:
|
28
|
+
self.ftp.connect(host=self.host, port=self.port, timeout=10)
|
29
|
+
self.ftp.encoding = self.encoding
|
30
|
+
self.ftp.login(user=self.username, passwd=self.password)
|
31
|
+
logger.success("FTP connect success")
|
32
|
+
logger.info("-" * 80)
|
33
|
+
return True
|
34
|
+
except Exception as e:
|
35
|
+
# print(f"FTP connect error: {e}, retry...")
|
36
|
+
# if self.retry >= 3:
|
37
|
+
# print("FTP connect faild")
|
38
|
+
# return False
|
39
|
+
# self.retry += 1
|
40
|
+
# self.connect()
|
41
|
+
logger.exception(e)
|
42
|
+
return False
|
43
|
+
|
44
|
+
def close(self, info=None) -> bool:
|
45
|
+
"""FTP close"""
|
46
|
+
if info is not None:
|
47
|
+
logger.info(info)
|
48
|
+
try:
|
49
|
+
self.ftp.quit()
|
50
|
+
except Exception as e:
|
51
|
+
logger.exception(e)
|
52
|
+
self.ftp.close()
|
53
|
+
logger.info("-" * 80)
|
54
|
+
logger.success("FTP connect closed")
|
55
|
+
return True
|
56
|
+
|
57
|
+
def get_file_list(self, target='/') -> (list[str] | None):
|
58
|
+
"""Get file list"""
|
59
|
+
try:
|
60
|
+
self.chdir_to_remote(target)
|
61
|
+
return self.ftp.nlst()
|
62
|
+
except Exception as e:
|
63
|
+
logger.exception(e)
|
64
|
+
return None
|
65
|
+
|
66
|
+
def get_file_size(self, file, target="/") -> (int | None):
|
67
|
+
"""Get file size"""
|
68
|
+
try:
|
69
|
+
self.chdir_to_remote(target)
|
70
|
+
return self.ftp.size(file)
|
71
|
+
except Exception as e:
|
72
|
+
logger.exception(e)
|
73
|
+
return None
|
74
|
+
|
75
|
+
def mkdir(self, target="/") -> bool:
|
76
|
+
"""创建目录 (从 / 目录依次递增创建子目录. 如果目录存在, 创建目录时会报错, 所以这里忽略所有错误.)"""
|
77
|
+
try:
|
78
|
+
dir_list = target.split("/")
|
79
|
+
for i, _ in enumerate(dir_list):
|
80
|
+
dir_path = "/".join(dir_list[:i + 1])
|
81
|
+
try:
|
82
|
+
self.ftp.mkd(dir_path)
|
83
|
+
except Exception as e:
|
84
|
+
logger.exception(e)
|
85
|
+
return True
|
86
|
+
except Exception as e:
|
87
|
+
logger.exception(e)
|
88
|
+
return False
|
89
|
+
|
90
|
+
def chdir_to_remote(self, target="/") -> bool:
|
91
|
+
"""change to remote directory"""
|
92
|
+
try:
|
93
|
+
self.ftp.cwd(target)
|
94
|
+
return True
|
95
|
+
except Exception as e:
|
96
|
+
self.close(f"remote directory error: {target}")
|
97
|
+
logger.exception(e)
|
98
|
+
return False
|
99
|
+
|
100
|
+
def x_exit(self, info=None):
|
101
|
+
"""Exit"""
|
102
|
+
if info is not None:
|
103
|
+
logger.info(info)
|
104
|
+
# 注意: exit() 并不会退出脚本, 配合 try 使用
|
105
|
+
exit()
|
106
|
+
|
107
|
+
def x_exec(self, local_dir=".", local_file="", remote_dir="/", remote_file="", upload=False):
|
108
|
+
"""Download or Upload"""
|
109
|
+
|
110
|
+
bufsize = 1024
|
111
|
+
local_path = f"{local_dir}/{local_file}"
|
112
|
+
remote_path = f"{remote_dir}/{remote_file}"
|
113
|
+
|
114
|
+
info = 'Download'
|
115
|
+
if upload is True:
|
116
|
+
info = 'Upload'
|
117
|
+
|
118
|
+
# 检查参数
|
119
|
+
if upload is True:
|
120
|
+
if local_file == "":
|
121
|
+
self.close('Argument Miss: local file')
|
122
|
+
# 如果没有设置 远程文件 名称, 则使用 本地文件 名称
|
123
|
+
if remote_file == "":
|
124
|
+
remote_file = local_file
|
125
|
+
remote_path = f"{remote_dir}/{remote_file}"
|
126
|
+
else:
|
127
|
+
if remote_file == "":
|
128
|
+
self.close("Argument Miss: remote file")
|
129
|
+
# 如果没有设置 本地文件 名称, 则使用 远程文件 名称
|
130
|
+
if local_file == "":
|
131
|
+
local_file = remote_file
|
132
|
+
local_path = f"{local_dir}/{local_file}"
|
133
|
+
|
134
|
+
# 进入本地目录
|
135
|
+
try:
|
136
|
+
if upload is True:
|
137
|
+
# 检查本地目录
|
138
|
+
stat = Path(local_dir)
|
139
|
+
if stat.exists() is False:
|
140
|
+
self.close(f"Local directory error: {local_dir}")
|
141
|
+
else:
|
142
|
+
# 创建本地目录
|
143
|
+
Path(local_dir).mkdir(parents=True, exist_ok=True)
|
144
|
+
# 进入本地目录
|
145
|
+
os.chdir(local_dir)
|
146
|
+
except Exception as e:
|
147
|
+
logger.exception(e)
|
148
|
+
# 第一层 try 使用 self.x_exit() 无效, 直接使用 self.close()
|
149
|
+
self.close(f"Local directory error: {local_dir}")
|
150
|
+
|
151
|
+
# 上传或下载
|
152
|
+
try:
|
153
|
+
|
154
|
+
if upload is True:
|
155
|
+
|
156
|
+
# 上传
|
157
|
+
|
158
|
+
# 创建远程目录
|
159
|
+
if remote_dir != "/":
|
160
|
+
self.mkdir(remote_dir)
|
161
|
+
|
162
|
+
# 进入远程目录
|
163
|
+
self.chdir_to_remote(remote_dir)
|
164
|
+
|
165
|
+
# 上传文件
|
166
|
+
stat = Path(local_file)
|
167
|
+
if stat.exists() and stat.is_file():
|
168
|
+
with open(local_file, "rb") as fid:
|
169
|
+
self.ftp.storbinary(f'STOR {remote_file}', fid, bufsize)
|
170
|
+
logger.success(f"{info} success: {local_path.replace('//', '/')} -> {remote_path.replace('//', '/')}")
|
171
|
+
return True
|
172
|
+
|
173
|
+
self.x_exit(f"{info} error: {local_path.replace('//', '/')} is not exist")
|
174
|
+
|
175
|
+
else:
|
176
|
+
|
177
|
+
# 下载
|
178
|
+
|
179
|
+
# 进入远程目录
|
180
|
+
self.chdir_to_remote(remote_dir)
|
181
|
+
|
182
|
+
# 下载文件
|
183
|
+
if remote_file in self.ftp.nlst():
|
184
|
+
with open(local_file, "wb") as fid:
|
185
|
+
self.ftp.retrbinary(f'RETR {remote_file}', fid.write, bufsize)
|
186
|
+
logger.success(f"{info} success: {remote_path.replace('//', '/')} -> {local_path.replace('//', '/')}")
|
187
|
+
return True
|
188
|
+
|
189
|
+
self.x_exit(f"{info} error: {remote_path.replace('//', '/')} is not exist")
|
190
|
+
|
191
|
+
except Exception as e:
|
192
|
+
# 第一层 try 使用 self.x_exit() 无效, 直接使用 self.close()
|
193
|
+
# self.close('{} faild! Please check {} or {}'.format(info, local_path, remote_path))
|
194
|
+
self.close(f"{info} error: {e}")
|
195
|
+
return False
|
196
|
+
|
197
|
+
def handle_all(self, local_dir=".", remote_dir="/", upload=False):
|
198
|
+
"""Handle All"""
|
199
|
+
if upload is True:
|
200
|
+
# 检查本地目录
|
201
|
+
stat = Path(local_dir)
|
202
|
+
if stat.exists() is False:
|
203
|
+
self.close(f"Local directory error: {local_dir}")
|
204
|
+
# 获取文件列表
|
205
|
+
local_files = [f for f in os.listdir(local_dir) if os.path.isfile(os.path.join(local_dir, f))]
|
206
|
+
for i in local_files:
|
207
|
+
self.x_exec(local_dir=local_dir, remote_dir=remote_dir, local_file=i, upload=True)
|
208
|
+
else:
|
209
|
+
remote_files = self.get_file_list(remote_dir)
|
210
|
+
if remote_files is not None:
|
211
|
+
for i in remote_files:
|
212
|
+
self.x_exec(local_dir=local_dir, remote_dir=remote_dir, remote_file=i)
|
213
|
+
|
214
|
+
def retrlines(self, remote_dir="/", cmd="LIST"):
|
215
|
+
"""Retrlines"""
|
216
|
+
try:
|
217
|
+
self.chdir_to_remote(remote_dir)
|
218
|
+
print(self.ftp.retrlines(cmd))
|
219
|
+
self.close()
|
220
|
+
except Exception as e:
|
221
|
+
# 第一层 try 使用 self.x_exit() 无效, 直接使用 self.close()
|
222
|
+
self.close(e)
|
@@ -0,0 +1 @@
|
|
1
|
+
loguru>=0.7.0
|
@@ -3,15 +3,14 @@ from setuptools import find_packages, setup
|
|
3
3
|
|
4
4
|
setup(
|
5
5
|
name='ezKit',
|
6
|
-
version='1.7.
|
6
|
+
version='1.7.7',
|
7
7
|
author='septvean',
|
8
8
|
author_email='septvean@gmail.com',
|
9
9
|
description='Easy Kit',
|
10
10
|
packages=find_packages(exclude=['documents', 'tests']),
|
11
11
|
include_package_data=True,
|
12
|
-
python_requires='>=3.11'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# ]
|
12
|
+
python_requires='>=3.11',
|
13
|
+
install_requires=[
|
14
|
+
"loguru>=0.7.0"
|
15
|
+
]
|
17
16
|
)
|
ezkit-1.7.5/ezKit/token.py
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
|
3
|
-
from .cipher import AESCipher
|
4
|
-
from .utils import datetime_now, datetime_offset, datetime_string_to_datetime, datetime_to_string, v_true
|
5
|
-
|
6
|
-
|
7
|
-
def generate_token(key: str = 'Fc0zXCmGKd7tPu6W', timeout: int = 3600, data: any = None) -> None | str:
|
8
|
-
try:
|
9
|
-
source = json.dumps(
|
10
|
-
obj={
|
11
|
-
'datetime': datetime_to_string(datetime_offset(datetime_now(), seconds=+timeout)),
|
12
|
-
'data': data
|
13
|
-
},
|
14
|
-
default=str
|
15
|
-
)
|
16
|
-
cipher = AESCipher(key=key, algorithm='sha256')
|
17
|
-
return cipher.encrypt(source)
|
18
|
-
except:
|
19
|
-
return None
|
20
|
-
|
21
|
-
|
22
|
-
def parsing_token(token_string: str, key: str = 'Fc0zXCmGKd7tPu6W') -> None | dict:
|
23
|
-
try:
|
24
|
-
if v_true(token_string, str):
|
25
|
-
cipher = AESCipher(key=key, algorithm='sha256')
|
26
|
-
source: dict = json.loads(cipher.decrypt(token_string))
|
27
|
-
source['datetime'] = datetime_string_to_datetime(source['datetime'])
|
28
|
-
return source
|
29
|
-
else:
|
30
|
-
return None
|
31
|
-
except:
|
32
|
-
return None
|
33
|
-
|
34
|
-
|
35
|
-
def certify_token(token_string: str, key: str = 'Fc0zXCmGKd7tPu6W') -> bool:
|
36
|
-
try:
|
37
|
-
result = parsing_token(token_string, key)
|
38
|
-
if not v_true(result, dict):
|
39
|
-
return False
|
40
|
-
if result.get('datetime') < datetime_now():
|
41
|
-
return False
|
42
|
-
return True
|
43
|
-
except:
|
44
|
-
return False
|
ezkit-1.7.5/ezKit/xftp.py
DELETED
@@ -1,194 +0,0 @@
|
|
1
|
-
'''
|
2
|
-
ftplib: https://docs.python.org/3.10/library/ftplib.html
|
3
|
-
'''
|
4
|
-
import os
|
5
|
-
from ftplib import FTP
|
6
|
-
from pathlib import Path
|
7
|
-
|
8
|
-
|
9
|
-
class XFTP:
|
10
|
-
|
11
|
-
def __init__(self, host='127.0.0.1', port=21, username='anonymous', password='', encoding='UTF-8', debuglevel=0):
|
12
|
-
''' Initiation '''
|
13
|
-
self.ftp = FTP()
|
14
|
-
self.ftp.set_debuglevel(debuglevel)
|
15
|
-
self.host = host
|
16
|
-
self.port = port
|
17
|
-
self.username = username
|
18
|
-
self.password = password
|
19
|
-
self.encoding = encoding
|
20
|
-
self.retry = 1
|
21
|
-
|
22
|
-
def connect(self):
|
23
|
-
''' FTP connect '''
|
24
|
-
try:
|
25
|
-
self.ftp.connect(host=self.host, port=self.port, timeout=10)
|
26
|
-
self.ftp.encoding = self.encoding
|
27
|
-
self.ftp.login(user=self.username, passwd=self.password)
|
28
|
-
print('FTP connect success')
|
29
|
-
print('-' * 80)
|
30
|
-
return True
|
31
|
-
except Exception as e:
|
32
|
-
print(f'FTP connect error: {e}, retry...')
|
33
|
-
if self.retry >= 3:
|
34
|
-
print('FTP connect faild')
|
35
|
-
return False
|
36
|
-
self.retry += 1
|
37
|
-
self.connect()
|
38
|
-
|
39
|
-
def close(self, info=None):
|
40
|
-
''' FTP close '''
|
41
|
-
print(info) if info else None
|
42
|
-
try:
|
43
|
-
self.ftp.quit()
|
44
|
-
except:
|
45
|
-
self.ftp.close()
|
46
|
-
print('-' * 80)
|
47
|
-
print('FTP connect closed')
|
48
|
-
|
49
|
-
def get_file_list(self, dir='/'):
|
50
|
-
''' Get file list '''
|
51
|
-
self.chdir_to_remote(dir)
|
52
|
-
return self.ftp.nlst()
|
53
|
-
|
54
|
-
def get_file_size(self, dir='/', file=None):
|
55
|
-
''' Get file size '''
|
56
|
-
self.chdir_to_remote(dir)
|
57
|
-
return self.ftp.size(file)
|
58
|
-
|
59
|
-
def mkdir(self, dir_string='/'):
|
60
|
-
''' 创建目录 (从 / 目录依次递增创建子目录. 如果目录存在, 创建目录时会报错, 所以这里忽略所有错误.) '''
|
61
|
-
try:
|
62
|
-
dir_list = dir_string.split("/")
|
63
|
-
for i, _ in enumerate(dir_list):
|
64
|
-
dir = '/'.join(dir_list[:i + 1])
|
65
|
-
try:
|
66
|
-
self.ftp.mkd(dir)
|
67
|
-
except:
|
68
|
-
pass
|
69
|
-
return True
|
70
|
-
except:
|
71
|
-
return False
|
72
|
-
|
73
|
-
def chdir_to_remote(self, dir='/'):
|
74
|
-
''' change to remote directory'''
|
75
|
-
try:
|
76
|
-
self.ftp.cwd(dir)
|
77
|
-
except:
|
78
|
-
self.close(f'Remote directory error: {dir}')
|
79
|
-
|
80
|
-
def x_exit(self, info=None):
|
81
|
-
''' Exit '''
|
82
|
-
print(info) if info else None
|
83
|
-
# 注意: exit() 并不会退出脚本, 配合 try 使用
|
84
|
-
exit()
|
85
|
-
|
86
|
-
def x_exec(self, local_dir='.', local_file='', remote_dir='/', remote_file='', upload=False):
|
87
|
-
''' Download or Upload '''
|
88
|
-
|
89
|
-
bufsize = 1024
|
90
|
-
local_path = local_dir + '/' + local_file
|
91
|
-
remote_path = remote_dir + '/' + remote_file
|
92
|
-
info = 'Upload' if upload else 'Download'
|
93
|
-
|
94
|
-
# 检查参数
|
95
|
-
if upload:
|
96
|
-
if local_file == '':
|
97
|
-
self.close('Argument Miss: local file')
|
98
|
-
# 如果没有设置 远程文件 名称, 则使用 本地文件 名称
|
99
|
-
if remote_file == '':
|
100
|
-
remote_file = local_file
|
101
|
-
remote_path = remote_dir + '/' + remote_file
|
102
|
-
else:
|
103
|
-
if remote_file == '':
|
104
|
-
self.close('Argument Miss: remote file')
|
105
|
-
# 如果没有设置 本地文件 名称, 则使用 远程文件 名称
|
106
|
-
if local_file == '':
|
107
|
-
local_file = remote_file
|
108
|
-
local_path = local_dir + '/' + local_file
|
109
|
-
|
110
|
-
# 进入本地目录
|
111
|
-
try:
|
112
|
-
if upload:
|
113
|
-
# 检查本地目录
|
114
|
-
stat = Path(local_dir)
|
115
|
-
self.close(f'Local directory error: {local_dir}') if stat.exists() == False else None
|
116
|
-
else:
|
117
|
-
# 创建本地目录
|
118
|
-
Path(local_dir).mkdir(parents=True, exist_ok=True)
|
119
|
-
# 进入本地目录
|
120
|
-
os.chdir(local_dir)
|
121
|
-
except:
|
122
|
-
# 第一层 try 使用 self.x_exit() 无效, 直接使用 self.close()
|
123
|
-
self.close(f'Local directory error: {local_dir}')
|
124
|
-
|
125
|
-
# 上传或下载
|
126
|
-
try:
|
127
|
-
|
128
|
-
if upload:
|
129
|
-
|
130
|
-
''' 上传 '''
|
131
|
-
|
132
|
-
# 创建远程目录
|
133
|
-
if remote_dir != '/':
|
134
|
-
self.mkdir(remote_dir)
|
135
|
-
|
136
|
-
# 进入远程目录
|
137
|
-
self.chdir_to_remote(remote_dir)
|
138
|
-
|
139
|
-
# 上传文件
|
140
|
-
stat = Path(local_file)
|
141
|
-
if stat.exists() and stat.is_file():
|
142
|
-
with open(local_file, 'rb') as fid:
|
143
|
-
self.ftp.storbinary(f'STOR {remote_file}', fid, bufsize)
|
144
|
-
print('{} success: {} -> {}'.format(info, local_path.replace('//', '/'), remote_path.replace('//', '/')))
|
145
|
-
return True
|
146
|
-
else:
|
147
|
-
self.x_exit('{} error: {} is not exist'.format(info, local_path.replace('//', '/')))
|
148
|
-
|
149
|
-
else:
|
150
|
-
|
151
|
-
''' 下载 '''
|
152
|
-
|
153
|
-
# 进入远程目录
|
154
|
-
self.chdir_to_remote(remote_dir)
|
155
|
-
|
156
|
-
# 下载文件
|
157
|
-
if remote_file in self.ftp.nlst():
|
158
|
-
with open(local_file, 'wb') as fid:
|
159
|
-
self.ftp.retrbinary(f'RETR {remote_file}', fid.write, bufsize)
|
160
|
-
print('{} success: {} -> {}'.format(info, remote_path.replace('//', '/'), local_path.replace('//', '/')))
|
161
|
-
return True
|
162
|
-
else:
|
163
|
-
self.x_exit('{} error: {} is not exist'.format(info, remote_path.replace('//', '/')))
|
164
|
-
|
165
|
-
except Exception as e:
|
166
|
-
# 第一层 try 使用 self.x_exit() 无效, 直接使用 self.close()
|
167
|
-
# self.close('{} faild! Please check {} or {}'.format(info, local_path, remote_path))
|
168
|
-
self.close(f'{info} error: {e}')
|
169
|
-
return False
|
170
|
-
|
171
|
-
def handle_all(self, local_dir='.', remote_dir='/', upload=False):
|
172
|
-
''' Handle All '''
|
173
|
-
if upload:
|
174
|
-
# 检查本地目录
|
175
|
-
stat = Path(local_dir)
|
176
|
-
self.close(f'Local directory error: {local_dir}') if stat.exists() == False else None
|
177
|
-
# 获取文件列表
|
178
|
-
local_files = [f for f in os.listdir(local_dir) if os.path.isfile(os.path.join(local_dir, f))]
|
179
|
-
for i in local_files:
|
180
|
-
self.x_exec(local_dir=local_dir, remote_dir=remote_dir, local_file=i, upload=True)
|
181
|
-
else:
|
182
|
-
remote_files = self.get_file_list(remote_dir)
|
183
|
-
for i in remote_files:
|
184
|
-
self.x_exec(local_dir=local_dir, remote_dir=remote_dir, remote_file=i)
|
185
|
-
|
186
|
-
def retrlines(self, remote_dir='/', cmd='LIST'):
|
187
|
-
''' Retrlines '''
|
188
|
-
try:
|
189
|
-
self.chdir_to_remote(remote_dir)
|
190
|
-
print(self.ftp.retrlines(cmd))
|
191
|
-
self.close()
|
192
|
-
except Exception as e:
|
193
|
-
# 第一层 try 使用 self.x_exit() 无效, 直接使用 self.close()
|
194
|
-
self.close(e)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|