bos_client.py 112 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268
  1. # Copyright 2014 Baidu, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  4. # except in compliance with the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the
  9. # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  10. # either express or implied. See the License for the specific language governing permissions
  11. # and limitations under the License.
  12. """
  13. This module provides a client class for BOS.
  14. """
  15. import io
  16. import copy
  17. import http.client
  18. import os
  19. import json
  20. import logging
  21. import shutil
  22. import struct
  23. from builtins import str
  24. from builtins import bytes
  25. from future.utils import iteritems, iterkeys, itervalues
  26. from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED, FIRST_COMPLETED
  27. import threading
  28. import functools
  29. import multiprocessing
  30. import baidubce
  31. from baidubce import bce_client_configuration
  32. from baidubce import utils
  33. from baidubce.auth import bce_v1_signer
  34. from baidubce.bce_base_client import BceBaseClient
  35. from baidubce.exception import BceClientError
  36. from baidubce.exception import BceServerError
  37. from baidubce.exception import BceHttpClientError
  38. from baidubce.http import bce_http_client
  39. from baidubce.http import handler
  40. from baidubce.http import http_content_types
  41. from baidubce.http import http_headers
  42. from baidubce.http import http_methods
  43. from baidubce.services import bos
  44. from baidubce.services.bos import bos_handler
  45. from baidubce.services.bos import storage_class
  46. from baidubce.utils import required
  47. from baidubce import compat
  48. _logger = logging.getLogger(__name__)
  49. FETCH_MODE_SYNC = b"sync"
  50. FETCH_MODE_ASYNC = b"async"
  51. ENCRYPTION_ALGORITHM= "AES256"
  52. HTTP_PROTOCOL_HEAD = b'http'
  53. class UploadTaskHandle:
  54. """
  55. handle to control multi upload file with multi-thread
  56. """
  57. def __init__(self):
  58. self.cancel_flag = False
  59. self.cancel_lock = threading.Lock()
  60. def cancel(self):
  61. """
  62. cancel putting super object from file with multi-thread
  63. """
  64. self.cancel_lock.acquire()
  65. self.cancel_flag= True
  66. self.cancel_lock.release()
  67. def is_cancel(self):
  68. """
  69. get cancel flag
  70. """
  71. self.cancel_lock.acquire()
  72. result = self.cancel_flag
  73. self.cancel_lock.release()
  74. return result
  75. class BosClient(BceBaseClient):
  76. """
  77. sdk client
  78. """
  79. def __init__(self, config=None):
  80. BceBaseClient.__init__(self, config)
  81. def list_buckets(self, config=None):
  82. """
  83. List buckets of user
  84. :param config: None
  85. :type config: BceClientConfiguration
  86. :returns: all buckets owned by the user.
  87. :rtype: baidubce.bce_response.BceResponse
  88. """
  89. return self._send_request(http_methods.GET, config=config)
  90. @required(bucket_name=(bytes, str))
  91. def get_bucket_location(self, bucket_name, config=None):
  92. """
  93. Get the region which the bucket located in.
  94. :param bucket_name: the name of bucket
  95. :type bucket_name: string or unicode
  96. :param config: None
  97. :type config: BceClientConfiguration
  98. :return: region of the bucket
  99. :rtype: str
  100. """
  101. params = {b'location': b''}
  102. response = self._send_request(http_methods.GET, bucket_name, params=params, config=config)
  103. return response.location_constraint
  104. @required(bucket_name=(bytes, str))
  105. def create_bucket(self, bucket_name, config=None):
  106. """
  107. Create bucket with specific name
  108. :param bucket_name: the name of bucket
  109. :type bucket_name: string or unicode
  110. :param config: None
  111. :type config: BceClientConfiguration
  112. :returns:
  113. :rtype: baidubce.bce_response.BceResponse
  114. """
  115. return self._send_request(http_methods.PUT, bucket_name, config=config)
  116. @required(bucket_name=(bytes, str))
  117. def does_bucket_exist(self, bucket_name, config=None):
  118. """
  119. Check whether there is a bucket with specific name
  120. :param bucket_name: None
  121. :type bucket_name: str
  122. :return:True or False
  123. :rtype: bool
  124. """
  125. try:
  126. self._send_request(http_methods.HEAD, bucket_name, config=config)
  127. return True
  128. except BceHttpClientError as e:
  129. if isinstance(e.last_error, BceServerError):
  130. if e.last_error.status_code == http.client.FORBIDDEN:
  131. return True
  132. if e.last_error.status_code == http.client.NOT_FOUND:
  133. return False
  134. raise e
  135. @required(bucket_name=(bytes, str))
  136. def get_bucket_acl(self, bucket_name, config=None):
  137. """
  138. Get Access Control Level of bucket
  139. :type bucket: string
  140. :param bucket: None
  141. :return:
  142. **json text of acl**
  143. """
  144. return self._send_request(
  145. http_methods.GET,
  146. bucket_name,
  147. params={b'acl': b''},
  148. config=config)
  149. @staticmethod
  150. def _dump_acl_object(acl):
  151. result = {}
  152. for k, v in iteritems(acl.__dict__):
  153. if not k.startswith('_'):
  154. result[k] = v
  155. return result
  156. @required(bucket_name=(bytes, str), acl=(list, dict))
  157. def set_bucket_acl(self, bucket_name, acl, config=None):
  158. """
  159. Set Access Control Level of bucket
  160. :type bucket: string
  161. :param bucket: None
  162. :type grant_list: list of grant
  163. :param grant_list: None
  164. :return:
  165. **HttpResponse Class**
  166. """
  167. self._send_request(http_methods.PUT,
  168. bucket_name,
  169. body=json.dumps({'accessControlList': acl},
  170. default=BosClient._dump_acl_object),
  171. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  172. params={b'acl': b''},
  173. config=config)
  174. @required(bucket_name=(bytes, str), canned_acl=bytes)
  175. def set_bucket_canned_acl(self, bucket_name, canned_acl, config=None):
  176. """
  177. :param bucket_name:
  178. :param canned_acl:
  179. :param config:
  180. :return:
  181. """
  182. self._send_request(http_methods.PUT,
  183. bucket_name,
  184. headers={http_headers.BCE_ACL: canned_acl},
  185. params={b'acl': b''},
  186. config=config)
  187. @required(bucket_name=(bytes, str))
  188. def set_bucket_storage_class(self, bucket_name, storage_class, config=None):
  189. """
  190. :param bucket_name:
  191. :param config:
  192. :return:
  193. """
  194. storage_class = compat.convert_to_string(storage_class)
  195. return self._send_request(http_methods.PUT,
  196. bucket_name,
  197. body=json.dumps({'storageClass': storage_class},
  198. default=BosClient._dump_acl_object),
  199. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  200. params={b'storageClass': b''},
  201. config=config)
  202. @required(bucket_name=(bytes, str))
  203. def get_bucket_storage_class(self, bucket_name, config=None):
  204. """
  205. :param bucket_name:
  206. :param config:
  207. :return:
  208. """
  209. return self._send_request(http_methods.GET,
  210. bucket_name,
  211. params={b'storageClass': b''},
  212. config=config)
  213. @required(bucket_name=(bytes, str))
  214. def delete_bucket(self, bucket_name, config=None):
  215. """
  216. Delete a Bucket(Must Delete all the Object in Bucket before)
  217. :type bucket: string
  218. :param bucket: None
  219. :return:
  220. **HttpResponse Class**
  221. """
  222. return self._send_request(http_methods.DELETE, bucket_name, config=config)
  223. # bucket static website
  224. @required(bucket_name=(bytes, str))
  225. def put_bucket_static_website(self, bucket_name, index=None, not_found=None, config=None):
  226. """
  227. Set index page and not_found 404 page for static website trusteeship
  228. :type bucket_name: string
  229. :param bucket_name: None
  230. :type index:string
  231. :param index:object name of index page for static website trusteeship
  232. :type not_found:string
  233. :param not_found:object name of not_found 404 page for static website trusteeship
  234. :return:
  235. **HttpResponse Class**
  236. """
  237. body = {}
  238. if index is not None:
  239. body['index'] = index
  240. if not_found is not None:
  241. body['notFound'] = not_found
  242. return self._send_request(http_methods.PUT,
  243. bucket_name,
  244. body=json.dumps(body,
  245. default=BosClient._dump_acl_object),
  246. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  247. params={b'website': b''},
  248. config=config)
  249. @required(bucket_name=(bytes, str))
  250. def get_bucket_static_website(self, bucket_name, config=None):
  251. """
  252. Get Information of static website trusteeship
  253. :type bucket: string
  254. :param bucket: None
  255. :return:
  256. **HttpResponse Class**
  257. """
  258. return self._send_request(http_methods.GET,
  259. bucket_name,
  260. params={b'website': b''},
  261. config=config)
  262. @required(bucket_name=(bytes, str))
  263. def delete_bucket_static_website(self, bucket_name, config=None):
  264. """
  265. Delete Information of static website trusteeship to be closed
  266. :type bucket: string
  267. :param bucket: None
  268. :return:
  269. **HttpResponse Class**
  270. """
  271. return self._send_request(http_methods.DELETE,
  272. bucket_name,
  273. params={b'website': b''},
  274. config=config)
  275. # bucket encryption
  276. @required(bucket_name=(bytes, str))
  277. def put_bucket_encryption(self, bucket_name, encryption_algorithm=ENCRYPTION_ALGORITHM, config=None):
  278. """
  279. Set server encryption for bucket
  280. :type bucket: string
  281. :param bucket: None
  282. :type encryption_algorithm: string
  283. :param grant_list: server encryption algorithm for bucekt.Now the value of encryption_algorithm
  284. only is 'AES256'
  285. :return:
  286. **HttpResponse Class**
  287. """
  288. encryption_algorithm = compat.convert_to_string(encryption_algorithm)
  289. return self._send_request(http_methods.PUT,
  290. bucket_name,
  291. body=json.dumps({"encryptionAlgorithm":encryption_algorithm},
  292. default=BosClient._dump_acl_object),
  293. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  294. params={b'encryption': b''},
  295. config=config)
  296. @required(bucket_name=(bytes, str))
  297. def get_bucket_encryption(self, bucket_name, config=None):
  298. """
  299. Get status of server encryption
  300. :type bucket: string
  301. :param bucket: None
  302. :return:
  303. **HttpResponse Class**
  304. """
  305. return self._send_request(http_methods.GET,
  306. bucket_name,
  307. params={b'encryption': b''},
  308. config=config)
  309. @required(bucket_name=(bytes, str))
  310. def delete_bucket_encryption(self, bucket_name, config=None):
  311. """
  312. Close server encryption
  313. :type bucket: string
  314. :param bucket: None
  315. :return:
  316. **HttpResponse Class**
  317. """
  318. return self._send_request(http_methods.DELETE,
  319. bucket_name,
  320. params={b'encryption': b''},
  321. config=config)
  322. # Bucket Copyright Protection
  323. @required(bucket_name=(bytes, str), resource=(list))
  324. def put_bucket_copyright_protection(self, bucket_name, resource, config=None):
  325. """
  326. Open image copyright protection and set resource
  327. :type bucket: string
  328. :param bucket: None
  329. :type resource: list of string
  330. :param grant_list: resource range to be protected
  331. :return:
  332. **HttpResponse Class**
  333. """
  334. return self._send_request(http_methods.PUT,
  335. bucket_name,
  336. body=json.dumps({"resource": resource},
  337. default=BosClient._dump_acl_object),
  338. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  339. params={b'copyrightProtection': b''},
  340. config=config)
  341. @required(bucket_name=(bytes, str))
  342. def get_bucket_copyright_protection(self, bucket_name, config=None):
  343. """
  344. Get configuration of image copyright protection
  345. :type bucket: string
  346. :param grant_list: None
  347. :return:
  348. **HttpResponse Class**
  349. """
  350. return self._send_request(http_methods.GET,
  351. bucket_name,
  352. params={b'copyrightProtection': b''},
  353. config=config)
  354. @required(bucket_name=(bytes, str))
  355. def delete_bucket_copyright_protection(self, bucket_name, config=None):
  356. """
  357. Close image copyright protection
  358. :type bucket: string
  359. :param bucket: None
  360. :return:
  361. **HttpResponse Class**
  362. """
  363. return self._send_request(http_methods.DELETE,
  364. bucket_name,
  365. params={b'copyrightProtection': b''},
  366. config=config)
  367. # bucket replication
  368. @required(bucket_name=(bytes, str), replication=(dict))
  369. def put_bucket_replication(self, bucket_name, replication, config=None):
  370. """
  371. Open cross-region replication
  372. :type bucket: string
  373. :param bucket: None
  374. :type replication: dict
  375. :type replication: configuration for cross-region replication
  376. :return:
  377. **HttpResponse Class**
  378. """
  379. params={b'replication': b''}
  380. if "id" in replication:
  381. params[b"id"] = compat.convert_to_bytes(replication["id"])
  382. return self._send_request(http_methods.PUT,
  383. bucket_name,
  384. body=json.dumps(replication,
  385. default=BosClient._dump_acl_object),
  386. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  387. params=params,
  388. config=config)
  389. @required(bucket_name=(bytes, str))
  390. def get_bucket_replication(self, bucket_name, id=None, config=None):
  391. """
  392. Get configuration of cross-region replication
  393. :type bucket: string
  394. :param bucket: None
  395. :type id: string
  396. :param id: replication rule id
  397. :return:
  398. **HttpResponse Class**
  399. """
  400. params={b'replication': b''}
  401. if id is not None:
  402. params[b"id"] = compat.convert_to_bytes(id)
  403. return self._send_request(http_methods.GET,
  404. bucket_name,
  405. params=params,
  406. config=config)
  407. @required(bucket_name=(bytes, str))
  408. def delete_bucket_replication(self, bucket_name, id=None, config=None):
  409. """
  410. Delete configuration of cross-region replication and close it
  411. :type bucket: string
  412. :param bucket: None
  413. :type id: string
  414. :param id: replication rule id
  415. :return:
  416. **HttpResponse Class**
  417. """
  418. params={b'replication': b''}
  419. if id is not None:
  420. params[b"id"] = compat.convert_to_bytes(id)
  421. return self._send_request(http_methods.DELETE,
  422. bucket_name,
  423. params=params,
  424. config=config)
  425. @required(bucket_name=(bytes, str))
  426. def get_bucket_replication_progress(self, bucket_name, id=None, config=None):
  427. """
  428. Get status of cross-region replication,for exapmle 'historyReplicationPercent',
  429. 'latestReplicationTime'
  430. :type bucket: string
  431. :param bucket: None
  432. :type id: string
  433. :param id: replication rule id
  434. :return:
  435. **HttpResponse Class**
  436. """
  437. params={b'replicationProgress': b''}
  438. if id is not None:
  439. params[b"id"] = compat.convert_to_bytes(id)
  440. return self._send_request(http_methods.GET,
  441. bucket_name,
  442. params=params,
  443. config=config)
  444. @required(bucket_name=(bytes, str))
  445. def list_bucket_replication(self, bucket_name, config=None):
  446. """
  447. list configuration of cross-region replication rule
  448. :type bucket: string
  449. :param bucket: None
  450. :return:
  451. **HttpResponse Class**
  452. """
  453. return self._send_request(http_methods.GET,
  454. bucket_name,
  455. params={b'replication': b'', b'list': b''},
  456. config=config)
  457. @required(bucket_name=(bytes, str), inventory=(dict))
  458. def put_bucket_inventory(self, bucket_name, inventory, config=None):
  459. """
  460. set bucket inventoru
  461. :type bucket: string
  462. :param bucket: None
  463. :type inventory: dict
  464. :param inventory: configuration for bucket inventory
  465. :return:
  466. **HttpResponse Class**
  467. """
  468. conf_id = compat.convert_to_bytes(inventory["id"])
  469. return self._send_request(http_methods.PUT,
  470. bucket_name,
  471. body=json.dumps(inventory,
  472. default=BosClient._dump_acl_object),
  473. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  474. params={b'inventory': b'', b'id': conf_id},
  475. config=config)
  476. @required(bucket_name=(bytes, str), inventory_conf_id=(bytes, str))
  477. def get_bucket_inventory(self, bucket_name, inventory_conf_id, config=None):
  478. """
  479. Get configuration of bucket inventory
  480. :type bucket: string
  481. :param bucket: None
  482. :return:
  483. **HttpResponse Class**
  484. """
  485. return self._send_request(http_methods.GET,
  486. bucket_name,
  487. params={b'inventory': b'', b'id': compat.convert_to_bytes(inventory_conf_id)},
  488. config=config)
  489. @required(bucket_name=(bytes, str), inventory_conf_id=(bytes, str))
  490. def delete_bucket_inventory(self, bucket_name, inventory_conf_id, config=None):
  491. """
  492. Delete configuration of bucket inventory
  493. :type bucket: string
  494. :param bucket: None
  495. :return:
  496. **HttpResponse Class**
  497. """
  498. return self._send_request(http_methods.DELETE,
  499. bucket_name,
  500. params={b'inventory': b'', b'id': compat.convert_to_bytes(inventory_conf_id)},
  501. config=config)
  502. @required(bucket_name=(bytes, str))
  503. def list_bucket_inventory(self, bucket_name, config=None):
  504. """
  505. list configuration of bucket inventory
  506. :type bucket: string
  507. :param bucket: None
  508. :return:
  509. **HttpResponse Class**
  510. """
  511. return self._send_request(http_methods.GET,
  512. bucket_name,
  513. params={b'inventory': b''},
  514. config=config)
  515. @required(bucket_name=(bytes, str))
  516. def put_bucket_trash(self, bucket_name, trash_dir=None, config=None):
  517. """
  518. Open bucket trash function
  519. :type bucket: string
  520. :param bucket: None
  521. :type trash_dir: string
  522. :param trash_dir: directory of trash,optional
  523. :return:
  524. **HttpResponse Class**
  525. """
  526. if trash_dir is not None:
  527. trash_dir = compat.convert_to_string(trash_dir)
  528. return self._send_request(http_methods.PUT,
  529. bucket_name,
  530. body=json.dumps({"trashDir": trash_dir},
  531. default=BosClient._dump_acl_object),
  532. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  533. params={b'trash': b''},
  534. config=config)
  535. @required(bucket_name=(bytes, str))
  536. def get_bucket_trash(self, bucket_name, config=None):
  537. """
  538. Get status of bucket trash
  539. :type bucket: string
  540. :param grant_list: None
  541. :return:
  542. **HttpResponse Class**
  543. """
  544. return self._send_request(http_methods.GET,
  545. bucket_name,
  546. params={b'trash': b''},
  547. config=config)
  548. @required(bucket_name=(bytes, str))
  549. def delete_bucket_trash(self, bucket_name, config=None):
  550. """
  551. Close bucket trash
  552. :type bucket: string
  553. :param bucket: None
  554. :return:
  555. **HttpResponse Class**
  556. """
  557. return self._send_request(http_methods.DELETE,
  558. bucket_name,
  559. params={b'trash': b''},
  560. config=config)
  561. @required(bucket_name=(bytes, str), key=(bytes, str))
  562. def generate_pre_signed_url(self,
  563. bucket_name,
  564. key,
  565. timestamp=0,
  566. expiration_in_seconds=1800,
  567. headers=None,
  568. params=None,
  569. headers_to_sign=None,
  570. protocol=None,
  571. config=None,
  572. httpmethod=http_methods.GET):
  573. """
  574. Get an authorization url with expire time.
  575. specified protocol in endpoint > protocal > default protocol in config.
  576. :type timestamp: int
  577. :param timestamp: None
  578. :type expiration_in_seconds: int
  579. :param expiration_in_seconds: None
  580. :type options: dict
  581. :param options: None
  582. :param is_official_domain: default use not official domain,example: bucket.bj.bcebos.com
  583. :return:
  584. **URL string**
  585. """
  586. key = compat.convert_to_bytes(key)
  587. if len(key) == 0 or key == b'v1':
  588. raise ValueError('generate url the key param error!')
  589. config = self._merge_config(config, bucket_name)
  590. headers = headers or {}
  591. params = params or {}
  592. # specified protocol in endpoint > protocal > default protocol in config
  593. if protocol is not None:
  594. config.protocol = protocol
  595. endpoint_protocol, endpoint_host, endpoint_port = \
  596. utils.parse_host_port(config.endpoint, config.protocol)
  597. full_host = endpoint_host
  598. if endpoint_port != endpoint_protocol.default_port:
  599. full_host += b':' + compat.convert_to_bytes(endpoint_port)
  600. headers[http_headers.HOST] = full_host
  601. path = self._get_path(config, bucket_name, key)
  602. if httpmethod != http_methods.GET and httpmethod != http_methods.HEAD:
  603. headers_to_sign = set([b'host'])
  604. # Compatible with STS request acquisition
  605. if config.security_token is not None:
  606. params[http_headers.STS_SECURITY_TOKEN.lower()] = config.security_token
  607. params[http_headers.AUTHORIZATION.lower()] = bce_v1_signer.sign(
  608. config.credentials,
  609. httpmethod,
  610. path,
  611. headers,
  612. params,
  613. timestamp,
  614. expiration_in_seconds,
  615. headers_to_sign)
  616. return b"%s://%s%s?%s" % (compat.convert_to_bytes(endpoint_protocol.name),
  617. full_host,
  618. path,
  619. utils.get_canonical_querystring(params, False))
  620. @required(bucket_name=(bytes, str), rules=(list, dict))
  621. def put_bucket_lifecycle(self,
  622. bucket_name,
  623. rules,
  624. config=None):
  625. """
  626. Put Bucket Lifecycle
  627. :type bucket: string
  628. :param bucket: None
  629. :type rules: list
  630. :param rules: None
  631. :return:**Http Response**
  632. """
  633. return self._send_request(http_methods.PUT,
  634. bucket_name,
  635. params={b'lifecycle': b''},
  636. body=json.dumps({'rule': rules}),
  637. config=config)
  638. @required(bucket_name=(bytes, str))
  639. def get_bucket_lifecycle(self, bucket_name, config=None):
  640. """
  641. Get Bucket Lifecycle
  642. :type bucket: string
  643. :param bucket: None
  644. :return:**Http Response**
  645. """
  646. return self._send_request(http_methods.GET,
  647. bucket_name,
  648. params={b'lifecycle': b''},
  649. config=config)
  650. @required(bucket_name=(bytes, str))
  651. def delete_bucket_lifecycle(self, bucket_name, config=None):
  652. """
  653. Delete Bucket Lifecycle
  654. :type bucket: string
  655. :param bucket: None
  656. :return:**Http Response**
  657. """
  658. return self._send_request(http_methods.DELETE,
  659. bucket_name,
  660. params={b'lifecycle': b''},
  661. config=config)
  662. @required(bucket_name=(bytes, str), cors_configuration=list)
  663. def put_bucket_cors(self,
  664. bucket_name,
  665. cors_configuration,
  666. config=None):
  667. """
  668. Put Bucket Cors
  669. :type bucket: string
  670. :param bucket: None
  671. :type cors_configuration: list
  672. :param cors_configuration: None
  673. :return:**Http Response**
  674. """
  675. return self._send_request(http_methods.PUT,
  676. bucket_name,
  677. params={b'cors': b''},
  678. body=json.dumps({'corsConfiguration': cors_configuration}),
  679. config=config)
  680. @required(bucket_name=(bytes, str))
  681. def get_bucket_cors(self, bucket_name, config=None):
  682. """
  683. Get Bucket Cors
  684. :type bucket: string
  685. :param bucket: None
  686. :return:**Http Response**
  687. """
  688. return self._send_request(http_methods.GET,
  689. bucket_name,
  690. params={b'cors': b''},
  691. config=config)
  692. @required(bucket_name=(bytes, str))
  693. def delete_bucket_cors(self, bucket_name, config=None):
  694. """
  695. Delete Bucket Cors
  696. :type bucket: string
  697. :param bucket: None
  698. :return:**Http Response**
  699. """
  700. return self._send_request(http_methods.DELETE,
  701. bucket_name,
  702. params={b'cors': b''},
  703. config=config)
  704. @required(bucket_name=(bytes, str))
  705. def list_objects(self, bucket_name,
  706. max_keys=1000, prefix=None, marker=None, delimiter=None,
  707. config=None):
  708. """
  709. Get Object Information of bucket
  710. :type bucket: string
  711. :param bucket: None
  712. :type delimiter: string
  713. :param delimiter: None
  714. :type marker: string
  715. :param marker: None
  716. :type max_keys: int
  717. :param max_keys: value <= 1000
  718. :type prefix: string
  719. :param prefix: None
  720. :return:
  721. **_ListObjectsResponse Class**
  722. """
  723. params = {}
  724. if max_keys is not None:
  725. params[b'maxKeys'] = max_keys
  726. if prefix is not None:
  727. params[b'prefix'] = prefix
  728. if marker is not None:
  729. params[b'marker'] = marker
  730. if delimiter is not None:
  731. params[b'delimiter'] = delimiter
  732. return self._send_request(http_methods.GET, bucket_name, params=params, config=config)
  733. @required(bucket_name=(bytes, str))
  734. def list_all_objects(self, bucket_name, prefix=None, delimiter=None, config=None):
  735. """
  736. :param bucket_name:
  737. :param prefix:
  738. :param delimiter:
  739. :param config:
  740. :return:
  741. """
  742. marker = None
  743. while True:
  744. response = self.list_objects(
  745. bucket_name, marker=marker, prefix=prefix, delimiter=delimiter, config=config)
  746. for item in response.contents:
  747. yield item
  748. if response.is_truncated:
  749. marker = response.next_marker
  750. else:
  751. break
  752. @staticmethod
  753. def _get_range_header_dict(range):
  754. if range is None:
  755. return None
  756. if not isinstance(range, (list, tuple)):
  757. raise TypeError('range should be a list or a tuple')
  758. if len(range) != 2:
  759. raise ValueError('range should have length of 2')
  760. return {http_headers.RANGE: b'bytes=%d-%d' % tuple(range)}
  761. @staticmethod
  762. def _parse_bos_object(http_response, response):
  763. """Sets response.body to http_response and response.user_metadata to a dict consists of all http
  764. headers starts with 'x-bce-meta-'.
  765. :param http_response: the http_response object returned by HTTPConnection.getresponse()
  766. :type http_response: httplib.HTTPResponse
  767. :param response: general response object which will be returned to the caller
  768. :type response: baidubce.BceResponse
  769. :return: always true
  770. :rtype bool
  771. """
  772. user_metadata = {}
  773. headers_list = http_response.getheaders()
  774. if compat.PY3:
  775. temp_heads = []
  776. for k, v in headers_list:
  777. k = k.lower()
  778. temp_heads.append((k, v))
  779. headers_list = temp_heads
  780. prefix = compat.convert_to_string(
  781. http_headers.BCE_USER_METADATA_PREFIX
  782. )
  783. for k, v in headers_list:
  784. if k.startswith(prefix):
  785. k = k[len(prefix):]
  786. user_metadata[compat.convert_to_unicode(k)] = \
  787. compat.convert_to_unicode(v)
  788. response.metadata.user_metadata = user_metadata
  789. response.data = http_response
  790. return True
  791. @required(bucket_name=(bytes, str), key=(bytes, str))
  792. def get_object(self, bucket_name, key, range=None, traffic_limit=None, version_id=None,
  793. cond_read_write=None, config=None):
  794. """
  795. :param bucket_name:
  796. :param key:
  797. :param range:
  798. :param traffic_limit:
  799. :param version_id:
  800. :param cond_read_write:
  801. :param config:
  802. :return:
  803. """
  804. query_params = {}
  805. if version_id is not None:
  806. version_id = compat.convert_to_bytes(version_id)
  807. query_params={b'versionId': version_id}
  808. key = compat.convert_to_bytes(key)
  809. if len(key) == 0 or key.startswith(b"/"):
  810. raise BceClientError("Key can not be empty or start with '/' .")
  811. range_header = BosClient._get_range_header_dict(range)
  812. if traffic_limit is not None:
  813. if range_header is None:
  814. range_header = {}
  815. range_header[http_headers.BOS_TRAFFIC_LIMIT] = traffic_limit
  816. if cond_read_write is not None:
  817. if range_header is None:
  818. range_header = {}
  819. range_header = self._get_cond_read_write_headers(http_methods.GET, range_header, cond_read_write)
  820. return self._send_request(
  821. http_methods.GET,
  822. bucket_name,
  823. key,
  824. headers=range_header,
  825. params=query_params,
  826. config=config,
  827. body_parser=BosClient._parse_bos_object)
  828. # restore object
  829. @required(bucket_name=(bytes, str), key=(bytes, str))
  830. def restore_object(self, bucket_name, key, days=None, tier="Standard", config=None):
  831. """
  832. :param bucket_name:
  833. :param key:
  834. :param config:
  835. :return:
  836. """
  837. key = compat.convert_to_bytes(key)
  838. headers = {}
  839. if days is not None:
  840. headers[http_headers.BOS_RESTORE_DAYS] = days
  841. if compat.convert_to_string(tier) not in ("Standard", "Expedited", "LowCost"):
  842. raise ValueError('invalid tier:{} for restore_object.The valid value is \"Standard\" or \"Expedited\" or '\
  843. '\"LowCost\"'.format(tier) )
  844. headers[http_headers.BOS_RESTORE_TIER] = compat.convert_to_bytes(tier)
  845. return self._send_request(
  846. http_methods.POST,
  847. bucket_name,
  848. key,
  849. headers=headers,
  850. params={b'restore': b''},
  851. config=config,
  852. body_parser=BosClient._parse_bos_object)
  853. @staticmethod
  854. def _save_body_to_file(http_response, response, file_name, buf_size=16 * 1024, progress_callback=None):
  855. f = open(file_name, 'wb')
  856. try:
  857. # Added progress bar monitoring
  858. if progress_callback:
  859. file_size = int(response.metadata.content_length)
  860. stream = utils.make_progress_adapter(http_response, progress_callback, file_size)
  861. else:
  862. stream = http_response
  863. shutil.copyfileobj(stream, f, buf_size)
  864. http_response.close()
  865. finally:
  866. f.close()
  867. return True
  868. @staticmethod
  869. def _parse_select_message(http_response, response, select_response):
  870. select_response.init_from_http_response(http_response, response)
  871. return True
  872. @required(bucket_name=(bytes, str), key=(bytes, str))
  873. def get_object_as_string(self, bucket_name, key, range=None, version_id=None, cond_read_write=None, config=None):
  874. """
  875. :param bucket_name:
  876. :param key:
  877. :param range:
  878. :param config:
  879. :return:
  880. """
  881. key = compat.convert_to_bytes(key)
  882. response = self.get_object(bucket_name, key, range=range, version_id = version_id,
  883. cond_read_write = cond_read_write, config=config)
  884. s = response.data.read()
  885. response.data.close()
  886. return s
  887. @required(bucket_name=(bytes, str), key=(bytes, str), file_name=(bytes, str))
  888. def get_object_to_file(self, bucket_name, key, file_name, range=None, config=None,
  889. progress_callback=None, traffic_limit=None, cond_read_write=None, version_id=None):
  890. """
  891. Get Content of Object and Put Content to File
  892. :type bucket: string
  893. :param bucket: None
  894. :type key: string
  895. :param key: None
  896. :type file_name: string
  897. :param file_name: None
  898. :type range: tuple
  899. :param range: (0,9) represent get object contents of 0-9 in bytes. 10 bytes date in total.
  900. :return:
  901. **HTTP Response**
  902. """
  903. query_params = {}
  904. if version_id is not None:
  905. version_id = compat.convert_to_bytes(version_id)
  906. query_params={b'versionId': version_id}
  907. key = compat.convert_to_bytes(key)
  908. if len(key) == 0 or key.startswith(b"/"):
  909. raise BceClientError("Key can not be empty or start with '/' .")
  910. file_name = compat.convert_to_bytes(file_name)
  911. range_header = BosClient._get_range_header_dict(range)
  912. if traffic_limit is not None:
  913. if range_header is None:
  914. range_header = {}
  915. range_header[http_headers.BOS_TRAFFIC_LIMIT] = traffic_limit
  916. if cond_read_write is not None:
  917. if range_header is None:
  918. range_header = {}
  919. range_header = self._get_cond_read_write_headers(http_methods.GET, range_header, cond_read_write)
  920. return self._send_request(
  921. http_methods.GET,
  922. bucket_name,
  923. key,
  924. headers=range_header,
  925. params=query_params,
  926. config=config,
  927. body_parser=lambda http_response, response: BosClient._save_body_to_file(
  928. http_response,
  929. response,
  930. file_name,
  931. self._get_config_parameter(config, 'recv_buf_size'),
  932. progress_callback=progress_callback))
  933. @required(bucket_name=(bytes, str), key=(bytes, str))
  934. def get_object_meta_data(self, bucket_name, key, version_id=None, cond_read_write=None, config=None):
  935. """
  936. Get head of object
  937. :type bucket: string
  938. :param bucket: None
  939. :type key: string
  940. :param key: None
  941. :return:
  942. **_GetObjectMetaDataResponse Class**
  943. """
  944. query_params = {}
  945. if version_id is not None:
  946. version_id = compat.convert_to_bytes(version_id)
  947. query_params={b'versionId': version_id}
  948. headers = {}
  949. if cond_read_write is not None:
  950. headers = self._get_cond_read_write_headers(http_methods.HEAD, headers, cond_read_write)
  951. key = compat.convert_to_bytes(key)
  952. return self._send_request(http_methods.HEAD, bucket_name, key,
  953. headers=headers, params=query_params, config=config)
  954. @required(bucket_name=(bytes, str),
  955. key=(bytes, str),
  956. data=object,
  957. content_length=compat.integer_types,
  958. content_md5=(bytes, str))
  959. def append_object(self, bucket_name, key, data,
  960. content_md5,
  961. content_length,
  962. offset=None,
  963. content_type=None,
  964. user_metadata=None,
  965. content_sha256=None,
  966. storage_class=None,
  967. user_headers=None,
  968. progress_callback=None,
  969. traffic_limit=None,
  970. object_tagging=None,
  971. config=None):
  972. """
  973. Put an appendable object to BOS or add content to an appendable object
  974. :type bucket: string
  975. :param bucket: None
  976. :type key: string
  977. :param key: None
  978. :type content_length: long
  979. :type offset: long
  980. :return:
  981. **HTTP Response**
  982. """
  983. key = compat.convert_to_bytes(key)
  984. content_md5 = compat.convert_to_bytes(content_md5)
  985. headers = self._prepare_object_headers(
  986. content_length=content_length,
  987. content_md5=content_md5,
  988. content_type=content_type,
  989. content_sha256=content_sha256,
  990. user_metadata=user_metadata,
  991. storage_class=storage_class,
  992. user_headers=user_headers,
  993. traffic_limit=traffic_limit,
  994. object_tagging=object_tagging)
  995. if content_length > bos.MAX_APPEND_OBJECT_LENGTH:
  996. raise ValueError('Object length should be less than %d. '
  997. 'Use multi-part upload instead.' % bos.MAX_APPEND_OBJECT_LENGTH)
  998. params = {b'append': b''}
  999. if offset is not None:
  1000. params[b'offset'] = offset
  1001. if progress_callback:
  1002. data = utils.make_progress_adapter(data, progress_callback)
  1003. return self._send_request(
  1004. http_methods.POST,
  1005. bucket_name,
  1006. key,
  1007. body=data,
  1008. headers=headers,
  1009. params=params,
  1010. config=config)
  1011. @required(bucket_name=(bytes, str),
  1012. key=(bytes, str),
  1013. data=(bytes, str))
  1014. def append_object_from_string(self, bucket_name, key, data,
  1015. content_md5=None,
  1016. offset=None,
  1017. content_type=None,
  1018. user_metadata=None,
  1019. content_sha256=None,
  1020. storage_class=None,
  1021. user_headers=None,
  1022. progress_callback=None,
  1023. traffic_limit=None,
  1024. config=None):
  1025. """
  1026. Create an appendable object and put content of string to the object
  1027. or add content of string to an appendable object
  1028. """
  1029. key = compat.convert_to_bytes(key)
  1030. if isinstance(data, str):
  1031. data = data.encode(baidubce.DEFAULT_ENCODING)
  1032. fp = None
  1033. try:
  1034. fp = io.BytesIO(data)
  1035. if content_md5 is None:
  1036. content_md5 = utils.get_md5_from_fp(
  1037. fp, buf_size=self._get_config_parameter(config, 'recv_buf_size'))
  1038. return self.append_object(bucket_name=bucket_name,
  1039. key=key,
  1040. data=fp,
  1041. content_md5=content_md5,
  1042. content_length=len(data),
  1043. offset=offset,
  1044. content_type=content_type,
  1045. user_metadata=user_metadata,
  1046. content_sha256=content_sha256,
  1047. storage_class=storage_class,
  1048. user_headers=user_headers,
  1049. progress_callback=progress_callback,
  1050. traffic_limit=traffic_limit,
  1051. config=config)
  1052. finally:
  1053. if fp is not None:
  1054. fp.close()
  1055. @required(bucket_name=(bytes, str),
  1056. key=(bytes, str),
  1057. data=object,
  1058. content_length=compat.integer_types,
  1059. content_md5=(bytes, str))
  1060. def put_object(self, bucket_name, key, data,
  1061. content_length,
  1062. content_md5,
  1063. content_type=None,
  1064. content_sha256=None,
  1065. user_metadata=None,
  1066. storage_class=None,
  1067. user_headers=None,
  1068. encryption=None,
  1069. customer_key=None,
  1070. customer_key_md5=None,
  1071. progress_callback=None,
  1072. traffic_limit=None,
  1073. object_tagging=None,
  1074. cond_read_write=None,
  1075. config=None):
  1076. """
  1077. Put object and put content of file to the object
  1078. :type bucket: string
  1079. :param bucket: None
  1080. :type key: string
  1081. :param key: None
  1082. :type fp: FILE
  1083. :param fp: None
  1084. :type file_size: long
  1085. :type offset: long
  1086. :type content_length: long
  1087. :return:
  1088. **HTTP Response**
  1089. """
  1090. key = compat.convert_to_bytes(key)
  1091. content_md5 = compat.convert_to_bytes(content_md5)
  1092. headers = self._prepare_object_headers(
  1093. content_length=content_length,
  1094. content_md5=content_md5,
  1095. content_type=content_type,
  1096. content_sha256=content_sha256,
  1097. user_metadata=user_metadata,
  1098. storage_class=storage_class,
  1099. user_headers=user_headers,
  1100. traffic_limit=traffic_limit,
  1101. object_tagging=object_tagging,)
  1102. if cond_read_write is not None:
  1103. headers = self._get_cond_read_write_headers(http_methods.PUT, headers, cond_read_write)
  1104. buf_size = self._get_config_parameter(config, 'recv_buf_size')
  1105. if content_length > bos.MAX_PUT_OBJECT_LENGTH:
  1106. raise ValueError('Object length should be less than %d. '
  1107. 'Use multi-part upload instead.' % bos.MAX_PUT_OBJECT_LENGTH)
  1108. if progress_callback:
  1109. data = utils.make_progress_adapter(data, progress_callback)
  1110. return self._send_request(
  1111. http_methods.PUT,
  1112. bucket_name,
  1113. key,
  1114. body=data,
  1115. headers=headers,
  1116. config=config)
  1117. @required(bucket=(bytes, str), key=(bytes, str), data=(bytes, str))
  1118. def put_object_from_string(self, bucket, key, data,
  1119. content_md5=None,
  1120. content_type=None,
  1121. content_sha256=None,
  1122. user_metadata=None,
  1123. storage_class=None,
  1124. user_headers=None,
  1125. encryption=None,
  1126. customer_key=None,
  1127. customer_key_md5=None,
  1128. progress_callback=None,
  1129. traffic_limit=None,
  1130. object_tagging=None,
  1131. cond_read_write=None,
  1132. config=None):
  1133. """
  1134. Create object and put content of string to the object
  1135. :type bucket: string
  1136. :param bucket: None
  1137. :type key: string
  1138. :param key: None
  1139. :type input_content: string
  1140. :param input_content: None
  1141. :type options: dict
  1142. :param options: None
  1143. :return:
  1144. **HTTP Response**
  1145. """
  1146. key = compat.convert_to_bytes(key)
  1147. if isinstance(data, str):
  1148. data = data.encode(baidubce.DEFAULT_ENCODING)
  1149. fp = None
  1150. try:
  1151. fp = io.BytesIO(data)
  1152. if content_md5 is None:
  1153. content_md5 = utils.get_md5_from_fp(
  1154. fp, buf_size=self._get_config_parameter(config, 'recv_buf_size'))
  1155. return self.put_object(bucket, key, fp,
  1156. content_length=len(data),
  1157. content_md5=content_md5,
  1158. content_type=content_type,
  1159. content_sha256=content_sha256,
  1160. user_metadata=user_metadata,
  1161. storage_class=storage_class,
  1162. user_headers=user_headers,
  1163. encryption=encryption,
  1164. customer_key=customer_key,
  1165. customer_key_md5=customer_key_md5,
  1166. progress_callback = progress_callback,
  1167. traffic_limit=traffic_limit,
  1168. object_tagging=object_tagging,
  1169. cond_read_write=cond_read_write,
  1170. config=config)
  1171. finally:
  1172. if fp is not None:
  1173. fp.close()
  1174. @required(bucket=(bytes, str), key=(bytes, str), file_name=(bytes, str))
  1175. def put_object_from_file(self, bucket, key, file_name,
  1176. content_length=None,
  1177. content_md5=None,
  1178. content_type=None,
  1179. content_sha256=None,
  1180. user_metadata=None,
  1181. storage_class=None,
  1182. user_headers=None,
  1183. encryption=None,
  1184. customer_key=None,
  1185. customer_key_md5=None,
  1186. progress_callback=None,
  1187. traffic_limit=None,
  1188. object_tagging=None,
  1189. cond_read_write=None,
  1190. config=None,
  1191. ):
  1192. """
  1193. Put object and put content of file to the object
  1194. :type bucket: string
  1195. :param bucket: None
  1196. :type key: string
  1197. :param key: None
  1198. :type file_name: string
  1199. :param file_name: None
  1200. :type options: dict
  1201. :param options: None
  1202. :return:
  1203. **HttpResponse Class**
  1204. """
  1205. key = compat.convert_to_bytes(key)
  1206. fp = open(file_name, 'rb')
  1207. try:
  1208. if content_length is None:
  1209. fp.seek(0, os.SEEK_END)
  1210. content_length = fp.tell()
  1211. fp.seek(0)
  1212. if content_md5 is None:
  1213. recv_buf_size = self._get_config_parameter(config, 'recv_buf_size')
  1214. content_md5 = utils.get_md5_from_fp(fp, length=content_length,
  1215. buf_size=recv_buf_size)
  1216. if content_type is None:
  1217. content_type = utils.guess_content_type_by_file_name(file_name)
  1218. return self.put_object(bucket, key, fp,
  1219. content_length=content_length,
  1220. content_md5=content_md5,
  1221. content_type=content_type,
  1222. content_sha256=content_sha256,
  1223. user_metadata=user_metadata,
  1224. storage_class=storage_class,
  1225. user_headers=user_headers,
  1226. encryption=encryption,
  1227. customer_key=customer_key,
  1228. customer_key_md5=customer_key_md5,
  1229. progress_callback=progress_callback,
  1230. traffic_limit=traffic_limit,
  1231. object_tagging=object_tagging,
  1232. cond_read_write=cond_read_write,
  1233. config=config)
  1234. finally:
  1235. fp.close()
  1236. @required(source_bucket_name=(bytes, str),
  1237. source_key=(bytes, str),
  1238. target_bucket_name=(bytes, str),
  1239. target_key=(bytes, str))
  1240. def copy_object(self,
  1241. source_bucket_name, source_key,
  1242. target_bucket_name, target_key,
  1243. etag=None,
  1244. content_type=None,
  1245. user_metadata=None,
  1246. storage_class=None,
  1247. user_headers=None,
  1248. copy_object_user_headers=None,
  1249. traffic_limit=None,
  1250. object_tagging=None,
  1251. source_version_id=None,
  1252. config=None):
  1253. """
  1254. Copy one object to another object
  1255. :type source_bucket: string
  1256. :param source_bucket: None
  1257. :type source_key: string
  1258. :param source_key: None
  1259. :type target_bucket: string
  1260. :param target_bucket: None
  1261. :type target_key: string
  1262. :param target_key: None
  1263. :return:
  1264. **HttpResponse Class**
  1265. """
  1266. source_key = compat.convert_to_bytes(source_key)
  1267. target_key = compat.convert_to_bytes(target_key)
  1268. headers = self._prepare_object_headers(
  1269. content_type=content_type,
  1270. user_metadata=user_metadata,
  1271. storage_class=storage_class,
  1272. user_headers=user_headers,
  1273. traffic_limit=traffic_limit,
  1274. object_tagging=object_tagging)
  1275. merge_source_key = utils.normalize_string(
  1276. b'/%s/%s' % (
  1277. compat.convert_to_bytes(source_bucket_name),
  1278. source_key), False)
  1279. if source_version_id is not None:
  1280. merge_source_key = merge_source_key + b'?versionId=%s' % compat.convert_to_bytes(source_version_id)
  1281. headers[http_headers.BCE_COPY_SOURCE] = merge_source_key
  1282. if etag is not None:
  1283. headers[http_headers.BCE_COPY_SOURCE_IF_MATCH] = etag
  1284. if user_metadata is not None or content_type is not None:
  1285. headers[http_headers.BCE_COPY_METADATA_DIRECTIVE] = b'replace'
  1286. else:
  1287. headers[http_headers.BCE_COPY_METADATA_DIRECTIVE] = b'copy'
  1288. if copy_object_user_headers is not None:
  1289. try:
  1290. headers = BosClient._get_user_header(headers, copy_object_user_headers, True)
  1291. except Exception as e:
  1292. raise e
  1293. return self._send_request(
  1294. http_methods.PUT,
  1295. target_bucket_name,
  1296. target_key,
  1297. headers=headers,
  1298. config=config,
  1299. body_parser=bos_handler.parse_copy_object_response)
  1300. @required(bucket_name=(bytes, str), key=(bytes, str))
  1301. def delete_object(self, bucket_name, key, version_id=None, config=None):
  1302. """
  1303. Delete Object
  1304. :type bucket: string
  1305. :param bucket: None
  1306. :type key: string
  1307. :param key: None
  1308. :return:
  1309. **HttpResponse Class**
  1310. """
  1311. query_params = {}
  1312. if version_id is not None:
  1313. version_id = compat.convert_to_bytes(version_id)
  1314. query_params={b'versionId': version_id}
  1315. key = compat.convert_to_bytes(key)
  1316. return self._send_request(http_methods.DELETE, bucket_name, key,
  1317. params=query_params, config=config)
  1318. @required(bucket_name=(bytes, str), key_list=list)
  1319. def delete_multiple_objects(self, bucket_name, key_list, config=None):
  1320. """
  1321. Delete Multiple Objects
  1322. :type bucket: string
  1323. :param bucket: None
  1324. :type key_list: string list
  1325. :param key_list: None
  1326. :return:
  1327. **HttpResponse Class**
  1328. """
  1329. key_list_json = [{'key': compat.convert_to_string(k)} for k in key_list]
  1330. return self._send_request(http_methods.POST,
  1331. bucket_name,
  1332. body=json.dumps({'objects': key_list_json}),
  1333. params={b'delete': b''},
  1334. config=config)
  1335. @required(source_bucket=(bytes, str),
  1336. target_bucket=(bytes, str),
  1337. target_prefix=(bytes, str))
  1338. def put_bucket_logging(self,
  1339. source_bucket,
  1340. target_bucket,
  1341. target_prefix=None,
  1342. config=None):
  1343. """
  1344. Put Bucket Logging
  1345. :type source_bucket: string
  1346. :param source_bucket: None
  1347. :type target_bucket: string
  1348. :param target_bucket: None
  1349. :return:
  1350. **HttpResponse Class**
  1351. """
  1352. return self._send_request(http_methods.PUT,
  1353. source_bucket,
  1354. params={b'logging': b''},
  1355. body=json.dumps({'targetBucket': target_bucket,
  1356. 'targetPrefix': target_prefix}),
  1357. config=config)
  1358. @required(bucket_name=(bytes, str))
  1359. def get_bucket_logging(self, bucket_name, config=None):
  1360. """
  1361. Get Bucket Logging
  1362. :type bucket_name: string
  1363. :param bucket_name: None
  1364. :return:
  1365. **HttpResponse Class**
  1366. """
  1367. return self._send_request(http_methods.GET,
  1368. bucket_name,
  1369. params={b'logging': b''},
  1370. config=config)
  1371. @required(bucket_name=(bytes, str))
  1372. def delete_bucket_logging(self, bucket_name, config=None):
  1373. """
  1374. Delete Bucket Logging
  1375. :type bucket_name: string
  1376. :param bucket_name: None
  1377. :return:
  1378. **HttpResponse Class**
  1379. """
  1380. return self._send_request(http_methods.DELETE,
  1381. bucket_name,
  1382. params={b'logging': b''},
  1383. config=config)
  1384. @required(bucket_name=(bytes, str), key=(bytes, str))
  1385. def initiate_multipart_upload(self,
  1386. bucket_name,
  1387. key,
  1388. content_type=None,
  1389. storage_class=None,
  1390. user_headers=None,
  1391. config=None):
  1392. """
  1393. Initialize multi_upload_file.
  1394. :type bucket: string
  1395. :param bucket: None
  1396. :type key: string
  1397. :param key: None
  1398. :return:
  1399. **HttpResponse**
  1400. """
  1401. key = compat.convert_to_bytes(key)
  1402. headers = {}
  1403. if storage_class is not None:
  1404. headers[http_headers.BOS_STORAGE_CLASS] = storage_class
  1405. if content_type is not None:
  1406. headers[http_headers.CONTENT_TYPE] = utils.convert_to_standard_string(content_type)
  1407. else:
  1408. headers[http_headers.CONTENT_TYPE] = http_content_types.OCTET_STREAM
  1409. if user_headers is not None:
  1410. try:
  1411. headers = BosClient._get_user_header(headers, user_headers, False)
  1412. except Exception as e:
  1413. raise e
  1414. return self._send_request(
  1415. http_methods.POST,
  1416. bucket_name,
  1417. key,
  1418. headers=headers,
  1419. params={b'uploads': b''},
  1420. config=config)
  1421. @required(bucket_name=(bytes, str),
  1422. key=(bytes, str),
  1423. upload_id=(bytes, str),
  1424. part_number=int,
  1425. part_size=compat.integer_types,
  1426. part_fp=object)
  1427. def upload_part(self, bucket_name, key, upload_id,
  1428. part_number, part_size, part_fp, part_md5=None,
  1429. progress_callback=None, traffic_limit=None, config=None):
  1430. """
  1431. Upload a part.
  1432. :type bucket: string
  1433. :param bucket: None
  1434. :type key: string
  1435. :param key: None
  1436. :type upload_id: string
  1437. :param upload_id: None
  1438. :type part_number: int
  1439. :param part_number: None
  1440. :type part_size: int or long
  1441. :param part_size: None
  1442. :type part_fp: file pointer
  1443. :param part_fp: not None
  1444. :type part_md5: str
  1445. :param part_md5: None
  1446. :type config: dict
  1447. :param config: None
  1448. :return:
  1449. **HttpResponse**
  1450. """
  1451. key = compat.convert_to_bytes(key)
  1452. if part_number < bos.MIN_PART_NUMBER or part_number > bos.MAX_PART_NUMBER:
  1453. raise ValueError('Invalid part_number %d. The valid range is from %d to %d.' % (
  1454. part_number, bos.MIN_PART_NUMBER, bos.MAX_PART_NUMBER))
  1455. if part_size > bos.MAX_PUT_OBJECT_LENGTH:
  1456. raise ValueError('Single part length should be less than %d. '
  1457. % bos.MAX_PUT_OBJECT_LENGTH)
  1458. headers = {http_headers.CONTENT_LENGTH: part_size,
  1459. http_headers.CONTENT_TYPE: http_content_types.OCTET_STREAM}
  1460. if part_md5 is not None:
  1461. headers[http_headers.CONTENT_MD5] = part_md5
  1462. if progress_callback:
  1463. part_fp = utils.make_progress_adapter(part_fp, progress_callback, part_size)
  1464. if traffic_limit is not None:
  1465. headers[http_headers.BOS_TRAFFIC_LIMIT] = traffic_limit
  1466. return self._send_request(
  1467. http_methods.PUT,
  1468. bucket_name,
  1469. key,
  1470. body=part_fp,
  1471. headers=headers,
  1472. params={b'partNumber': part_number, b'uploadId': upload_id},
  1473. config=config)
  1474. @required(source_bucket_name=(bytes, str),
  1475. source_key=(bytes, str),
  1476. target_bucket_name=(bytes, str),
  1477. target_key=(bytes, str),
  1478. upload_id=(bytes, str),
  1479. part_number=int,
  1480. part_size=compat.integer_types,
  1481. offset=compat.integer_types)
  1482. def upload_part_copy(self,
  1483. source_bucket_name, source_key,
  1484. target_bucket_name, target_key,
  1485. upload_id, part_number, part_size, offset,
  1486. etag=None,
  1487. content_type=None,
  1488. user_metadata=None,
  1489. traffic_limit=None,
  1490. config=None):
  1491. """
  1492. Copy part.
  1493. :type source_bucket_name: string
  1494. :param source_bucket_name: None
  1495. :type source_key: string
  1496. :param source_key: None
  1497. :type target_bucket_name: string
  1498. :param target_bucket_name: None
  1499. :type target_key: string
  1500. :param target_key: None
  1501. :type upload_id: string
  1502. :param upload_id: None
  1503. :return:
  1504. **HttpResponse**
  1505. """
  1506. source_key = compat.convert_to_bytes(source_key)
  1507. target_key = compat.convert_to_bytes(target_key)
  1508. headers = self._prepare_object_headers(
  1509. content_type=content_type,
  1510. user_metadata=user_metadata,
  1511. traffic_limit=traffic_limit)
  1512. headers[http_headers.BCE_COPY_SOURCE] = utils.normalize_string(
  1513. b"/%s/%s" % (compat.convert_to_bytes(source_bucket_name),
  1514. source_key), False)
  1515. range = b"""bytes=%d-%d""" % (offset, offset + part_size - 1)
  1516. headers[http_headers.BCE_COPY_SOURCE_RANGE] = range
  1517. if etag is not None:
  1518. headers[http_headers.BCE_COPY_SOURCE_IF_MATCH] = etag
  1519. return self._send_request(
  1520. http_methods.PUT,
  1521. target_bucket_name,
  1522. target_key,
  1523. headers=headers,
  1524. params={b'partNumber': part_number, b'uploadId': upload_id},
  1525. config=config)
  1526. @required(bucket_name=(bytes, str),
  1527. key=(bytes, str),
  1528. upload_id=(bytes, str),
  1529. part_number=int,
  1530. part_size=compat.integer_types,
  1531. file_name=(bytes, str),
  1532. offset=compat.integer_types)
  1533. def upload_part_from_file(self, bucket_name, key, upload_id,
  1534. part_number, part_size, file_name, offset, part_md5=None,
  1535. progress_callback=None, traffic_limit=None, config=None):
  1536. """
  1537. :param bucket_name:
  1538. :param key:
  1539. :param upload_id:
  1540. :param part_number:
  1541. :param part_size:
  1542. :param file_name:
  1543. :param offset:
  1544. :param part_md5:
  1545. :param config:
  1546. :return:
  1547. """
  1548. key = compat.convert_to_bytes(key)
  1549. f = open(file_name, 'rb')
  1550. try:
  1551. f.seek(offset)
  1552. return self.upload_part(bucket_name, key, upload_id, part_number, part_size, f,
  1553. part_md5=part_md5, progress_callback=progress_callback,
  1554. traffic_limit=traffic_limit, config=config)
  1555. finally:
  1556. f.close()
  1557. @required(bucket_name=(bytes, str),
  1558. key=(bytes, str),
  1559. upload_id=(bytes, str),
  1560. part_list=list)
  1561. def complete_multipart_upload(self, bucket_name, key,
  1562. upload_id, part_list,
  1563. user_headers=None,
  1564. user_metadata=None,
  1565. cond_read_write=None,
  1566. config=None):
  1567. """
  1568. After finish all the task, complete multi_upload_file.
  1569. :type bucket: string
  1570. :param bucket: None
  1571. :type key: string
  1572. :param key: None
  1573. :type upload_id: string
  1574. :param upload_id: None
  1575. :type part_list: list
  1576. :param part_list: None
  1577. :return:
  1578. **HttpResponse**
  1579. """
  1580. key = compat.convert_to_bytes(key)
  1581. headers = self._prepare_object_headers(
  1582. content_type=http_content_types.JSON,
  1583. user_metadata=user_metadata,
  1584. user_headers=user_headers)
  1585. if cond_read_write is not None:
  1586. headers = self._get_cond_read_write_headers(http_methods.POST, headers, cond_read_write)
  1587. return self._send_request(
  1588. http_methods.POST,
  1589. bucket_name,
  1590. key,
  1591. body=json.dumps({'parts': part_list}),
  1592. headers=headers,
  1593. params={b'uploadId': upload_id})
  1594. @required(bucket_name=(bytes, str), key=(bytes, str), upload_id=(bytes, str))
  1595. def abort_multipart_upload(self, bucket_name, key, upload_id, config=None):
  1596. """
  1597. Abort upload a part which is being uploading.
  1598. :type bucket: string
  1599. :param bucket: None
  1600. :type key: string
  1601. :param key: None
  1602. :type upload_id: string
  1603. :param upload_id: None
  1604. :return:
  1605. **HttpResponse**
  1606. """
  1607. key = compat.convert_to_bytes(key)
  1608. return self._send_request(http_methods.DELETE, bucket_name, key,
  1609. params={b'uploadId': upload_id})
  1610. @required(bucket_name=(bytes, str), key=(bytes, str), upload_id=(bytes, str))
  1611. def list_parts(self, bucket_name, key, upload_id,
  1612. max_parts=None, part_number_marker=None,
  1613. config=None):
  1614. """
  1615. List all the parts that have been upload success.
  1616. :type bucket: string
  1617. :param bucket: None
  1618. :type key: string
  1619. :param key: None
  1620. :type upload_id: string
  1621. :param upload_id: None
  1622. :type max_parts: int
  1623. :param max_parts: None
  1624. :type part_number_marker: string
  1625. :param part_number_marker: None
  1626. :return:
  1627. **_ListPartsResponse Class**
  1628. """
  1629. key = compat.convert_to_bytes(key)
  1630. params = {b'uploadId': upload_id}
  1631. if max_parts is not None:
  1632. params[b'maxParts'] = max_parts
  1633. if part_number_marker is not None:
  1634. params[b'partNumberMarker'] = part_number_marker
  1635. return self._send_request(http_methods.GET, bucket_name, key, params=params, config=config)
  1636. @required(bucket_name=(bytes, str), key=(bytes, str), upload_id=(bytes, str))
  1637. def list_all_parts(self, bucket_name, key, upload_id, config=None):
  1638. """
  1639. :param bucket_name:
  1640. :param key:
  1641. :param upload_id:
  1642. :param config:
  1643. :return:
  1644. """
  1645. key = compat.convert_to_bytes(key)
  1646. part_number_marker = None
  1647. while True:
  1648. response = self.list_parts(bucket_name, key, upload_id,
  1649. part_number_marker=part_number_marker, config=config)
  1650. for item in response.parts:
  1651. yield item
  1652. if not response.is_truncated:
  1653. break
  1654. part_number_marker = response.next_part_number_marker
  1655. @required(bucket_name=(bytes, str))
  1656. def list_multipart_uploads(self, bucket_name, max_uploads=None, key_marker=None,
  1657. prefix=None, delimiter=None,
  1658. config=None):
  1659. """
  1660. List all Multipart upload task which haven't been ended.(Completed Init_MultiPartUpload
  1661. but not completed Complete_MultiPartUpload or Abort_MultiPartUpload)
  1662. :type bucket: string
  1663. :param bucket: None
  1664. :type delimiter: string
  1665. :param delimiter: None
  1666. :type max_uploads: int
  1667. :param max_uploads: <=1000
  1668. :type key_marker: string
  1669. :param key_marker: None
  1670. :type prefix: string
  1671. :param prefix: None
  1672. :type upload_id_marker: string
  1673. :param upload_id_marker:
  1674. :return:
  1675. **_ListMultipartUploadResponse Class**
  1676. """
  1677. params = {b'uploads': b''}
  1678. if delimiter is not None:
  1679. params[b'delimiter'] = delimiter
  1680. if max_uploads is not None:
  1681. params[b'maxUploads'] = max_uploads
  1682. if key_marker is not None:
  1683. params[b'keyMarker'] = key_marker
  1684. if prefix is not None:
  1685. params[b'prefix'] = prefix
  1686. return self._send_request(http_methods.GET, bucket_name, params=params, config=config)
  1687. @required(bucket_name=(bytes, str))
  1688. def list_all_multipart_uploads(self, bucket_name, prefix=None, delimiter=None, config=None):
  1689. """
  1690. :param bucket_name:
  1691. :param prefix:
  1692. :param delimiter:
  1693. :param config:
  1694. :return:
  1695. """
  1696. key_marker = None
  1697. while True:
  1698. response = self.list_multipart_uploads(bucket_name,
  1699. key_marker=key_marker,
  1700. prefix=prefix,
  1701. delimiter=delimiter,
  1702. config=config)
  1703. for item in response.uploads:
  1704. yield item
  1705. if not response.is_truncated:
  1706. break
  1707. if response.next_key_marker is not None:
  1708. key_marker = response.next_key_marker
  1709. elif len(response.uploads) != 0:
  1710. key_marker = response.uploads[-1].key
  1711. else:
  1712. break
  1713. def _upload_task(self, bucket_name, object_key, upload_id,
  1714. part_number, part_size, file_name, offset, part_list, uploadTaskHandle,
  1715. progress_callback=None, traffic_limit=None):
  1716. if uploadTaskHandle.is_cancel():
  1717. _logger.debug("upload task canceled with partNumber={}!".format(part_number))
  1718. return
  1719. try:
  1720. response = self.upload_part_from_file(bucket_name, object_key, upload_id,
  1721. part_number, part_size, file_name, offset, progress_callback=progress_callback
  1722. , traffic_limit=traffic_limit)
  1723. part_list.append({
  1724. "partNumber": part_number,
  1725. "eTag": response.metadata.etag
  1726. })
  1727. _logger.debug("upload task success with partNumber={}!".format(part_number))
  1728. except Exception as e:
  1729. _logger.debug("upload task failed with partNumber={}!".format(part_number))
  1730. raise e
  1731. #_logger.debug(e)
  1732. @required(bucket_name=(bytes, str), key=(bytes, str), file_name=(bytes, str))
  1733. def put_super_obejct_from_file(self, bucket_name, key, file_name, chunk_size=5,
  1734. thread_num=None,
  1735. uploadTaskHandle=None,
  1736. content_type=None,
  1737. storage_class=None,
  1738. user_headers=None,
  1739. progress_callback=None,
  1740. traffic_limit=None,
  1741. cond_read_write=None,
  1742. config=None):
  1743. """
  1744. Multipart Upload file to bos
  1745. param chunk_size: part size , default part size is 5MB
  1746. """
  1747. # check params
  1748. if chunk_size > 5 * 1024 or chunk_size <= 0:
  1749. raise BceClientError("chunk size is valid, it should be more than 0 and not nore than 5120!")
  1750. left_size = os.path.getsize(file_name)
  1751. # if file size more than 48.8TB, reject
  1752. if left_size > 50000 * 1024 * 1024 * 1024:
  1753. raise BceClientError("File size must not be more than 48.8TB!")
  1754. if thread_num is None or thread_num <= 1:
  1755. thread_num = multiprocessing.cpu_count()
  1756. part_size = chunk_size * 1024 * 1024
  1757. total_part = left_size // part_size
  1758. if left_size % part_size != 0:
  1759. total_part += 1
  1760. if uploadTaskHandle is None:
  1761. uploadTaskHandle = UploadTaskHandle()
  1762. # initial
  1763. upload_id = self.initiate_multipart_upload(bucket_name, key,
  1764. content_type=content_type,
  1765. storage_class=storage_class,
  1766. user_headers=user_headers).upload_id
  1767. executor = ThreadPoolExecutor(thread_num)
  1768. all_tasks = []
  1769. offset = 0
  1770. part_number = 1
  1771. part_list = []
  1772. while left_size > 0:
  1773. if left_size < part_size:
  1774. part_size = left_size
  1775. temp_task= executor.submit(self._upload_task, bucket_name, key, upload_id, part_number, part_size,
  1776. file_name, offset, part_list, uploadTaskHandle, progress_callback, traffic_limit)
  1777. all_tasks.append(temp_task)
  1778. left_size -= part_size
  1779. offset += part_size
  1780. part_number += 1
  1781. # wait all upload task to exit
  1782. wait(all_tasks, return_when=ALL_COMPLETED)
  1783. if uploadTaskHandle.is_cancel():
  1784. _logger.debug("putting super object is canceled!")
  1785. self.abort_multipart_upload(bucket_name, key, upload_id = upload_id)
  1786. return False
  1787. elif len(part_list) != total_part:
  1788. _logger.debug("putting super object failed!")
  1789. self.abort_multipart_upload(bucket_name, key, upload_id = upload_id)
  1790. return False
  1791. # sort
  1792. part_list.sort(key=lambda x: x["partNumber"])
  1793. # complete_multipart_upload
  1794. self.complete_multipart_upload(bucket_name, key, upload_id, part_list, cond_read_write)
  1795. return True
  1796. @required(bucket_name=(bytes, str), key=(bytes, str), acl=(list, dict))
  1797. def set_object_acl(self, bucket_name, key, acl, config=None):
  1798. """
  1799. Set Access Control Level of object
  1800. :type bucket: string
  1801. :param bucket: None
  1802. :type acl: list of grant
  1803. :param acl: None
  1804. :return:
  1805. **HttpResponse Class**
  1806. """
  1807. key = compat.convert_to_bytes(key)
  1808. self._send_request(http_methods.PUT,
  1809. bucket_name,
  1810. key,
  1811. body=json.dumps({'accessControlList': acl},
  1812. default=BosClient._dump_acl_object),
  1813. headers={http_headers.CONTENT_TYPE: http_content_types.JSON},
  1814. params={b'acl': b''},
  1815. config=config)
  1816. @required(bucket_name=(bytes, str), key=(bytes, str))
  1817. def set_object_canned_acl(self, bucket_name, key,
  1818. canned_acl=None,
  1819. grant_read=None,
  1820. grant_full_control=None,
  1821. config=None):
  1822. """
  1823. :type bucket_name: string
  1824. :param bucket_name: None
  1825. :type key: string
  1826. :param key: None
  1827. :type canned_acl: string
  1828. :param canned_acl: for header 'x-bce-acl', it's value only is
  1829. canned_acl.PRIVATE or canned_acl.PRIVATE_READ
  1830. :type grant_read: string
  1831. :param grant_read: Object id of getting READ right permission.
  1832. for exapmle,grant_read = 'id="6c47...4c94",id="8c42...4c94"'
  1833. :type grant_full_control: string
  1834. :param grant_full_control: Object id of getting READ right permission.
  1835. for exapmle,grant_full_control = 'id="6c47...4c94",id="8c42...4c94"'
  1836. :param config:
  1837. :return:
  1838. **HttpResponse Class**
  1839. """
  1840. key = compat.convert_to_bytes(key)
  1841. headers = None
  1842. num_args = 0
  1843. if canned_acl is not None:
  1844. headers = {http_headers.BCE_ACL: compat.convert_to_bytes(canned_acl)}
  1845. num_args += 1
  1846. if grant_read is not None:
  1847. headers = {http_headers.BOS_GRANT_READ: compat.convert_to_bytes(grant_read)}
  1848. num_args += 1
  1849. if grant_full_control is not None:
  1850. headers = {http_headers.BOS_GRANT_FULL_CONTROL: compat.convert_to_bytes(grant_full_control)}
  1851. num_args += 1
  1852. if num_args == 0:
  1853. raise ValueError("donn't give any object canned acl arguments!")
  1854. elif num_args >= 2:
  1855. raise ValueError("cann't get more than one object canned acl arguments!")
  1856. self._send_request(http_methods.PUT,
  1857. bucket_name,
  1858. key,
  1859. headers=headers,
  1860. params={b'acl': b''},
  1861. config=config)
  1862. @required(bucket_name=(bytes, str), key=(bytes, str))
  1863. def get_object_acl(self, bucket_name, key, config=None):
  1864. """
  1865. Get Access Control Level of object
  1866. :type bucket: string
  1867. :param bucket: None
  1868. :type key: string
  1869. :param key: None
  1870. :return:
  1871. **HttpResponse Class**
  1872. """
  1873. key = compat.convert_to_bytes(key)
  1874. return self._send_request(
  1875. http_methods.GET,
  1876. bucket_name,
  1877. key,
  1878. params={b'acl': b''},
  1879. config=config)
  1880. @required(bucket_name=(bytes, str), key=(bytes, str))
  1881. def delete_object_acl(self, bucket_name, key, config=None):
  1882. """
  1883. Get Access Control Level of object
  1884. :type bucket: string
  1885. :param bucket: None
  1886. :type key: string
  1887. :param key: None
  1888. :return:
  1889. **HttpResponse Class**
  1890. """
  1891. key = compat.convert_to_bytes(key)
  1892. return self._send_request(
  1893. http_methods.DELETE,
  1894. bucket_name,
  1895. key,
  1896. params={b'acl': b''},
  1897. config=config)
  1898. @required(bucket_name=(bytes, str), key=(bytes, str), url=(bytes, str))
  1899. def fetch_object(self, bucket_name, key, url,
  1900. fetch_mode=None,
  1901. storage_class=None,
  1902. config=None):
  1903. """
  1904. fetch object with given url and save to Baidu object storage
  1905. :type bucket: string
  1906. :param bucket: None
  1907. :type key: string
  1908. :param key: object name to be saved
  1909. :type url:string
  1910. :param url: url of resource to be fetched
  1911. :type fetch_mode:string
  1912. :param fetch_mode: fetch mode for get resource, valid value only is
  1913. 'sync' and 'async'
  1914. :return:
  1915. **HttpResponse Class**
  1916. """
  1917. key = compat.convert_to_bytes(key)
  1918. headers = {}
  1919. headers[http_headers.BOS_FETCH_SOURCE] = compat.convert_to_bytes(url)
  1920. if fetch_mode is not None:
  1921. headers[http_headers.BOS_FETCH_MODE] = fetch_mode
  1922. if storage_class is not None:
  1923. headers[http_headers.BOS_STORAGE_CLASS] = storage_class
  1924. return self._send_request(
  1925. http_methods.POST,
  1926. bucket_name,
  1927. key,
  1928. headers=headers,
  1929. params={b'fetch': b''},
  1930. config=config)
  1931. @required(bucket_name=(bytes, str), target_key=(bytes, str), symlink=(bytes, str), forbid_overwrite=(bool))
  1932. def put_object_symlink(self, bucket_name, target_key, symlink, forbid_overwrite=None,
  1933. user_metadata=None, storage_class=None, target_bucket=None, content_type=None, config=None):
  1934. """
  1935. put object symlink
  1936. :type bucket: string
  1937. :param bucket: None
  1938. :type key: string
  1939. :type key: object name
  1940. :type symlink: string
  1941. :type symlink_key: symlink name
  1942. :return:
  1943. **HttpResponse Class**
  1944. """
  1945. target_key = compat.convert_to_bytes(target_key)
  1946. symlink = compat.convert_to_bytes(symlink)
  1947. if content_type is None:
  1948. content_type = utils.guess_content_type_by_file_name(symlink)
  1949. headers = self._prepare_object_headers(user_metadata=user_metadata,
  1950. content_type=content_type,
  1951. storage_class=storage_class)
  1952. headers[http_headers.BOS_SYMLINK_TARGET] = target_key
  1953. if forbid_overwrite is not None:
  1954. if forbid_overwrite:
  1955. headers[http_headers.BOS_FORBID_OVERWRITE] = b'true'
  1956. else:
  1957. headers[http_headers.BOS_FORBID_OVERWRITE] = b'false'
  1958. if target_bucket is not None:
  1959. headers[http_headers.BOS_SYMLINK_BUCKET] = compat.convert_to_bytes(target_bucket)
  1960. return self._send_request(http_methods.PUT,
  1961. bucket_name,
  1962. symlink,
  1963. headers=headers,
  1964. params={b'symlink': b''},
  1965. config=config)
  1966. @required(bucket_name=(bytes, str), symlink=(bytes, str))
  1967. def get_object_symlink(self, bucket_name, symlink, config=None):
  1968. """
  1969. Get symlink info
  1970. :type bucket: string
  1971. :param bucket: None
  1972. :type symlink: string
  1973. :param symlink: symlink
  1974. :return:
  1975. **HttpResponse Class**
  1976. """
  1977. key = compat.convert_to_bytes(symlink)
  1978. return self._send_request(
  1979. http_methods.GET,
  1980. bucket_name,
  1981. key,
  1982. params={b'symlink': b''},
  1983. config=config)
  1984. @required(bucket_name=(bytes, str), key=(bytes, str), select_object_args=(dict, ))
  1985. def select_object(self, bucket_name, key, select_object_args, headers=None, config=None):
  1986. """
  1987. :type bucket_name: string
  1988. :param bucket_name: bucket name
  1989. :type key: string
  1990. :param key: object name
  1991. :type select_object_args: dict
  1992. :param select_object_args: requesta parameters for select object api
  1993. :param config:
  1994. :return:
  1995. """
  1996. key = compat.convert_to_bytes(key)
  1997. headers = headers or {}
  1998. if "inputSerialization" in select_object_args and "json" in select_object_args["inputSerialization"]:
  1999. select_type = b"json"
  2000. elif "inputSerialization" in select_object_args and "csv" in select_object_args["inputSerialization"]:
  2001. select_type = b"csv"
  2002. else:
  2003. select_type = b"parquet"
  2004. select_response = SelectResponse()
  2005. self._send_request(
  2006. http_methods.POST,
  2007. bucket_name,
  2008. key,
  2009. body=json.dumps({'selectRequest': select_object_args}, default=BosClient._dump_acl_object),
  2010. headers=headers,
  2011. params={b'select': b'', b'type': select_type},
  2012. config=config,
  2013. body_parser=lambda http_response, response: BosClient._parse_select_message(
  2014. http_response, response, select_response)
  2015. )
  2016. return select_response
  2017. def get_user_quota(self, config=None):
  2018. """
  2019. get user quota
  2020. :param config:
  2021. :return:
  2022. """
  2023. return self._send_request(
  2024. http_methods.GET, params={b'userQuota': b''}, config=config,)
  2025. @required(max_bucket_count=(int), max_capacity_mega_bytes=(int))
  2026. def put_user_quota(self, max_bucket_count, max_capacity_mega_bytes, config=None):
  2027. """
  2028. put user quota
  2029. :type max_bucket_count: int
  2030. :param max_bucket_count: max bucket count
  2031. :type max_capacity_mega_bytes: long
  2032. :param max_capacity_mega_bytes: max capacity mega bytes
  2033. :param config:
  2034. :return:
  2035. """
  2036. return self._send_request(
  2037. http_methods.PUT,
  2038. body=json.dumps({'maxBucketCount': max_bucket_count,
  2039. 'maxCapacityMegaBytes': max_capacity_mega_bytes}),
  2040. params={b'userQuota': b''}, config=config)
  2041. def delete_user_quota(self, config=None):
  2042. """
  2043. delete user quota
  2044. :param config:
  2045. :return:
  2046. """
  2047. return self._send_request(
  2048. http_methods.DELETE, params={b'userQuota': b''}, config=config)
  2049. @required(bucket_name=(bytes, str))
  2050. def get_notification(self, bucket_name, config=None):
  2051. """
  2052. get notification
  2053. :type bucket_name: string
  2054. :param bucket_name: bucket name
  2055. :param config:
  2056. :return:
  2057. """
  2058. return self._send_request(
  2059. http_methods.GET, bucket_name=bucket_name,
  2060. params={b'notification': b''}, config=config,)
  2061. @required(bucket_name=(bytes, str), notifications=(list, ))
  2062. def put_notification(self, bucket_name, notifications, config=None):
  2063. """
  2064. put user quota
  2065. :type bucket_name: string
  2066. :param bucket_name: bucket
  2067. :type notifications: list of dict
  2068. :param notifications: notifacation param
  2069. :param config:
  2070. :return:
  2071. """
  2072. return self._send_request(
  2073. http_methods.PUT, bucket_name=bucket_name,
  2074. body=json.dumps({'notifications': notifications}),
  2075. params={b'notification': b''}, config=config,)
  2076. @required(bucket_name=(bytes, str))
  2077. def delete_notification(self, bucket_name, config=None):
  2078. """
  2079. delete notification
  2080. :type bucket_name: string
  2081. :param bucket_name: bucket name
  2082. :param config:
  2083. :return:
  2084. """
  2085. return self._send_request(
  2086. http_methods.DELETE, bucket_name=bucket_name, params={b'notification': b''},
  2087. config=config,)
  2088. @required(bucket_name=(bytes, str), mirror_args=(list, ))
  2089. def put_bucket_mirroring(self, bucket_name, mirror_args, config=None):
  2090. """
  2091. put bucket mirroring
  2092. :type bucket_name: string
  2093. :param bucket_name: bucket name
  2094. :param mirror_args: mirror conf
  2095. :return:
  2096. """
  2097. return self._send_request(
  2098. http_methods.PUT,
  2099. bucket_name=bucket_name,
  2100. body=json.dumps({'bucketMirroringConfiguration': mirror_args}, default=BosClient._dump_acl_object),
  2101. params={b'mirroring': b''},
  2102. config=config,
  2103. )
  2104. @required(bucket_name=(bytes, str))
  2105. def get_bucket_mirroring(self, bucket_name, config=None):
  2106. """
  2107. get bucket mirroring
  2108. :type bucket_name: string
  2109. :param bucket_name: bucket name
  2110. :return:
  2111. """
  2112. return self._send_request(
  2113. http_methods.GET,
  2114. bucket_name=bucket_name,
  2115. params={b'mirroring': b''},
  2116. config=config,
  2117. )
  2118. @required(bucket_name=(bytes, str))
  2119. def delete_bucket_mirroring(self, bucket_name, config=None):
  2120. """
  2121. delete bucket mirroring
  2122. :type bucket_name: string
  2123. :param bucket_name: bucket name
  2124. :return:
  2125. """
  2126. return self._send_request(
  2127. http_methods.DELETE,
  2128. bucket_name=bucket_name,
  2129. params={b'mirroring': b''},
  2130. config=config,
  2131. )
  2132. def put_object_tagging(self, bucket_name, key, obj_tag_args, config=None):
  2133. """
  2134. put object tagging
  2135. :type bucket_name: string
  2136. :param bucket_name: bucket name
  2137. :type key: string
  2138. :param key: object name
  2139. :type obj_tag_args: dict
  2140. :param obj_tag_args: object tagging args
  2141. :return:
  2142. """
  2143. return self._send_request(
  2144. http_methods.PUT,
  2145. bucket_name=bucket_name,
  2146. key=key,
  2147. body=json.dumps(obj_tag_args, default=BosClient._dump_acl_object),
  2148. params={b'tagging': b''},
  2149. config=config,)
  2150. def put_object_tagging_canned(self, bucket_name, key, tag_header, config=None):
  2151. """
  2152. put object tagging
  2153. :type bucket_name: string
  2154. :param bucket_name: bucket name
  2155. :type key: string
  2156. :param key: object name
  2157. :type obj_tag_args: dict
  2158. :param obj_tag_args: object tagging args
  2159. :return:
  2160. """
  2161. headers = {}
  2162. headers[http_headers.BOS_TAGGING] = compat.convert_to_bytes(tag_header)
  2163. return self._send_request(
  2164. http_methods.PUT,
  2165. bucket_name=bucket_name,
  2166. key=key,
  2167. headers=headers,
  2168. params={b'tagging': b''},
  2169. config=config,)
  2170. def get_object_tagging(self, bucket_name, key, config=None):
  2171. """
  2172. put object tagging
  2173. :type bucket_name: string
  2174. :param bucket_name: bucket name
  2175. :type key: string
  2176. :param key: object name
  2177. :return:
  2178. """
  2179. return self._send_request(
  2180. http_methods.GET,
  2181. bucket_name=bucket_name,
  2182. key=key,
  2183. params={b'tagging': b''},
  2184. config=config,)
  2185. def put_bucket_versioning(self, bucket_name, status, config=None):
  2186. """
  2187. put bucket versioning
  2188. :type bucket_name: string
  2189. :param bucket_name: bucket name
  2190. :type status: string
  2191. :param key: version status:disable/enabled/suspended
  2192. :return:
  2193. """
  2194. return self._send_request(
  2195. http_methods.PUT,
  2196. bucket_name=bucket_name,
  2197. body=json.dumps({'status': status}, default=BosClient._dump_acl_object),
  2198. params={b'versioning': b''},
  2199. config=config,)
  2200. def get_bucket_versioning(self, bucket_name, config=None):
  2201. """
  2202. get bucket versioning
  2203. :type bucket_name: string
  2204. :param bucket_name: bucket name
  2205. :return:
  2206. """
  2207. return self._send_request(
  2208. http_methods.GET,
  2209. bucket_name=bucket_name,
  2210. params={b'versioning': b''},
  2211. config=config,)
  2212. @required(bucket_name=(bytes, str))
  2213. def list_objects_versions(self, bucket_name,
  2214. max_keys=1000, prefix=None, marker=None, version_marker=None,
  2215. delimiter=None, config=None):
  2216. """
  2217. Get Object Information of bucket
  2218. :type bucket: string
  2219. :param bucket: None
  2220. :type delimiter: string
  2221. :param delimiter: None
  2222. :type marker: string
  2223. :param marker: None
  2224. :type max_keys: int
  2225. :param max_keys: value <= 1000
  2226. :type prefix: string
  2227. :param prefix: None
  2228. :return:
  2229. **_ListObjectsResponse Class**
  2230. """
  2231. params = {b'versions': b''}
  2232. if max_keys is not None:
  2233. params[b'maxKeys'] = max_keys
  2234. if prefix is not None:
  2235. params[b'prefix'] = prefix
  2236. if marker is not None:
  2237. params[b'marker'] = marker
  2238. if delimiter is not None:
  2239. params[b'delimiter'] = delimiter
  2240. if version_marker is not None:
  2241. params[b'versionIdMarker'] = version_marker
  2242. return self._send_request(http_methods.GET, bucket_name, params=params, config=config)
  2243. @required(bucket_name=(bytes, str), cors_configuration=list)
  2244. def init_bucket_object_lock(self,
  2245. bucket_name,
  2246. retention_days,
  2247. config=None):
  2248. """
  2249. init bucket object lock
  2250. :type bucket: string
  2251. :param bucket: None
  2252. :type retention_days: int
  2253. :param retention_days: None
  2254. :return:**Http Response**
  2255. """
  2256. return self._send_request(http_methods.POST,
  2257. bucket_name,
  2258. params={b'objectlock': b''},
  2259. body=json.dumps({'retentionDays': retention_days}),
  2260. config=config)
  2261. @required(bucket_name=(bytes, str))
  2262. def get_bucket_object_lock(self, bucket_name, config=None):
  2263. """
  2264. get bucket object lock
  2265. :type bucket: string
  2266. :param bucket: None
  2267. :return:**Http Response**
  2268. """
  2269. return self._send_request(http_methods.GET,
  2270. bucket_name,
  2271. params={b'objectlock': b''},
  2272. config=config)
  2273. @required(bucket_name=(bytes, str))
  2274. def delete_bucket_object_lock(self, bucket_name, config=None):
  2275. """
  2276. Delete Bucket Object Lock
  2277. :type bucket: string
  2278. :param bucket: None
  2279. :return:**Http Response**
  2280. """
  2281. return self._send_request(http_methods.DELETE,
  2282. bucket_name,
  2283. params={b'objectlock': b''},
  2284. config=config)
  2285. @required(bucket_name=(bytes, str), cors_configuration=list)
  2286. def complete_bucket_object_lock(self,
  2287. bucket_name,
  2288. config=None):
  2289. """
  2290. complete bucket object lock
  2291. :type bucket: string
  2292. :param bucket: None
  2293. :type retention_days: int
  2294. :param retention_days: None
  2295. :return:**Http Response**
  2296. """
  2297. return self._send_request(http_methods.POST,
  2298. bucket_name,
  2299. params={b'completeobjectlock': b''},
  2300. config=config)
  2301. @required(bucket_name=(bytes, str), cors_configuration=list)
  2302. def extend_bucket_object_lock(self,
  2303. bucket_name,
  2304. extend_retent_days,
  2305. config=None):
  2306. """
  2307. extend bucket object lock
  2308. :type bucket: string
  2309. :param bucket: None
  2310. :type extend_retent_days: int
  2311. :param extend_retent_days: None
  2312. :return:**Http Response**
  2313. """
  2314. return self._send_request(http_methods.POST,
  2315. bucket_name,
  2316. params={b'extendobjectlock': b''},
  2317. body=json.dumps({'extendRetentionDays': extend_retent_days}),
  2318. config=config)
  2319. @required(bucket_name=(bytes, str), cors_configuration=list)
  2320. def get_bucket_quota(self,
  2321. bucket_name,
  2322. config=None):
  2323. """
  2324. get bucket quota
  2325. :type bucket: string
  2326. :param bucket: None
  2327. :return:**Http Response**
  2328. """
  2329. return self._send_request(http_methods.GET,
  2330. bucket_name,
  2331. params={b'quota': b''},
  2332. config=config)
  2333. @required(bucket_name=(bytes, str), cors_configuration=list)
  2334. def put_bucket_quota(self,
  2335. bucket_name,
  2336. quota_conf,
  2337. config=None):
  2338. """
  2339. put quota conf of bucket
  2340. :type bucket: string
  2341. :param bucket: None
  2342. :type retention_days: int
  2343. :param retention_days: None
  2344. :return:**Http Response**
  2345. """
  2346. return self._send_request(http_methods.PUT,
  2347. bucket_name,
  2348. body=json.dumps(quota_conf, default=BosClient._dump_acl_object),
  2349. params={b'quota': b''},
  2350. config=config)
  2351. @required(bucket_name=(bytes, str), cors_configuration=list)
  2352. def delete_bucket_quota(self,
  2353. bucket_name,
  2354. config=None):
  2355. """
  2356. get bucket quota
  2357. :type bucket: string
  2358. :param bucket: None
  2359. :return:**Http Response**
  2360. """
  2361. return self._send_request(http_methods.DELETE,
  2362. bucket_name,
  2363. params={b'quota': b''},
  2364. config=config)
  2365. @required(bucket_name=(bytes, str), cors_configuration=list)
  2366. def get_bucket_tagging(self,
  2367. bucket_name,
  2368. config=None):
  2369. """
  2370. get bucket tagging
  2371. :type bucket: string
  2372. :param bucket: None
  2373. :return:**Http Response**
  2374. """
  2375. return self._send_request(http_methods.GET,
  2376. bucket_name,
  2377. params={b'tagging': b''},
  2378. config=config)
  2379. @required(bucket_name=(bytes, str), cors_configuration=list)
  2380. def put_bucket_tagging(self,
  2381. bucket_name,
  2382. tag_conf,
  2383. config=None):
  2384. """
  2385. put tagging conf of bucket
  2386. :type bucket: string
  2387. :param bucket: None
  2388. :type retention_days: int
  2389. :param retention_days: None
  2390. :return:**Http Response**
  2391. """
  2392. return self._send_request(http_methods.PUT,
  2393. bucket_name,
  2394. body=json.dumps(tag_conf, default=BosClient._dump_acl_object),
  2395. params={b'tagging': b''},
  2396. config=config)
  2397. @required(bucket_name=(bytes, str), cors_configuration=list)
  2398. def delete_bucket_tagging(self,
  2399. bucket_name,
  2400. config=None):
  2401. """
  2402. delete bucket tagging
  2403. :type bucket: string
  2404. :param bucket: None
  2405. :return:**Http Response**
  2406. """
  2407. return self._send_request(http_methods.DELETE,
  2408. bucket_name,
  2409. params={b'tagging': b''},
  2410. config=config)
  2411. @staticmethod
  2412. def _prepare_object_headers(
  2413. content_length=None,
  2414. content_md5=None,
  2415. content_type=None,
  2416. content_sha256=None,
  2417. etag=None,
  2418. user_metadata=None,
  2419. storage_class=None,
  2420. user_headers=None,
  2421. encryption=None,
  2422. customer_key=None,
  2423. customer_key_md5=None,
  2424. traffic_limit=None,
  2425. object_tagging=None,):
  2426. headers = {}
  2427. if content_length is not None:
  2428. if content_length and content_length < 0:
  2429. raise ValueError('content_length should not be negative.')
  2430. headers[http_headers.CONTENT_LENGTH] = compat.convert_to_bytes(content_length)
  2431. if content_md5 is not None:
  2432. headers[http_headers.CONTENT_MD5] = utils.convert_to_standard_string(content_md5)
  2433. if content_type is not None:
  2434. headers[http_headers.CONTENT_TYPE] = utils.convert_to_standard_string(content_type)
  2435. else:
  2436. headers[http_headers.CONTENT_TYPE] = http_content_types.OCTET_STREAM
  2437. if content_sha256 is not None:
  2438. headers[http_headers.BCE_CONTENT_SHA256] = content_sha256
  2439. if etag is not None:
  2440. headers[http_headers.ETAG] = b'"%s"' % utils.convert_to_standard_string(etag)
  2441. if user_metadata is not None:
  2442. meta_size = 0
  2443. meta_data_set = set()
  2444. if not isinstance(user_metadata, dict):
  2445. raise TypeError('user_metadata should be of type dict.')
  2446. for k, v in iteritems(user_metadata):
  2447. meta_data_set.add(k.lower())
  2448. k = utils.convert_to_standard_string(k)
  2449. v = utils.convert_to_standard_string(v)
  2450. normalized_key = http_headers.BCE_USER_METADATA_PREFIX + k
  2451. headers[normalized_key] = v
  2452. meta_size += len(normalized_key)
  2453. meta_size += len(v)
  2454. if meta_size > bos.MAX_USER_METADATA_SIZE:
  2455. raise ValueError(
  2456. 'Metadata size should not be greater than %d.' % bos.MAX_USER_METADATA_SIZE)
  2457. if len(meta_data_set) != len(user_metadata):
  2458. raise ValueError('user_metadata has duplicate elements.')
  2459. if storage_class is not None:
  2460. headers[http_headers.BOS_STORAGE_CLASS] = storage_class
  2461. if encryption is not None:
  2462. headers[http_headers.BOS_SERVER_SIDE_ENCRYPTION] = utils.convert_to_standard_string(encryption)
  2463. if customer_key is not None:
  2464. headers[http_headers.BOS_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY] = \
  2465. utils.convert_to_standard_string(customer_key)
  2466. if customer_key_md5 is not None:
  2467. headers[http_headers.BOS_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5] = \
  2468. utils.convert_to_standard_string(customer_key_md5)
  2469. if user_headers is not None:
  2470. try:
  2471. headers = BosClient._get_user_header(headers, user_headers, False)
  2472. except Exception as e:
  2473. raise e
  2474. if traffic_limit is not None:
  2475. headers[http_headers.BOS_TRAFFIC_LIMIT] = traffic_limit
  2476. if object_tagging is not None:
  2477. headers[http_headers.BOS_TAGGING] = compat.convert_to_bytes(object_tagging)
  2478. return headers
  2479. @staticmethod
  2480. def _get_cond_read_write_headers(http_method, headers, cond_read_write):
  2481. """
  2482. get if condition headers
  2483. :type http_method: string
  2484. :param http_method: GET, HEAD, PUT, POST
  2485. :type cond_read_write: string
  2486. :param cond_read_write: put_object, complete_multipart_upload, get_object, get_object_meta_data
  2487. :return: headers
  2488. """
  2489. cond_read_write_set = http_headers.BOS_COND_READ_WRITE_HEADERS
  2490. if http_method == http_methods.GET or http_method == http_methods.HEAD:
  2491. cond_read_write_set = cond_read_write_set.union(set(
  2492. [http_headers.BOS_IF_MODIFIED_SINCE,
  2493. http_headers.BOS_IF_UNMODIFIED_SINCE,
  2494. http_headers.BOS_IF_MATCH,
  2495. http_headers.BOS_IF_NONE_MATCH]))
  2496. for k, v in iteritems(cond_read_write):
  2497. k = utils.convert_to_standard_string(k)
  2498. if k in cond_read_write_set:
  2499. headers[k] = v
  2500. else:
  2501. raise ValueError('%s is not valid in %s' % (k, http_method))
  2502. return headers
  2503. @staticmethod
  2504. def _get_user_header(headers, user_headers, is_copy=False):
  2505. if not isinstance(user_headers, dict):
  2506. raise TypeError('user_headers should be of type dict.')
  2507. bos_headers = http_headers.BOS_BASE_ALLOW_HEADERS
  2508. if not is_copy:
  2509. user_headers_set = bos_headers.union(set([http_headers.CACHE_CONTROL,
  2510. http_headers.CONTENT_ENCODING,
  2511. http_headers.CONTENT_DISPOSITION,
  2512. http_headers.EXPIRES,
  2513. http_headers.BOS_PROCESS]))
  2514. else:
  2515. user_headers_set = bos_headers.union(set([http_headers.BCE_COPY_SOURCE_IF_NONE_MATCH,
  2516. http_headers.BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE,
  2517. http_headers.BCE_COPY_SOURCE_IF_MODIFIED_SINCE]))
  2518. for k, v in iteritems(user_headers):
  2519. k = utils.convert_to_standard_string(k)
  2520. if k != http_headers.BOS_OBJECT_EXPIRES:
  2521. v = utils.convert_to_standard_string(v)
  2522. if k in user_headers_set:
  2523. headers[k] = v
  2524. return headers
  2525. def _get_config_parameter(self, config, attr):
  2526. result = None
  2527. if config is not None:
  2528. result = getattr(config, attr)
  2529. if result is not None:
  2530. return result
  2531. return getattr(self.config, attr)
  2532. @staticmethod
  2533. def _get_path(config, bucket_name=None, key=None, use_backup_endpoint=False):
  2534. host = config.endpoint
  2535. if use_backup_endpoint:
  2536. host = config.backup_endpoint
  2537. endpoint_protocol, host_name, endpoint_port = \
  2538. utils.parse_host_port(config.endpoint, config.protocol)
  2539. if config.cname_enabled or utils.is_cname_like_host(host_name) or utils.is_custom_host(host_name, bucket_name):
  2540. return utils.append_uri(bos.URL_PREFIX, key)
  2541. return utils.append_uri(bos.URL_PREFIX, bucket_name, key)
  2542. def _merge_config(self, config, bucket_name):
  2543. new_config = copy.copy(self.config)
  2544. if config is not None:
  2545. new_config.merge_non_none_values(config)
  2546. endpoint = self._change_user_endpoint(new_config, bucket_name)
  2547. new_config.endpoint = endpoint
  2548. return new_config
  2549. def _change_user_endpoint(self, config, bucket_name):
  2550. endpoint_protocol, user_host_name, endpoint_port = \
  2551. utils.parse_host_port(config.endpoint, config.protocol)
  2552. user_endpoint_split = compat.convert_to_bytes(user_host_name).split(b'.')
  2553. user_endpoint = config.endpoint
  2554. is_bos_path_style_host = utils.is_bos_suffixed_host(user_host_name) and len(user_endpoint_split) == 3
  2555. # 1. check ipv4 or path style
  2556. if utils.check_ipv4(user_host_name):
  2557. return config.endpoint
  2558. if config.path_style_enable:
  2559. return config.endpoint
  2560. # 2. check cname domain
  2561. if config.cname_enabled or utils.is_cname_like_host(user_host_name):
  2562. # cname domain
  2563. if is_bos_path_style_host:
  2564. raise ValueError(
  2565. 'endpoint is not cname domain, please set cname_enabled=False')
  2566. else:
  2567. return config.endpoint
  2568. # default use virtual-hosted endpoint
  2569. if bucket_name is not None:
  2570. if is_bos_path_style_host:
  2571. # split http head
  2572. if user_endpoint.startswith(HTTP_PROTOCOL_HEAD):
  2573. http_head_split = user_endpoint.split(b'//')
  2574. if len(http_head_split) < 2:
  2575. return config.endpoint
  2576. bucket_endpoint = http_head_split[0] + b'//' + compat.convert_to_bytes(bucket_name) +\
  2577. b'.' + http_head_split[1]
  2578. return compat.convert_to_bytes(bucket_endpoint)
  2579. else:
  2580. return compat.convert_to_bytes(bucket_name)+b'.'+\
  2581. compat.convert_to_bytes(user_endpoint)
  2582. # check virtual-hosted endpoint's bucket_name is not query bucket_name
  2583. if len(user_endpoint_split) == 4 and bucket_name is not None:
  2584. if user_endpoint_split[0] != compat.convert_to_bytes(bucket_name):
  2585. raise ValueError('your endpoint\'s bucket_name is not equal your query bucket_name!')
  2586. return config.endpoint
  2587. @staticmethod
  2588. def _need_retry_for_bos(config, error):
  2589. if not isinstance(error, BceServerError):
  2590. return False
  2591. # if you need add more retry condition, please add it here
  2592. if error.status_code == http.client.FORBIDDEN:
  2593. _logger.debug('BOS retry condition matched: 403 Forbidden')
  2594. return True
  2595. return False
  2596. @staticmethod
  2597. def _need_retry_backup_endpoint(error):
  2598. # always retry on IOError
  2599. if isinstance(error, IOError):
  2600. return True
  2601. # Only retry on a subset of service exceptions
  2602. if isinstance(error, BceServerError):
  2603. if error.status_code == http.client.INTERNAL_SERVER_ERROR:
  2604. return True
  2605. if error.status_code == http.client.SERVICE_UNAVAILABLE:
  2606. return True
  2607. if error.code == BceServerError.REQUEST_EXPIRED:
  2608. return True
  2609. if error.status_code == http.client.FORBIDDEN:
  2610. return True
  2611. return False
  2612. def _send_request(
  2613. self, http_method, bucket_name=None, key=None,
  2614. body=None, headers=None, params=None,
  2615. config=None,
  2616. body_parser=None):
  2617. config = self._merge_config(config, bucket_name)
  2618. path = BosClient._get_path(config, bucket_name, key)
  2619. if body_parser is None:
  2620. body_parser = handler.parse_json
  2621. if config.security_token is not None:
  2622. headers = headers or {}
  2623. headers[http_headers.STS_SECURITY_TOKEN] = config.security_token
  2624. last_exception = None
  2625. e = None
  2626. try:
  2627. return bce_http_client.send_request(
  2628. config, bce_v1_signer.sign, [handler.parse_error, body_parser],
  2629. http_method, path, body, headers, params)
  2630. except BceHttpClientError as ex:
  2631. last_exception = ex
  2632. e = ex
  2633. # retry backup endpoint
  2634. if e is not None and config.backup_endpoint is not None and BosClient._need_retry_backup_endpoint(e):
  2635. try:
  2636. _logger.debug(b'Retry for backup endpoint error code: %d.', e.status_code)
  2637. path = BosClient._get_path(config, bucket_name, key, True)
  2638. return bce_http_client.send_request(
  2639. config, bce_v1_signer.sign, [handler.parse_error, body_parser],
  2640. http_method, path, body, headers, params, True)
  2641. except BceHttpClientError as ex:
  2642. last_exception = ex
  2643. e = ex
  2644. # retry for bos error
  2645. if e is not None and BosClient._need_retry_for_bos(config, e.last_error):
  2646. try:
  2647. _logger.debug(b'Retry for BOS error code: %d.', e.status_code)
  2648. return bce_http_client.send_request(
  2649. config, bce_v1_signer.sign, [handler.parse_error, body_parser],
  2650. http_method, path, body, headers, params)
  2651. except BceHttpClientError as ex:
  2652. last_exception = ex
  2653. e = ex
  2654. if last_exception is None:
  2655. raise
  2656. raise last_exception
  2657. class SelectMessage(object):
  2658. """
  2659. returned message from select object api
  2660. """
  2661. def set_record_message(self, headers, payload, crc):
  2662. """
  2663. Initialize for record message
  2664. """
  2665. self.type = "Records"
  2666. self.headers = headers
  2667. self.payload = payload
  2668. self.crc = crc
  2669. def set_cont_message(self, headers, bytes_scanned, bytes_returned, crc):
  2670. """
  2671. Initialize for continue message
  2672. """
  2673. self.type = "Cont"
  2674. self.headers = headers
  2675. self.bytes_scanned = bytes_scanned
  2676. self.bytes_returned = bytes_returned
  2677. self.crc = crc
  2678. def set_end_message(self, headers, crc):
  2679. """
  2680. Initialize for end message
  2681. """
  2682. self.type = "End"
  2683. self.headers = headers
  2684. self.crc = crc
  2685. def __str__(self):
  2686. if self.type == "Records":
  2687. return '{}\n{}'.format(self.headers, self.payload)
  2688. elif self.type == "Cont":
  2689. return '{}\nbytes_scanned/bytes_returned={}/{}'.format(self.headers, self.bytes_scanned,
  2690. self.bytes_returned)
  2691. else:
  2692. return '{}'.format(self.headers)
  2693. class SelectResponse(object):
  2694. """
  2695. deal with message of select object api
  2696. """
  2697. def __init__(self):
  2698. self.finish = False
  2699. def init_from_http_response(self, http_response, response):
  2700. """
  2701. get HttpResponse and BceResponse
  2702. """
  2703. self.http_response = http_response
  2704. self.response = response
  2705. def result(self):
  2706. """
  2707. generator for SelectMessage
  2708. """
  2709. f = self.http_response
  2710. try:
  2711. while not self.finish:
  2712. prelude = f.read(8)
  2713. if not prelude:
  2714. raise StopIteration
  2715. return
  2716. total_len = struct.unpack('>I', prelude[0:4])[0]
  2717. headers_len = struct.unpack('>I', prelude[4:8])[0]
  2718. headers = f.read(headers_len)
  2719. headers_map = self._parse_select_headers(headers)
  2720. msg = SelectMessage()
  2721. if headers_map['message-type'] == 'Records':
  2722. payload_len = total_len - headers_len - 12
  2723. payload = f.read(payload_len)
  2724. crc = struct.unpack('>I', f.read(4))[0]
  2725. msg.set_record_message(headers_map, compat.convert_to_string(payload), crc)
  2726. yield msg
  2727. elif headers_map['message-type'] == 'Cont':
  2728. bytes_scanned = f.read(8)
  2729. bytes_returned = f.read(8)
  2730. crc = struct.unpack('>I', f.read(4))[0]
  2731. bytes_scanned = struct.unpack('>Q', bytes_scanned)[0]
  2732. bytes_returned = struct.unpack('>Q', bytes_returned)[0]
  2733. msg.set_cont_message(headers_map, bytes_scanned, bytes_returned, crc)
  2734. yield msg
  2735. elif headers_map['message-type'] == 'End':
  2736. crc = struct.unpack('>I', f.read(4))[0]
  2737. if headers_map["error-code"] != "success":
  2738. raise BceServerError(headers_map['error-message'], code=headers_map['error-code'],
  2739. request_id=self.response.metadata.bce_request_id)
  2740. return
  2741. msg.set_end_message(headers_map, crc)
  2742. self.finish = True
  2743. yield msg
  2744. return
  2745. finally:
  2746. self.http_response.close()
  2747. @staticmethod
  2748. def _parse_select_headers(headers):
  2749. """
  2750. parse SELECT headers
  2751. :param headers: <str>
  2752. :return: <dict>
  2753. """
  2754. hm = {}
  2755. index = 0
  2756. while index < len(headers):
  2757. # headers key length
  2758. key_len = struct.unpack('B', headers[index: index + 1])[0]
  2759. index += 1
  2760. # headers key
  2761. key = headers[index: index + key_len]
  2762. index += key_len
  2763. # headers value length
  2764. value_len = struct.unpack('>H', headers[index: index + 2])[0]
  2765. index += 2
  2766. # headers value
  2767. value = headers[index: index + value_len]
  2768. index += value_len
  2769. hm[compat.convert_to_string(key)] = compat.convert_to_string(value)
  2770. return hm