在生产环境下使用生成器像服务器传输大文件

Posted Richie Wen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在生产环境下使用生成器像服务器传输大文件相关的知识,希望对你有一定的参考价值。

废话不多说,直接上代码

  1 # -*- coding: utf-8 -*-
  2 # Created by richie at 2018/10/23
  3 import os
  4 import sys
  5 
  6 _ver = sys.version_info
  7 
  8 #: Python 2.x?
  9 is_py2 = (_ver[0] == 2)
 10 
 11 #: Python 3.x?
 12 is_py3 = (_ver[0] == 3)
 13 
 14 import httplib
 15 import string
 16 import random
 17 from array import array
 18 from urllib3.exceptions import LocationParseError
 19 from urllib3.util import parse_url
 20 
 21 if is_py2:
 22     from collections import Mapping
 23 
 24     range = xrange
 25 elif is_py3:
 26     from collections.abc import Mapping
 27 
 28     range = range
 29 
 30 _BOUNDARY_CHARS = string.digits + string.ascii_letters
 31 
 32 
 33 def to_key_val_list(value):
 34     if value is None:
 35         return None
 36 
 37     if isinstance(value, (str, bytes, bool, int)):
 38         raise ValueError(cannot encode objects that are not 2-tuples)
 39 
 40     if isinstance(value, Mapping):
 41         value = value.items()
 42 
 43     return list(value)
 44 
 45 
 46 class HTTPConnection(httplib.HTTPConnection):
 47     """
 48     改写send方法  使之能够接受生成器
 49     """
 50     def __enter__(self):
 51         return self
 52 
 53     def __exit__(self, *args):
 54         self.close()
 55 
 56     def send(self, data):
 57         """Send `data‘ to the server."""
 58         if self.sock is None:
 59             if self.auto_open:
 60                 self.connect()
 61             else:
 62                 raise httplib.NotConnected()
 63 
 64         if self.debuglevel > 0:
 65             print "send:", repr(data)
 66         blocksize = 8192
 67         if hasattr(data, next):
 68             for value in data:
 69                 self.sock.sendall(value)
 70 
 71         elif hasattr(data, read) and not isinstance(data, array):
 72             if self.debuglevel > 0: print "sendIng a read()able"
 73             datablock = data.read(blocksize)
 74             while datablock:
 75                 self.sock.sendall(datablock)
 76                 datablock = data.read(blocksize)
 77         else:
 78             self.sock.sendall(data)
 79 
 80 
 81 class Request(object):
 82 
 83     def __init__(self, method=None, url=None, data=None, files=None, ):
 84         # Default empty dicts for dict params.
 85         data = [] if data is None else data
 86         files = [] if files is None else files
 87 
 88         self.method = method
 89         self.url = url
 90         self.files = files
 91         self.data = data
 92         self.headers = {
 93             Accept: */*,
 94             Connection: keep-alive,
 95         }
 96         self.__boundary = None
 97         self.__item_header = []
 98         self.end_tag = None
 99 
100         self.init_body()
101 
102     def __repr__(self):
103         return <Request [%s]> % (self.method)
104 
105     def get_boundary(self):
106         if self.__boundary is None:
107             self.__boundary = ‘‘.join(random.choice(_BOUNDARY_CHARS) for _ in range(32))
108         return self.__boundary
109 
110     def init_body(self):
111         """
112         组装数据
113         :return:
114         """
115         def escape_quote(s):
116             return s.replace(", \")
117 
118         fields = to_key_val_list(self.data or {})
119         files = to_key_val_list(self.files or {})
120         lines = []
121         fpaths = []
122         fheads = []
123         if fields:
124             for name, value in fields:
125                 lines.extend((
126                     --{0}.format(self.get_boundary()),
127                     Content-Disposition: form-data; name="{0}".format(escape_quote(name)),
128                     ‘‘,
129                     str(value),
130                 ))
131         if files:
132             __temp = []
133             for name, value in files:
134                 filename = os.path.basename(value)
135                 filename = filename.encode(utf-8)
136                 __temp.extend((
137                     --{0}.format(self.get_boundary()),
138                     Content-Disposition: form-data; name="{0}"; filename="{1}".format(
139                         escape_quote(name), escape_quote(filename)),
140                     
,
141                 ))
142                 fpaths.append(value)
143                 fheads.append(
.join(__temp))
144         fields_heads = 
.join(lines) + 

145         self.end_tag = "--" + self.get_boundary() + "--
"
146 
147         content_type = str(multipart/form-data; boundary=%s % self.get_boundary())
148         if content_type and (content-type not in self.headers):
149             self.headers[Content-Type] = content_type
150 
151         self.prepare_content_length(fields_heads, fheads, fpaths)
152         self.data = self.prepare_data(fields_heads, fheads, fpaths)
153 
154     def prepare_data(self, fields_heads, fheads, fpaths):
155         """
156         通过生成器发送数据
157         :param fields_heads:
158         :param fheads:
159         :param fpaths:
160         :return:
161         """
162         b_size = 4096
163         yield fields_heads
164         for i in range(len(fheads)):
165             yield fheads[i]
166             file_obj = open(fpaths[i], rb)
167             while True:
168                 block = file_obj.read(b_size)
169                 if block:
170                     yield block
171                 else:
172                     yield 

173                     break
174             yield self.end_tag
175 
176     def prepare_content_length(self, fields_heads, fheads=None, fpaths=None):
177         """
178         准备发送内容的大小
179         :param fields_heads:
180         :param fheads:
181         :param fpaths:
182         :return:
183         """
184         if fields_heads is not None:
185             length = len(fields_heads)
186             if fpaths:
187                 for fpath in fpaths:
188                     length += os.path.getsize(fpath) + len(
)  # file length
189                 for fhead in fheads:
190                     length += len(fhead)
191                 length += len(self.end_tag)
192             if length:
193                 self.headers[Content-Length] = str(length)
194 
195         elif self.method not in (GET, HEAD) and self.headers.get(Content-Length) is None:
196             self.headers[Content-Length] = 0
197 
198     def send(self):
199         """
200         通过httplib发送请求
201         :return:
202         """
203         # Support for unicode domain names and paths.
204         try:
205             scheme, auth, host, port, path, query, fragment = parse_url(self.url)
206         except LocationParseError as e:
207             raise Exception(*e.args)
208 
209         with HTTPConnection(host, port=port) as conn:
210             conn.request(self.method.upper(), self.url, self.data, self.headers)
211             # Receive the response from the server
212             try:
213                 # For Python 2.7, use buffering of HTTP responses
214                 response = conn.getresponse(buffering=True)
215             except TypeError:
216                 # For compatibility with Python 3.3+
217                 response = conn.getresponse()
218             status = response.status
219             content = response.read()
220         return status, content

 

以上是关于在生产环境下使用生成器像服务器传输大文件的主要内容,如果未能解决你的问题,请参考以下文章

使用vue-cli构建 webpack打包工具时,生产环境下,每次build时,删除dist目录,并重新生成,以防dist目录文件越来越多。

React:在没有构建的情况下运行生产

VueJs生产环境部署

如何传输大容量文件

Linux通过NFS实现文件共享

如何使用 Idea 远程调试生产环境 Java 代码