場景:
CMS系統部署,當時採用Nginx和Tomcat架構,即靜態檔案放在Nginx部署的伺服器上,後臺動態程式碼(cms後臺管理系統class)部署在另一臺Tomcat伺服器上。Tomcat部署的系統,在文章釋出後生成的靜態頁面檔案(HTML檔案等),需要拷貝到Nginx伺服器上(最佳方案是兩臺伺服器共享儲存)。要求:
- 在Tomcat服務生成的靜態檔案實時(時間間隔5分鐘以內)同步到Tomcat服務中。
- 自動監控、7*24執行
- 部署時方便快捷
解決思路:
- 使用python3 開發,方便快捷,主要的伺服器都是Linux版本
- Tomcat服務中需要同步的根資料夾,只要有檔案改動(新增、修改、刪除)都發起同步
遇到問題:
- 客戶端怎樣實時監控檔案變化
- 在建立檔案時,監控發現觸發兩次檔案修改方法
- 檔案內容較大時,還沒有複製完,就開始觸發socket傳遞,使得檔案傳送失敗(因為檔案還沒有修改完)
- 伺服器端和客戶端都能實現迴圈等待
解決方案:
- 客戶端監控檔案變化是根據系統不同,使用的外掛不同。在Windows系統中使用win32file
- 設定可以傳遞檔案的大小
- 設定等待時間,即等待檔案建立或者修改完後,才能socket傳遞
- 伺服器端:接收檔名稱、建立檔案、儲存檔案、繼續等待..
程式碼實現
客戶端程式碼
#!/user/bin/python
# -*- coding: utf-8 -*-
'''
python3 socket 檔案傳輸----客戶端(Windows版本):
v1.5:
1、監控指定資料夾變化
2、瓶頸在檔案大小:即寫檔案時間,如果讀取檔案時,檔案沒有寫入完成,就會報錯。
解決方法:新增等待時間time.sleep(3)#等待3秒
'''
import socket, os,time,logging
import zipfile
import sys
import win32file
import win32con
socket = socket.socket()
socket.connect(("127.0.0.1", 9999))
SIZE = 1024 * 1024 * 2000
print(socket.recv(SIZE))
print("sending please wait for a second....")
ACTIONS = {
1: "Created",
2: "Deleted",
3: "Updated",
4: "Renamed from something",
5: "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001
path_to_watch = 'E:\\temp'
print('Watching changes in', path_to_watch)
hDir = win32file.CreateFile(
path_to_watch,
FILE_LIST_DIRECTORY,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS,
None
)
COUNT = 0
while 1:
results = win32file.ReadDirectoryChangesW(
hDir,
1024,
True,
win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
win32con.FILE_NOTIFY_CHANGE_SIZE |
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
win32con.FILE_NOTIFY_CHANGE_SECURITY,
None,
None)
COUNT = COUNT + 1
print(results)
print(COUNT)
print("--------")
for action, filename in results:
full_filename = os.path.join(path_to_watch, filename)
print(full_filename, ACTIONS.get(action, "Unknown"))
if action == 3:
print("permission =======")
print(full_filename)
time.sleep(3) #睡眠時間:等待檔案複製完成
try:
f2 = open(full_filename, 'rb')
socket.sendall(bytes(f2.name, encoding="utf-8"))
data = f2.read(SIZE)
socket.sendall(data)
f2.close()
except Exception as e:
logging.error("檔案開啟異常...")
logging.exception(e)
finally:
pass
print("sended!")
socket.close()
print("connection closed")
複製程式碼
服務端程式碼
#!/user/bin/python
# -*- coding: utf-8 -*-
'''
python3 socket 檔案傳輸--服務端(Windows版本):
v1.5:
1、接收客戶端傳遞過來的檔案
'''
import socket, os,logging
from datetime import datetime
socket = socket.socket()
socket.bind(("127.0.0.1", 9999))
socket.listen(20)
SIZE = 1024*1024*2000
savepath = "D:\\workspace\\python\\demo\\sc1\\py_s\\ss"
def Service():
while True:
conn, addr = socket.accept()
print('Accept new connection from %s:%s...' % addr)
conn.sendall(bytes("Welcome from server!", encoding="utf-8"))
print(conn)
try:
while True:
fpath = str(conn.recv(1024), encoding="utf-8")
f_dir = os.path.split(fpath)[0]
fname = os.path.split(fpath)[1]
fnameSave = os.path.join(savepath,fname)
if not os.path.isdir(savepath):
os.makedirs(savepath)
ff = open(fnameSave, 'wb') # 按照配置的路徑進行儲存
starttime = datetime.now()
print("start...")
recvdata = conn.recv(SIZE)
if not recvdata:
print("reach the end of file")
break
else:
ff.write(recvdata)
ff.close()
endtime = datetime.now()
print("end...花費時間(s)",(endtime-starttime).seconds)
except Exception as e:
logging.error("伺服器異常...")
logging.exception(e)
finally:
conn.close()
print("receive finished")
print("connection from %s:%s closed." % addr)
if __name__ == '__main__':
Service()
複製程式碼
總結
- 檔案操作和作業系統有關,不同系統呼叫底層的庫是不一樣的
- 現在只實現了windows 版本,且只是在win10系統測試通過
- 單個檔案傳輸有大小限制
- 監控檔案變化時,與檔案修改寫入的時間有衝突,socket發起傳遞時,一定是檔案操作關閉了才可以,不然會報錯誤。
- 後續計劃:
- 實現Linux版本
- 實現打包傳遞
- 實現超大檔案傳遞