server.dev.py 9.9 KB

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