server.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import cgi
  4. import os
  5. import datetime
  6. import uuid
  7. import json
  8. import zipfile
  9. # pip install requests
  10. import requests
  11. # pip install retry
  12. from retry import retry
  13. from http.server import BaseHTTPRequestHandler, HTTPServer
  14. '''
  15. 脚本运行说明:
  16. screen -S uploadServer
  17. nohup python3 ./server.py >/dev/null 2>&1 &
  18. 注意print有输出缓冲,使用-u参数,使得python不启用缓冲,这样就可以同步看到输出结果了。python -u ./server.py
  19. 关闭终端后再进入可以这样:
  20. screen -list
  21. 找到相关的screen id
  22. screen -r screenId
  23. '''
  24. def get_date_path():
  25. date_path = datetime.datetime.now().strftime("%Y/%m%d")
  26. return date_path
  27. def get_uuid_file():
  28. uuid_str = str(uuid.uuid1()).replace('-', '')
  29. return uuid_str
  30. def get_file_ext(path):
  31. return os.path.splitext(path)[1]
  32. DEBUG_MODE = True
  33. FILE_PATH = '/Volumes/HDD/workshop/old/ar.upload.ming/files'
  34. #BASE_URL = 'http://ar.upload.ming/files' # 必须与商城common/config/params.php中的remoteUploadUrl一致
  35. BASE_URL = 'http://localhost:9017/files'
  36. # FILE_PATH = '/data/wwwroot/upload/files/'
  37. # BASE_URL = 'http://upload.ar.wqcms.com/files/'
  38. # 重试的次数
  39. RETRY_TIMES = 5
  40. # 重试的时间间隔,成倍增长
  41. RETRY_BACK_OFF = 2
  42. # 重试的间隔
  43. RETRY_DELAY = 2
  44. @retry(FileNotFoundError, tries=RETRY_TIMES, backoff=RETRY_BACK_OFF, delay=RETRY_DELAY)
  45. def notify_api(attachment_notify_url, data):
  46. res = requests.post(attachment_notify_url, data=data)
  47. if res.status_code == 200:
  48. notify_json = res.json()['message']
  49. return notify_json
  50. else:
  51. print(res)
  52. raise FileNotFoundError('文件上传失败')
  53. class UploadServer(BaseHTTPRequestHandler):
  54. cgi_form = None
  55. post_uid = None
  56. post_token = None
  57. post_date = None
  58. post_notify_url = None
  59. def output(self, data, code):
  60. self.send_response(code)
  61. self.send_header('Content-Type', 'application/json')
  62. self.end_headers()
  63. self.wfile.write(json.dumps(data).encode())
  64. def error(self, data, debug_info=None):
  65. if DEBUG_MODE and debug_info is not None:
  66. data += '[%s]' % (debug_info,)
  67. self.output(data, 400)
  68. def success(self, data, debug_info=None):
  69. if DEBUG_MODE and debug_info is not None:
  70. data += '[%s]' % (debug_info,)
  71. self.output(data, 200)
  72. @staticmethod
  73. def logger(msg):
  74. if not DEBUG_MODE:
  75. return False
  76. if isinstance(msg, tuple):
  77. msg = tuple(msg).__str__()
  78. elif isinstance(msg, list):
  79. msg = "".join(list(msg))
  80. elif isinstance(msg, dict):
  81. msg = json.dumps(msg)
  82. date_file = datetime.datetime.now().strftime("%Y%m%d") + '.log'
  83. log_file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'logs')
  84. if not os.path.exists(log_file_path):
  85. os.makedirs(log_file_path)
  86. file = os.path.join(log_file_path, date_file)
  87. if not msg:
  88. msg = ''
  89. with open(file, 'a+') as f:
  90. date_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  91. msg = '[%s]%s' % (date_time, msg)
  92. f.write(msg + '\n')
  93. def request_logger(self, action, params):
  94. log_msg = '[%s]UID:%s,TOKEN:%s,DATE:%s,NOTIFY_URL:%s,PARAMS:%s' % (
  95. action, self.post_uid, self.post_token, self.post_date, self.post_notify_url, params,)
  96. self.logger(log_msg)
  97. def request_end_logger(self, action, data):
  98. log_msg = '[%s complete]UID:%s,TOKEN:%s,DATE:%s,NOTIFY_URL:%s,RESPONSE:%s' % (
  99. action, self.post_uid, self.post_token, self.post_date, self.post_notify_url, data,)
  100. self.logger(log_msg)
  101. def on_remove_file(self):
  102. local_file_path = self.cgi_form.getvalue('post_data')
  103. local_file_path = self.trim_base_url(local_file_path)
  104. # 记录请求日志
  105. self.request_logger('remove_file', local_file_path)
  106. target_path = os.path.join(FILE_PATH, local_file_path)
  107. file_exists = os.path.exists(target_path)
  108. success_data = {
  109. 'code': 200,
  110. 'error': 0,
  111. 'message': 'success',
  112. 'success': 1,
  113. }
  114. if not file_exists:
  115. success_data['message'] = target_path + ' file not exists'
  116. self.success(success_data)
  117. self.request_end_logger('remove_file', success_data)
  118. return
  119. try:
  120. # 删除文件
  121. os.remove(target_path)
  122. self.success(success_data)
  123. self.request_end_logger('remove_file', success_data)
  124. except BaseException as e:
  125. self.logger(e.args)
  126. self.error('文件删除失败', e.args[1])
  127. return
  128. @staticmethod
  129. def trim_base_url(str):
  130. return str.replace(BASE_URL + '/', '')
  131. def on_upload_file(self):
  132. # 临时文件路径
  133. attachment_file_path = self.cgi_form.getvalue('ATTACHMENT_path')
  134. # 原文件名
  135. attachment_file_name = self.cgi_form.getvalue('ATTACHMENT_name')
  136. # 文件大小
  137. attachment_file_size = self.cgi_form.getvalue('ATTACHMENT_size')
  138. # 文件md5
  139. attachment_file_md5 = self.cgi_form.getvalue('ATTACHMENT_md5')
  140. # 记录请求日志
  141. self.request_logger('upload', attachment_file_name)
  142. # 获取文件扩展名
  143. file_ext = get_file_ext(attachment_file_name)
  144. if not file_ext:
  145. file_ext = '.jpg'
  146. tmp_file_exists = os.path.exists(attachment_file_path)
  147. if not tmp_file_exists:
  148. self.error(404)
  149. return
  150. # 获取时间路径
  151. date_path = get_date_path()
  152. post_file_name = self.cgi_form.getvalue('file_name')
  153. if post_file_name:
  154. rnd_file = post_file_name
  155. else:
  156. # 获取随机文件名
  157. rnd_file = get_uuid_file() + str(file_ext)
  158. target_path = os.path.join(FILE_PATH, date_path)
  159. # 先判断文件夹是否存在,不存在就创建
  160. is_exists = os.path.exists(target_path)
  161. if not is_exists:
  162. os.makedirs(target_path)
  163. target_file = os.path.join(target_path, rnd_file)
  164. try:
  165. # 移动文件
  166. os.rename(attachment_file_path, target_file)
  167. # 生成URL
  168. file_url = target_file.replace(FILE_PATH, BASE_URL)
  169. data = {
  170. 'url': file_url,
  171. 'size': attachment_file_size,
  172. 'name': attachment_file_name,
  173. 'md5': attachment_file_md5,
  174. 'uid': self.post_uid,
  175. 'token': self.post_token,
  176. 'request_date': self.post_date,
  177. 'success': 1,
  178. }
  179. if self.post_notify_url:
  180. notify_result = notify_api(self.post_notify_url, data)
  181. data['IS_IMAGE'] = notify_result['IS_IMAGE']
  182. data['REMOTE_URL'] = notify_result['REMOTE_URL']
  183. data['url'] = notify_result['REMOTE_URL']
  184. self.success(data)
  185. self.request_end_logger('upload', data)
  186. except BaseException as e:
  187. self.logger(e.args)
  188. self.error('文件上传失败', e.args[0])
  189. return
  190. def on_zip_file(self):
  191. get_zip_files = self.cgi_form.getvalue('post_data')
  192. # 记录请求日志
  193. self.request_logger('zip_file', get_zip_files)
  194. zip_array = get_zip_files.strip(',').split(',')
  195. rnd_file = get_uuid_file() + '.zip'
  196. # 获取时间路径
  197. date_path = get_date_path()
  198. target_path = os.path.join(FILE_PATH, date_path)
  199. if not os.path.exists(target_path):
  200. os.makedirs(target_path)
  201. target_file = os.path.join(target_path, rnd_file)
  202. try:
  203. zip_file = zipfile.ZipFile(target_file, 'w', zipfile.ZIP_DEFLATED)
  204. for zf in zip_array:
  205. if not zf:
  206. continue
  207. zf = self.trim_base_url(zf)
  208. real_file = os.path.join(FILE_PATH, zf)
  209. is_exists = os.path.exists(real_file)
  210. if not is_exists:
  211. continue
  212. basename = os.path.basename(zf)
  213. zip_file.write(real_file, basename)
  214. # 生成URL
  215. file_url = target_file.replace(FILE_PATH, BASE_URL)
  216. data = {
  217. 'REMOTE_URL': file_url,
  218. 'success': 1,
  219. 'uid': self.post_uid,
  220. 'token': self.post_token,
  221. 'request_date': self.post_date,
  222. }
  223. if self.post_notify_url:
  224. notify_api(self.post_notify_url, data)
  225. self.success(data)
  226. self.request_end_logger('zip_file', data)
  227. except BaseException as e:
  228. self.logger(e.args)
  229. self.error('文件压缩失败', e.args[1])
  230. return
  231. def do_POST(self):
  232. self.cgi_form = cgi.FieldStorage(
  233. fp=self.rfile,
  234. headers=self.headers,
  235. environ={'REQUEST_METHOD': 'POST',
  236. 'CONTENT_TYPE': self.headers['Content-Type'],
  237. })
  238. action = self.cgi_form.getvalue('action')
  239. if not action:
  240. action = 'upload'
  241. # UID
  242. self.post_uid = self.get_params('UPLOAD-SERVER-USER')
  243. # TOKEN
  244. self.post_token = self.get_params('UPLOAD-SERVER-TOKEN')
  245. # REQUEST DATE
  246. self.post_date = self.get_params('UPLOAD-SERVER-DATE')
  247. # REQUEST NOTIFY URL
  248. self.post_notify_url = self.get_params('UPLOAD-SERVER-NOTIFY-URL')
  249. if 'upload' == action:
  250. self.on_upload_file()
  251. elif 'delete' == action:
  252. self.on_remove_file()
  253. elif 'download_zip' == action:
  254. self.on_zip_file()
  255. def get_params(self, key):
  256. if key in self.headers:
  257. return self.headers[key]
  258. return self.cgi_form.getvalue(key)
  259. httpd = HTTPServer(('127.0.0.1', 8181), UploadServer)
  260. print("Server started on 127.0.0.1,port 8181.....")
  261. httpd.serve_forever()