WebUtils.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Created on 2017-12-20
  4. @author: liuqun
  5. '''
  6. import json
  7. import os
  8. import itertools
  9. from alipay.aop.api.FileItem import FileItem
  10. from alipay.aop.api.constant.CommonConstants import *
  11. try:
  12. import httplib
  13. except ImportError:
  14. import http.client as httplib
  15. try:
  16. import urlparse
  17. except ImportError:
  18. import urllib.parse as urlparse
  19. try:
  20. from urllib.parse import quote_plus
  21. except ImportError:
  22. from urllib import quote_plus
  23. import mimetypes
  24. from alipay.aop.api.exception.Exception import *
  25. class MultiPartForm(object):
  26. """Accumulate the data to be used when posting a form."""
  27. def __init__(self, charset='utf-8'):
  28. self.charset = charset
  29. self.form_fields = []
  30. self.files = []
  31. self.boundary = "ALIPAY_SDK_PYTHON_BOUNDARY"
  32. return
  33. def get_content_type(self):
  34. return 'multipart/form-data; boundary=%s' % self.boundary
  35. def add_field(self, name, value):
  36. """Add a simple field to the form data."""
  37. if not isinstance(value, str):
  38. value = json.dumps(value, ensure_ascii=False)
  39. self.form_fields.append((name, value))
  40. return
  41. def add_file(self, field_name, file_name, file_content, mimetype=None):
  42. """Add a file to be uploaded."""
  43. if mimetype is None:
  44. mimetype = mimetypes.guess_type(file_name)[0] or 'application/octet-stream'
  45. self.files.append((field_name, file_name, mimetype, file_content))
  46. return
  47. def build_body(self):
  48. """Return a string representing the form data, including attached files."""
  49. # Build a list of lists, each containing "lines" of the
  50. # request. Each part is separated by a boundary string.
  51. # Once the list is built, return a string where each
  52. # line is separated by '\r\n'.
  53. parts = []
  54. part_boundary = '--' + self.boundary
  55. # Add the form fields
  56. parts.extend(
  57. [bytes(part_boundary.encode(self.charset)),
  58. bytes(('Content-Disposition: form-data; name="%s"' % name).encode(self.charset))
  59. if PYTHON_VERSION_3 else ('Content-Disposition: form-data; name="%s"' % name),
  60. bytes(('Content-Type: text/plain; charset=%s' % self.charset).encode(self.charset)),
  61. bytes(''.encode(self.charset)),
  62. bytes(value.encode(self.charset)) if PYTHON_VERSION_3 else value
  63. ]
  64. for name, value in self.form_fields
  65. )
  66. # Add the files to upload
  67. parts.extend(
  68. [bytes(part_boundary.encode(self.charset)),
  69. bytes(('Content-Disposition: form-data; name="%s"; filename="%s"' %
  70. (field_name, filename)).encode(self.charset)) if PYTHON_VERSION_3 else
  71. ('Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename)),
  72. bytes(('Content-Type: %s' % content_type).encode(self.charset)),
  73. bytes('Content-Transfer-Encoding: binary'.encode(self.charset)),
  74. bytes(''.encode(self.charset)),
  75. body,
  76. ]
  77. for field_name, filename, content_type, body in self.files
  78. )
  79. # Flatten the list and add closing boundary marker,
  80. # then return CR+LF separated data
  81. flattened = list(itertools.chain(*parts))
  82. flattened.append(bytes(('--' + self.boundary + '--').encode(self.charset)))
  83. flattened.append(bytes(''.encode(self.charset)))
  84. return bytes('\r\n'.encode(self.charset)).join(flattened)
  85. def url_encode(params, charset):
  86. query_string = ""
  87. for (k, v) in params.items():
  88. value = v
  89. if not isinstance(value, str):
  90. value = json.dumps(value, ensure_ascii=False)
  91. if PYTHON_VERSION_3:
  92. value = quote_plus(value, encoding=charset)
  93. else:
  94. value = quote_plus(value)
  95. query_string += ("&" + k + "=" + value)
  96. query_string = query_string[1:]
  97. return query_string
  98. def get_http_connection(url, query_string, timeout):
  99. url_parse_result = urlparse.urlparse(url)
  100. host = url_parse_result.hostname
  101. port = 80
  102. connection = httplib.HTTPConnection(host=host, port=port, timeout=timeout)
  103. if url.find("https") == 0:
  104. port = 443
  105. connection = httplib.HTTPSConnection(host=host, port=port, timeout=timeout)
  106. url = url_parse_result.scheme + "://" + url_parse_result.hostname
  107. if url_parse_result.port:
  108. url += url_parse_result.port
  109. url += url_parse_result.path
  110. url += ('?' + query_string)
  111. return url, connection
  112. def do_post(url, query_string=None, headers=None, params=None, charset='utf-8', timeout=15):
  113. url, connection = get_http_connection(url, query_string, timeout)
  114. try:
  115. connection.connect()
  116. except Exception as e:
  117. raise RequestException('[' + THREAD_LOCAL.uuid + ']post connect failed. ' + str(e))
  118. body = None
  119. if params:
  120. body = url_encode(params, charset)
  121. try:
  122. connection.request("POST", url, body=body, headers=headers)
  123. except Exception as e:
  124. raise RequestException('[' + THREAD_LOCAL.uuid + ']post request failed. ' + str(e))
  125. response = connection.getresponse()
  126. if response.status is not 200:
  127. raise ResponseException('[' + THREAD_LOCAL.uuid + ']invalid http status ' + str(response.status) + \
  128. ',detail body:' + response.read())
  129. result = response.read()
  130. try:
  131. response.close()
  132. connection.close()
  133. except Exception:
  134. pass
  135. return result
  136. def do_multipart_post(url, query_string=None, headers=None, params=None, multipart_params=None, charset='utf-8', timeout=30):
  137. url, connection = get_http_connection(url, query_string, timeout)
  138. try:
  139. connection.connect()
  140. except Exception as e:
  141. raise RequestException('[' + THREAD_LOCAL.uuid + ']post connect failed. ' + str(e))
  142. form = MultiPartForm(charset)
  143. for key, value in params.items():
  144. form.add_field(key, value)
  145. for key, value in multipart_params.items():
  146. file_item = value
  147. if file_item and isinstance(file_item, FileItem):
  148. form.add_file(field_name=key, file_name=file_item.get_file_name(),
  149. file_content=file_item.get_file_content(), mimetype=file_item.get_mime_type())
  150. body = form.build_body()
  151. if not headers:
  152. headers = {}
  153. headers['Content-type'] = form.get_content_type()
  154. try:
  155. connection.request("POST", url, body=body, headers=headers)
  156. except Exception as e:
  157. raise RequestException('[' + THREAD_LOCAL.uuid + ']post request failed. ' + str(e))
  158. response = connection.getresponse()
  159. if response.status is not 200:
  160. raise ResponseException('[' + THREAD_LOCAL.uuid + ']invalid http status ' + str(response.status) + \
  161. ',detail body:' + response.read())
  162. result = response.read()
  163. try:
  164. response.close()
  165. connection.close()
  166. except Exception:
  167. pass
  168. return result