api.py 127 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622
  1. # -*- coding: utf-8 -*-
  2. """
  3. 文件上传方法中的data参数
  4. ------------------------
  5. 诸如 :func:`put_object <Bucket.put_object>` 这样的上传接口都会有 `data` 参数用于接收用户数据。`data` 可以是下述类型
  6. - unicode类型(对于Python3则是str类型):内部会自动转换为UTF-8的bytes
  7. - bytes类型:不做任何转换
  8. - file-like object:对于可以seek和tell的file object,从当前位置读取直到结束。其他类型,请确保当前位置是文件开始。
  9. - 可迭代类型:对于无法探知长度的数据,要求一定是可迭代的。此时会通过Chunked Encoding传输。
  10. Bucket配置修改方法中的input参数
  11. -----------------------------
  12. 诸如 :func:`put_bucket_cors <Bucket.put_bucket_cors>` 这样的Bucket配置修改接口都会有 `input` 参数接收用户提供的配置数据。
  13. `input` 可以是下述类型
  14. - Bucket配置信息相关的类,如 `BucketCors`
  15. - unicode类型(对于Python3则是str类型)
  16. - 经过utf-8编码的bytes类型
  17. - file-like object
  18. - 可迭代类型,会通过Chunked Encoding传输
  19. 也就是说 `input` 参数可以比 `data` 参数多接受第一种类型的输入。
  20. 返回值
  21. ------
  22. :class:`Service` 和 :class:`Bucket` 类的大多数方法都是返回 :class:`RequestResult <oss2.models.RequestResult>`
  23. 及其子类。`RequestResult` 包含了HTTP响应的状态码、头部以及OSS Request ID,而它的子类则包含用户真正想要的结果。例如,
  24. `ListBucketsResult.buckets` 就是返回的Bucket信息列表;`GetObjectResult` 则是一个file-like object,可以调用 `read()` 来获取响应的
  25. HTTP包体。
  26. :class:`CryptoBucket`:
  27. 加密接口
  28. -------
  29. CryptoBucket仅提供上传下载加密数据的接口,诸如`get_object` 、 `put_object` ,返回值与Bucket相应接口一致。
  30. 异常
  31. ----
  32. 一般来说Python SDK可能会抛出三种类型的异常,这些异常都继承于 :class:`OssError <oss2.exceptions.OssError>` :
  33. - :class:`ClientError <oss2.exceptions.ClientError>` :由于用户参数错误而引发的异常;
  34. - :class:`ServerError <oss2.exceptions.ServerError>` 及其子类:OSS服务器返回非成功的状态码,如4xx或5xx;
  35. - :class:`RequestError <oss2.exceptions.RequestError>` :底层requests库抛出的异常,如DNS解析错误,超时等;
  36. 当然,`Bucket.put_object_from_file` 和 `Bucket.get_object_to_file` 这类函数还会抛出文件相关的异常。
  37. .. _byte_range:
  38. 指定下载范围
  39. ------------
  40. 诸如 :func:`get_object <Bucket.get_object>` 以及 :func:`upload_part_copy <Bucket.upload_part_copy>` 这样的函数,可以接受
  41. `byte_range` 参数,表明读取数据的范围。该参数是一个二元tuple:(start, last)。这些接口会把它转换为Range头部的值,如:
  42. - byte_range 为 (0, 99) 转换为 'bytes=0-99',表示读取前100个字节
  43. - byte_range 为 (None, 99) 转换为 'bytes=-99',表示读取最后99个字节
  44. - byte_range 为 (100, None) 转换为 'bytes=100-',表示读取第101个字节到文件结尾的部分(包含第101个字节)
  45. 分页罗列
  46. -------
  47. 罗列各种资源的接口,如 :func:`list_buckets <Service.list_buckets>` 、 :func:`list_objects <Bucket.list_objects>` 都支持
  48. 分页查询。通过设定分页标记(如:`marker` 、 `key_marker` )的方式可以指定查询某一页。首次调用将分页标记设为空(缺省值,可以不设),
  49. 后续的调用使用返回值中的 `next_marker` 、 `next_key_marker` 等。每次调用后检查返回值中的 `is_truncated` ,其值为 `False` 说明
  50. 已经到了最后一页。
  51. .. _line_range:
  52. 指定查询CSV文件范围
  53. ------------
  54. 诸如 :func:`select_object <Bucket.select_object>` 以及 :func:`select_object_to_file <Bucket.select_object_to_file>` 这样的函数的select_csv_params参数,可以接受
  55. `LineRange` 参数,表明读取CSV数据的范围。该参数是一个二元tuple:(start, last):
  56. - LineRange 为 (0, 99) 表示读取前100行
  57. - LineRange 为 (None, 99) 表示读取最后99行
  58. - LineRange 为 (100, None) 表示读取第101行到文件结尾的部分(包含第101行)
  59. .. _split_range:
  60. 指定查询CSV文件范围
  61. ------------
  62. split可以认为是切分好的大小大致相等的csv行簇。每个Split大小大致相等,这样以便更好的做到负载均衡。
  63. 诸如 :func:`select_object <Bucket.select_object>` 以及 :func:`select_object_to_file <Bucket.select_object_to_file>` 这样的函数的select_csv_params参数,可以接受
  64. `SplitRange` 参数,表明读取CSV数据的范围。该参数是一个二元tuple:(start, last):
  65. - SplitRange 为 (0, 9) 表示读取前10个Split
  66. - SplitRange 为 (None, 9) 表示读取最后9个split
  67. - SplitRange 为 (10, None) 表示读取第11个split到文件结尾的部分(包含第11个Split)
  68. 分页查询
  69. -------
  70. 和create_csv_object_meta配合使用,有两种方法:
  71. - 方法1:先获取文件总的行数(create_csv_object_meta返回),然后把文件以line_range分成若干部分并行查询
  72. - 方法2:先获取文件总的Split数(create_csv_object_meta返回), 然后把文件分成若干个请求,每个请求含有大致相等的Split
  73. .. _progress_callback:
  74. 上传下载进度
  75. -----------
  76. 上传下载接口,诸如 `get_object` 、 `put_object` 、`resumable_upload`,都支持进度回调函数,可以用它实现进度条等功能。
  77. `progress_callback` 的函数原型如下 ::
  78. def progress_callback(bytes_consumed, total_bytes):
  79. '''进度回调函数。
  80. :param int bytes_consumed: 已经消费的字节数。对于上传,就是已经上传的量;对于下载,就是已经下载的量。
  81. :param int total_bytes: 总长度。
  82. '''
  83. 其中 `total_bytes` 对于上传和下载有不同的含义:
  84. - 上传:当输入是bytes或可以seek/tell的文件对象,那么它的值就是总的字节数;否则,其值为None
  85. - 下载:当返回的HTTP相应中有Content-Length头部,那么它的值就是Content-Length的值;否则,其值为None
  86. .. _unix_time:
  87. Unix Time
  88. ---------
  89. OSS Python SDK会把从服务器获得时间戳都转换为自1970年1月1日UTC零点以来的秒数,即Unix Time。
  90. 参见 `Unix Time <https://en.wikipedia.org/wiki/Unix_time>`_
  91. OSS中常用的时间格式有
  92. - HTTP Date格式,形如 `Sat, 05 Dec 2015 11:04:39 GMT` 这样的GMT时间。
  93. 用在If-Modified-Since、Last-Modified这些HTTP请求、响应头里。
  94. - ISO8601格式,形如 `2015-12-05T00:00:00.000Z`。
  95. 用在生命周期管理配置、列举Bucket结果中的创建时间、列举文件结果中的最后修改时间等处。
  96. `http_date` 函数把Unix Time转换为HTTP Date;而 `http_to_unixtime` 则做相反的转换。如 ::
  97. >>> import oss2, time
  98. >>> unix_time = int(time.time()) # 当前UNIX Time,设其值为 1449313829
  99. >>> date_str = oss2.http_date(unix_time) # 得到 'Sat, 05 Dec 2015 11:10:29 GMT'
  100. >>> oss2.http_to_unixtime(date_str) # 得到 1449313829
  101. .. note::
  102. 生成HTTP协议所需的日期(即HTTP Date)时,请使用 `http_date` , 不要使用 `strftime` 这样的函数。因为后者是和locale相关的。
  103. 比如,`strftime` 结果中可能会出现中文,而这样的格式,OSS服务器是不能识别的。
  104. `iso8601_to_unixtime` 把ISO8601格式转换为Unix Time;`date_to_iso8601` 和 `iso8601_to_date` 则在ISO8601格式的字符串和
  105. datetime.date之间相互转换。如 ::
  106. >>> import oss2
  107. >>> d = oss2.iso8601_to_date('2015-12-05T00:00:00.000Z') # 得到 datetime.date(2015, 12, 5)
  108. >>> date_str = oss2.date_to_iso8601(d) # 得到 '2015-12-05T00:00:00.000Z'
  109. >>> oss2.iso8601_to_unixtime(date_str) # 得到 1449273600
  110. .. _select_params:
  111. 指定OSS Select的文件格式。
  112. 对于Csv文件,支持如下Keys:
  113. >>> CsvHeaderInfo: None|Use|Ignore #None表示没有CSV Schema头,Use表示启用CSV Schema头,可以在Select语句中使用Name,Ignore表示有CSV Schema头,但忽略它(Select语句中不可以使用Name)
  114. 默认值是None
  115. >>> CommentCharacter: Comment字符,默认值是#,不支持多个字符
  116. >>> RecordDelimiter: 行分隔符,默认是\n,最多支持两个字符分隔符(比如:\r\n)
  117. >>> FieldDelimiter: 列分隔符,默认是逗号(,), 不支持多个字符
  118. >>> QuoteCharacter: 列Quote字符,默认是双引号("),不支持多个字符。注意转义符合Quote字符相同。
  119. >>> LineRange: 指定查询CSV文件的行范围,参见 `line_range`。
  120. >>> SplitRange: 指定查询CSV文件的Split范围,参见 `split_range`.
  121. 注意LineRange和SplitRange两种不能同时指定。若同时指定LineRange会被忽略。
  122. >>> CompressionType: 文件的压缩格式,默认值是None, 支持GZIP。
  123. >>> OutputRawData: 指定是响应Body返回Raw数据,默认值是False.
  124. >>> SkipPartialDataRecord: 当CSV行数据不完整时(select语句中出现的列在该行为空),是否跳过该行。默认是False。
  125. >>> OutputHeader:是否输出CSV Header,默认是False.
  126. >>> EnablePayloadCrc:是否启用对Payload的CRC校验,默认是False. 该选项不能和OutputRawData:True混用。
  127. >>> MaxSkippedRecordsAllowed: 允许跳过的最大行数。默认值是0表示一旦有一行跳过就报错。当下列两种情况下该行CSV被跳过:1)当SkipPartialDataRecord为True时且该行不完整时 2)当该行的数据类型和SQL不匹配时
  128. 对于Json 文件, 支持如下Keys:
  129. >>> Json_Type: DOCUMENT | LINES . DOCUMENT就是指一般的Json文件,LINES是指每一行是一个合法的JSON对象,文件由多行Json对象组成,整个文件本身不是合法的Json对象。
  130. >>> LineRange: 指定查询JSON LINE文件的行范围,参见 `line_range`。注意该参数仅支持LINES类型
  131. >>> SplitRange: 指定查询JSON LINE文件的Split范围,参见 `split_range`.注意该参数仅支持LINES类型
  132. >>> CompressionType: 文件的压缩格式,默认值是None, 支持GZIP。
  133. >>> OutputRawData: 指定是响应Body返回Raw数据,默认值是False.
  134. >>> SkipPartialDataRecord: 当一条JSON记录数据不完整时(select语句中出现的Key在该对象为空),是否跳过该Json记录。默认是False。
  135. >>> EnablePayloadCrc:是否启用对Payload的CRC校验,默认是False. 该选项不能和OutputRawData:True混用。
  136. >>> MaxSkippedRecordsAllowed: 允许跳过的最大Json记录数。默认值是0表示一旦有一条Json记录跳过就报错。当下列两种情况下该JSON被跳过:1)当SkipPartialDataRecord为True时且该条Json记录不完整时 2)当该记录的数据类型和SQL不匹配时
  137. >>> ParseJsonNumberAsString: 将Json文件中的数字解析成字符串。使用场景是当Json文件中的浮点数精度较高时,系统默认的浮点数精度无法达到要求,当解析成字符串时将完整保留原始数据精度,在Sql中使用Cast可以将字符串无精度损失地转成decimal.
  138. >>> AllowQuotedRecordDelimiter: 允许CSV中的列包含转义过的换行符。默认为true。当值为False时,select API可以用Range:bytes来设置选取目标对象内容的范围
  139. .. _select_meta_params:
  140. create_select_object_meta参数集合,支持如下Keys:
  141. - RecordDelimiter: CSV换行符,最多支持两个字符
  142. - FieldDelimiter: CSV列分隔符,最多支持一个字符
  143. - QuoteCharacter: CSV转移Quote符,最多支持一个字符
  144. - OverwriteIfExists: true|false. true表示重新获得csv meta,并覆盖原有的meta。一般情况下不需要使用
  145. """
  146. import logging
  147. from . import xml_utils
  148. from . import http
  149. from . import utils
  150. from . import exceptions
  151. from . import defaults
  152. from . import models
  153. from . import select_params
  154. from .models import *
  155. from .compat import urlquote, urlparse, to_unicode, to_string
  156. from .headers import *
  157. from .select_params import *
  158. import time
  159. import shutil
  160. logger = logging.getLogger(__name__)
  161. class _Base(object):
  162. def __init__(self, auth, endpoint, is_cname, session, connect_timeout,
  163. app_name='', enable_crc=True, proxies=None):
  164. self.auth = auth
  165. self.endpoint = _normalize_endpoint(endpoint.strip())
  166. if utils.is_valid_endpoint(self.endpoint) is not True:
  167. raise ClientError('The endpoint you has specified is not valid, endpoint: {0}'.format(endpoint))
  168. self.session = session or http.Session()
  169. self.timeout = defaults.get(connect_timeout, defaults.connect_timeout)
  170. self.app_name = app_name
  171. self.enable_crc = enable_crc
  172. self.proxies = proxies
  173. self._make_url = _UrlMaker(self.endpoint, is_cname)
  174. def _do(self, method, bucket_name, key, **kwargs):
  175. key = to_string(key)
  176. req = http.Request(method, self._make_url(bucket_name, key),
  177. app_name=self.app_name,
  178. proxies=self.proxies,
  179. **kwargs)
  180. self.auth._sign_request(req, bucket_name, key)
  181. resp = self.session.do_request(req, timeout=self.timeout)
  182. if resp.status // 100 != 2:
  183. e = exceptions.make_exception(resp)
  184. logger.info("Exception: {0}".format(e))
  185. raise e
  186. # Note that connections are only released back to the pool for reuse once all body data has been read;
  187. # be sure to either set stream to False or read the content property of the Response object.
  188. # For more details, please refer to http://docs.python-requests.org/en/master/user/advanced/#keep-alive.
  189. content_length = models._hget(resp.headers, 'content-length', int)
  190. if content_length is not None and content_length == 0:
  191. resp.read()
  192. return resp
  193. def _do_url(self, method, sign_url, **kwargs):
  194. req = http.Request(method, sign_url, app_name=self.app_name, proxies=self.proxies, **kwargs)
  195. resp = self.session.do_request(req, timeout=self.timeout)
  196. if resp.status // 100 != 2:
  197. e = exceptions.make_exception(resp)
  198. logger.info("Exception: {0}".format(e))
  199. raise e
  200. # Note that connections are only released back to the pool for reuse once all body data has been read;
  201. # be sure to either set stream to False or read the content property of the Response object.
  202. # For more details, please refer to http://docs.python-requests.org/en/master/user/advanced/#keep-alive.
  203. content_length = models._hget(resp.headers, 'content-length', int)
  204. if content_length is not None and content_length == 0:
  205. resp.read()
  206. return resp
  207. @staticmethod
  208. def _parse_result(resp, parse_func, klass):
  209. result = klass(resp)
  210. parse_func(result, resp.read())
  211. return result
  212. class Service(_Base):
  213. """用于Service操作的类,如罗列用户所有的Bucket。
  214. 用法 ::
  215. >>> import oss2
  216. >>> auth = oss2.Auth('your-access-key-id', 'your-access-key-secret')
  217. >>> service = oss2.Service(auth, 'oss-cn-hangzhou.aliyuncs.com')
  218. >>> service.list_buckets()
  219. <oss2.models.ListBucketsResult object at 0x0299FAB0>
  220. :param auth: 包含了用户认证信息的Auth对象
  221. :type auth: oss2.Auth
  222. :param str endpoint: 访问域名,如杭州区域的域名为oss-cn-hangzhou.aliyuncs.com
  223. :param session: 会话。如果是None表示新开会话,非None则复用传入的会话
  224. :type session: oss2.Session
  225. :param float connect_timeout: 连接超时时间,以秒为单位。
  226. :param str app_name: 应用名。该参数不为空,则在User Agent中加入其值。
  227. 注意到,最终这个字符串是要作为HTTP Header的值传输的,所以必须要遵循HTTP标准。
  228. """
  229. QOS_INFO = 'qosInfo'
  230. def __init__(self, auth, endpoint,
  231. session=None,
  232. connect_timeout=None,
  233. app_name='',
  234. proxies=None):
  235. logger.debug("Init oss service, endpoint: {0}, connect_timeout: {1}, app_name: {2}, proxies: {3}".format(
  236. endpoint, connect_timeout, app_name, proxies))
  237. super(Service, self).__init__(auth, endpoint, False, session, connect_timeout,
  238. app_name=app_name, proxies=proxies)
  239. def list_buckets(self, prefix='', marker='', max_keys=100, params=None):
  240. """根据前缀罗列用户的Bucket。
  241. :param str prefix: 只罗列Bucket名为该前缀的Bucket,空串表示罗列所有的Bucket
  242. :param str marker: 分页标志。首次调用传空串,后续使用返回值中的next_marker
  243. :param int max_keys: 每次调用最多返回的Bucket数目
  244. :param dict params: list操作参数,传入'tag-key','tag-value'对结果进行过滤
  245. :return: 罗列的结果
  246. :rtype: oss2.models.ListBucketsResult
  247. """
  248. logger.debug("Start to list buckets, prefix: {0}, marker: {1}, max-keys: {2}".format(prefix, marker, max_keys))
  249. listParam = {}
  250. listParam['prefix'] = prefix
  251. listParam['marker'] = marker
  252. listParam['max-keys'] = str(max_keys)
  253. if params is not None:
  254. if 'tag-key' in params:
  255. listParam['tag-key'] = params['tag-key']
  256. if 'tag-value' in params:
  257. listParam['tag-value'] = params['tag-value']
  258. resp = self._do('GET', '', '', params=listParam)
  259. logger.debug("List buckets done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  260. return self._parse_result(resp, xml_utils.parse_list_buckets, ListBucketsResult)
  261. def get_user_qos_info(self):
  262. """获取User的QoSInfo
  263. :return: :class:`GetUserQosInfoResult <oss2.models.GetUserQosInfoResult>`
  264. """
  265. logger.debug("Start to get user qos info.")
  266. resp = self._do('GET', '', '', params={Service.QOS_INFO: ''})
  267. logger.debug("get use qos, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  268. return self._parse_result(resp, xml_utils.parse_get_qos_info, GetUserQosInfoResult)
  269. class Bucket(_Base):
  270. """用于Bucket和Object操作的类,诸如创建、删除Bucket,上传、下载Object等。
  271. 用法(假设Bucket属于杭州区域) ::
  272. >>> import oss2
  273. >>> auth = oss2.Auth('your-access-key-id', 'your-access-key-secret')
  274. >>> bucket = oss2.Bucket(auth, 'http://oss-cn-hangzhou.aliyuncs.com', 'your-bucket')
  275. >>> bucket.put_object('readme.txt', 'content of the object')
  276. <oss2.models.PutObjectResult object at 0x029B9930>
  277. :param auth: 包含了用户认证信息的Auth对象
  278. :type auth: oss2.Auth
  279. :param str endpoint: 访问域名或者CNAME
  280. :param str bucket_name: Bucket名
  281. :param bool is_cname: 如果endpoint是CNAME则设为True;反之,则为False。
  282. :param session: 会话。如果是None表示新开会话,非None则复用传入的会话
  283. :type session: oss2.Session
  284. :param float connect_timeout: 连接超时时间,以秒为单位。
  285. :param str app_name: 应用名。该参数不为空,则在User Agent中加入其值。
  286. 注意到,最终这个字符串是要作为HTTP Header的值传输的,所以必须要遵循HTTP标准。
  287. """
  288. ACL = 'acl'
  289. CORS = 'cors'
  290. LIFECYCLE = 'lifecycle'
  291. LOCATION = 'location'
  292. LOGGING = 'logging'
  293. REFERER = 'referer'
  294. WEBSITE = 'website'
  295. LIVE = 'live'
  296. COMP = 'comp'
  297. STATUS = 'status'
  298. VOD = 'vod'
  299. SYMLINK = 'symlink'
  300. STAT = 'stat'
  301. BUCKET_INFO = 'bucketInfo'
  302. PROCESS = 'x-oss-process'
  303. TAGGING = 'tagging'
  304. ENCRYPTION = 'encryption'
  305. VERSIONS = 'versions'
  306. VERSIONING = 'versioning'
  307. VERSIONID = 'versionId'
  308. RESTORE = 'restore'
  309. OBJECTMETA = 'objectMeta'
  310. POLICY = 'policy'
  311. REQUESTPAYMENT = 'requestPayment'
  312. QOS_INFO = 'qosInfo'
  313. USER_QOS = 'qos'
  314. ASYNC_FETCH = 'asyncFetch'
  315. SEQUENTIAL = 'sequential'
  316. INVENTORY = "inventory"
  317. INVENTORY_CONFIG_ID = "inventoryId"
  318. CONTINUATION_TOKEN = "continuation-token"
  319. WORM = "worm"
  320. WORM_ID = "wormId"
  321. WORM_EXTEND = "wormExtend"
  322. REPLICATION = "replication"
  323. REPLICATION_LOCATION = 'replicationLocation'
  324. REPLICATION_PROGRESS = 'replicationProgress'
  325. TRANSFER_ACCELERATION = 'transferAcceleration'
  326. def __init__(self, auth, endpoint, bucket_name,
  327. is_cname=False,
  328. session=None,
  329. connect_timeout=None,
  330. app_name='',
  331. enable_crc=True,
  332. proxies=None):
  333. logger.debug("Init Bucket: {0}, endpoint: {1}, isCname: {2}, connect_timeout: {3}, app_name: {4}, enabled_crc: {5}"
  334. ", proxies: {6}".format(bucket_name, endpoint, is_cname, connect_timeout, app_name, enable_crc, proxies))
  335. super(Bucket, self).__init__(auth, endpoint, is_cname, session, connect_timeout,
  336. app_name=app_name, enable_crc=enable_crc, proxies=proxies)
  337. self.bucket_name = bucket_name.strip()
  338. if utils.is_valid_bucket_name(self.bucket_name) is not True:
  339. raise ClientError("The bucket_name is invalid, please check it.")
  340. def sign_url(self, method, key, expires, headers=None, params=None, slash_safe=False):
  341. """生成签名URL。
  342. 常见的用法是生成加签的URL以供授信用户下载,如为log.jpg生成一个5分钟后过期的下载链接::
  343. >>> bucket.sign_url('GET', 'log.jpg', 5 * 60)
  344. r'http://your-bucket.oss-cn-hangzhou.aliyuncs.com/logo.jpg?OSSAccessKeyId=YourAccessKeyId\&Expires=1447178011&Signature=UJfeJgvcypWq6Q%2Bm3IJcSHbvSak%3D'
  345. :param method: HTTP方法,如'GET'、'PUT'、'DELETE'等
  346. :type method: str
  347. :param key: 文件名
  348. :param expires: 过期时间(单位:秒),链接在当前时间再过expires秒后过期
  349. :param headers: 需要签名的HTTP头部,如名称以x-oss-meta-开头的头部(作为用户自定义元数据)、
  350. Content-Type头部等。对于下载,不需要填。
  351. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  352. :param params: 需要签名的HTTP查询参数
  353. :param slash_safe: 是否开启key名称中的‘/’转义保护,如果不开启'/'将会转义成%2F
  354. :type slash_safe: bool
  355. :return: 签名URL。
  356. """
  357. key = to_string(key)
  358. logger.debug(
  359. "Start to sign_url, method: {0}, bucket: {1}, key: {2}, expires: {3}, headers: {4}, params: {5}, slash_safe: {6}".format(
  360. method, self.bucket_name, to_string(key), expires, headers, params, slash_safe))
  361. req = http.Request(method, self._make_url(self.bucket_name, key, slash_safe),
  362. headers=headers,
  363. params=params)
  364. return self.auth._sign_url(req, self.bucket_name, key, expires)
  365. def sign_rtmp_url(self, channel_name, playlist_name, expires):
  366. """生成RTMP推流的签名URL。
  367. 常见的用法是生成加签的URL以供授信用户向OSS推RTMP流。
  368. :param channel_name: 直播频道的名称
  369. :param expires: 过期时间(单位:秒),链接在当前时间再过expires秒后过期
  370. :param playlist_name: 播放列表名称,注意与创建live channel时一致
  371. :param params: 需要签名的HTTP查询参数
  372. :return: 签名URL。
  373. """
  374. logger.debug("Sign RTMP url, bucket: {0}, channel_name: {1}, playlist_name: {2}, expires: {3}".format(
  375. self.bucket_name, channel_name, playlist_name, expires))
  376. url = self._make_url(self.bucket_name, 'live').replace('http://', 'rtmp://').replace(
  377. 'https://', 'rtmp://') + '/' + channel_name
  378. params = {}
  379. if playlist_name is not None and playlist_name != "":
  380. params['playlistName'] = playlist_name
  381. return self.auth._sign_rtmp_url(url, self.bucket_name, channel_name, expires, params)
  382. def list_objects(self, prefix='', delimiter='', marker='', max_keys=100, headers=None):
  383. """根据前缀罗列Bucket里的文件。
  384. :param str prefix: 只罗列文件名为该前缀的文件
  385. :param str delimiter: 分隔符。可以用来模拟目录
  386. :param str marker: 分页标志。首次调用传空串,后续使用返回值的next_marker
  387. :param int max_keys: 最多返回文件的个数,文件和目录的和不能超过该值
  388. :param headers: HTTP头部
  389. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  390. :return: :class:`ListObjectsResult <oss2.models.ListObjectsResult>`
  391. """
  392. headers = http.CaseInsensitiveDict(headers)
  393. logger.debug(
  394. "Start to List objects, bucket: {0}, prefix: {1}, delimiter: {2}, marker: {3}, max-keys: {4}".format(
  395. self.bucket_name, to_string(prefix), delimiter, to_string(marker), max_keys))
  396. resp = self.__do_object('GET', '',
  397. params={'prefix': prefix,
  398. 'delimiter': delimiter,
  399. 'marker': marker,
  400. 'max-keys': str(max_keys),
  401. 'encoding-type': 'url'},
  402. headers=headers)
  403. logger.debug("List objects done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  404. return self._parse_result(resp, xml_utils.parse_list_objects, ListObjectsResult)
  405. def list_objects_v2(self, prefix='', delimiter='', continuation_token='', start_after='', fetch_owner=False, encoding_type='url', max_keys=100, headers=None):
  406. """根据前缀罗列Bucket里的文件。
  407. :param str prefix: 只罗列文件名为该前缀的文件
  408. :param str delimiter: 分隔符。可以用来模拟目录
  409. :param str continuation_token: 分页标志。首次调用传空串,后续使用返回值的next_continuation_token
  410. :param str start_after: 起始文件名称,OSS会返回按照字典序排列start_after之后的文件。
  411. :param bool fetch_owner: 是否获取文件的owner信息,默认不返回。
  412. :param int max_keys: 最多返回文件的个数,文件和目录的和不能超过该值
  413. :param headers: HTTP头部
  414. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  415. :return: :class:`ListObjectsV2Result <oss2.models.ListObjectsV2Result>`
  416. """
  417. headers = http.CaseInsensitiveDict(headers)
  418. logger.debug(
  419. "Start to List objects, bucket: {0}, prefix: {1}, delimiter: {2}, continuation_token: {3}, "
  420. "start-after: {4}, fetch-owner: {5}, encoding_type: {6}, max-keys: {7}".format(
  421. self.bucket_name, to_string(prefix), delimiter, continuation_token, start_after, fetch_owner, encoding_type, max_keys))
  422. resp = self.__do_object('GET', '',
  423. params={'list-type': '2',
  424. 'prefix': prefix,
  425. 'delimiter': delimiter,
  426. 'continuation-token': continuation_token,
  427. 'start-after': start_after,
  428. 'fetch-owner': str(fetch_owner).lower(),
  429. 'max-keys': str(max_keys),
  430. 'encoding-type': encoding_type},
  431. headers=headers)
  432. logger.debug("List objects V2 done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  433. return self._parse_result(resp, xml_utils.parse_list_objects_v2, ListObjectsV2Result)
  434. def put_object(self, key, data,
  435. headers=None,
  436. progress_callback=None):
  437. """上传一个普通文件。
  438. 用法 ::
  439. >>> bucket.put_object('readme.txt', 'content of readme.txt')
  440. >>> with open(u'local_file.txt', 'rb') as f:
  441. >>> bucket.put_object('remote_file.txt', f)
  442. :param key: 上传到OSS的文件名
  443. :param data: 待上传的内容。
  444. :type data: bytes,str或file-like object
  445. :param headers: 用户指定的HTTP头部。可以指定Content-Type、Content-MD5、x-oss-meta-开头的头部等
  446. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  447. :param progress_callback: 用户指定的进度回调函数。可以用来实现进度条等功能。参考 :ref:`progress_callback` 。
  448. :return: :class:`PutObjectResult <oss2.models.PutObjectResult>`
  449. """
  450. headers = utils.set_content_type(http.CaseInsensitiveDict(headers), key)
  451. if progress_callback:
  452. data = utils.make_progress_adapter(data, progress_callback)
  453. if self.enable_crc:
  454. data = utils.make_crc_adapter(data)
  455. logger.debug("Start to put object, bucket: {0}, key: {1}, headers: {2}".format(self.bucket_name, to_string(key),
  456. headers))
  457. resp = self.__do_object('PUT', key, data=data, headers=headers)
  458. logger.debug("Put object done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  459. result = PutObjectResult(resp)
  460. if self.enable_crc and result.crc is not None:
  461. utils.check_crc('put object', data.crc, result.crc, result.request_id)
  462. return result
  463. def put_object_from_file(self, key, filename,
  464. headers=None,
  465. progress_callback=None):
  466. """上传一个本地文件到OSS的普通文件。
  467. :param str key: 上传到OSS的文件名
  468. :param str filename: 本地文件名,需要有可读权限
  469. :param headers: 用户指定的HTTP头部。可以指定Content-Type、Content-MD5、x-oss-meta-开头的头部等
  470. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  471. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  472. :return: :class:`PutObjectResult <oss2.models.PutObjectResult>`
  473. """
  474. headers = utils.set_content_type(http.CaseInsensitiveDict(headers), filename)
  475. logger.debug("Put object from file, bucket: {0}, key: {1}, file path: {2}".format(
  476. self.bucket_name, to_string(key), filename))
  477. with open(to_unicode(filename), 'rb') as f:
  478. return self.put_object(key, f, headers=headers, progress_callback=progress_callback)
  479. def put_object_with_url(self, sign_url, data, headers=None, progress_callback=None):
  480. """ 使用加签的url上传对象
  481. :param sign_url: 加签的url
  482. :param data: 待上传的数据
  483. :param headers: 用户指定的HTTP头部。可以指定Content-Type、Content-MD5、x-oss-meta-开头的头部等,必须和签名时保持一致
  484. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  485. :return:
  486. """
  487. headers = http.CaseInsensitiveDict(headers)
  488. if progress_callback:
  489. data = utils.make_progress_adapter(data, progress_callback)
  490. if self.enable_crc:
  491. data = utils.make_crc_adapter(data)
  492. logger.debug("Start to put object with signed url, bucket: {0}, sign_url: {1}, headers: {2}".format(
  493. self.bucket_name, sign_url, headers))
  494. resp = self._do_url('PUT', sign_url, data=data, headers=headers)
  495. logger.debug("Put object with url done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  496. result = PutObjectResult(resp)
  497. if self.enable_crc and result.crc is not None:
  498. utils.check_crc('put object', data.crc, result.crc, result.request_id)
  499. return result
  500. def put_object_with_url_from_file(self, sign_url, filename,
  501. headers=None,
  502. progress_callback=None):
  503. """ 使用加签的url上传本地文件到oss
  504. :param sign_url: 加签的url
  505. :param filename: 本地文件路径
  506. :param headers: 用户指定的HTTP头部。可以指定Content-Type、Content-MD5、x-oss-meta-开头的头部等,必须和签名时保持一致
  507. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  508. :return:
  509. """
  510. logger.debug("Put object from file with signed url, bucket: {0}, sign_url: {1}, file path: {2}".format(
  511. self.bucket_name, sign_url, filename))
  512. with open(to_unicode(filename), 'rb') as f:
  513. return self.put_object_with_url(sign_url, f, headers=headers, progress_callback=progress_callback)
  514. def append_object(self, key, position, data,
  515. headers=None,
  516. progress_callback=None,
  517. init_crc=None):
  518. """追加上传一个文件。
  519. :param str key: 新的文件名,或已经存在的可追加文件名
  520. :param int position: 追加上传一个新的文件, `position` 设为0;追加一个已经存在的可追加文件, `position` 设为文件的当前长度。
  521. `position` 可以从上次追加的结果 `AppendObjectResult.next_position` 中获得。
  522. :param data: 用户数据
  523. :type data: str、bytes、file-like object或可迭代对象
  524. :param headers: 用户指定的HTTP头部。可以指定Content-Type、Content-MD5、x-oss-开头的头部等
  525. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  526. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  527. :return: :class:`AppendObjectResult <oss2.models.AppendObjectResult>`
  528. :raises: 如果 `position` 和当前文件长度不一致,抛出 :class:`PositionNotEqualToLength <oss2.exceptions.PositionNotEqualToLength>` ;
  529. 如果当前文件不是可追加类型,抛出 :class:`ObjectNotAppendable <oss2.exceptions.ObjectNotAppendable>` ;
  530. 还会抛出其他一些异常
  531. """
  532. headers = utils.set_content_type(http.CaseInsensitiveDict(headers), key)
  533. if progress_callback:
  534. data = utils.make_progress_adapter(data, progress_callback)
  535. if self.enable_crc and init_crc is not None:
  536. data = utils.make_crc_adapter(data, init_crc)
  537. logger.debug("Start to append object, bucket: {0}, key: {1}, headers: {2}, position: {3}".format(
  538. self.bucket_name, to_string(key), headers, position))
  539. resp = self.__do_object('POST', key,
  540. data=data,
  541. headers=headers,
  542. params={'append': '', 'position': str(position)})
  543. logger.debug("Append object done, req_id: {0}, statu_code: {1}".format(resp.request_id, resp.status))
  544. result = AppendObjectResult(resp)
  545. if self.enable_crc and result.crc is not None and init_crc is not None:
  546. utils.check_crc('append object', data.crc, result.crc, result.request_id)
  547. return result
  548. def get_object(self, key,
  549. byte_range=None,
  550. headers=None,
  551. progress_callback=None,
  552. process=None,
  553. params=None):
  554. """下载一个文件。
  555. 用法 ::
  556. >>> result = bucket.get_object('readme.txt')
  557. >>> print(result.read())
  558. 'hello world'
  559. :param key: 文件名
  560. :param byte_range: 指定下载范围。参见 :ref:`byte_range`
  561. :param headers: HTTP头部
  562. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  563. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  564. :param process: oss文件处理,如图像服务等。指定后process,返回的内容为处理后的文件。
  565. :param params: http 请求的查询字符串参数
  566. :type params: dict
  567. :return: file-like object
  568. :raises: 如果文件不存在,则抛出 :class:`NoSuchKey <oss2.exceptions.NoSuchKey>` ;还可能抛出其他异常
  569. """
  570. headers = http.CaseInsensitiveDict(headers)
  571. range_string = _make_range_string(byte_range)
  572. if range_string:
  573. headers['range'] = range_string
  574. params = {} if params is None else params
  575. if process:
  576. params.update({Bucket.PROCESS: process})
  577. logger.debug("Start to get object, bucket: {0}, key: {1}, range: {2}, headers: {3}, params: {4}".format(
  578. self.bucket_name, to_string(key), range_string, headers, params))
  579. resp = self.__do_object('GET', key, headers=headers, params=params)
  580. logger.debug("Get object done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  581. return GetObjectResult(resp, progress_callback, self.enable_crc)
  582. def select_object(self, key, sql,
  583. progress_callback=None,
  584. select_params=None,
  585. byte_range=None,
  586. headers=None
  587. ):
  588. """Select一个文件内容,支持(Csv,Json Doc,Json Lines及其GZIP压缩文件).
  589. 用法 ::
  590. 对于Csv:
  591. >>> result = bucket.select_object('access.log', 'select * from ossobject where _4 > 40')
  592. >>> print(result.read())
  593. 'hello world'
  594. 对于Json Doc: { contacts:[{"firstName":"abc", "lastName":"def"},{"firstName":"abc1", "lastName":"def1"}]}
  595. >>> result = bucket.select_object('sample.json', 'select s.firstName, s.lastName from ossobject.contacts[*] s', select_params = {"Json_Type":"DOCUMENT"})
  596. 对于Json Lines: {"firstName":"abc", "lastName":"def"},{"firstName":"abc1", "lastName":"def1"}
  597. >>> result = bucket.select_object('sample.json', 'select s.firstName, s.lastName from ossobject s', select_params = {"Json_Type":"LINES"})
  598. :param key: 文件名
  599. :param sql: sql statement
  600. :param select_params: select参数集合,对于Json文件必须制定Json_Type类型。参见 :ref:`select_params`
  601. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  602. :param byte_range: select content of specific range。可以设置Bytes header指定select csv时的文件起始offset和长度。
  603. :param headers: HTTP头部
  604. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  605. :return: file-like object
  606. :raises: 如果文件不存在,则抛出 :class:`NoSuchKey <oss2.exceptions.NoSuchKey>` ;还可能抛出其他异常
  607. """
  608. range_select = False
  609. headers = http.CaseInsensitiveDict(headers)
  610. range_string = _make_range_string(byte_range)
  611. if range_string:
  612. headers['range'] = range_string
  613. range_select = True
  614. if (range_select == True and
  615. (select_params is None or
  616. (SelectParameters.AllowQuotedRecordDelimiter not in select_params or str(select_params[SelectParameters.AllowQuotedRecordDelimiter]).lower() != 'false'))):
  617. raise ClientError('"AllowQuotedRecordDelimiter" must be specified in select_params as False when "Range" is specified in header.')
  618. body = xml_utils.to_select_object(sql, select_params)
  619. params = {'x-oss-process': 'csv/select'}
  620. if select_params is not None and SelectParameters.Json_Type in select_params:
  621. params['x-oss-process'] = 'json/select'
  622. self.timeout = 3600
  623. resp = self.__do_object('POST', key, data=body, headers=headers, params=params)
  624. crc_enabled = False
  625. if select_params is not None and SelectParameters.EnablePayloadCrc in select_params:
  626. if str(select_params[SelectParameters.EnablePayloadCrc]).lower() == "true":
  627. crc_enabled = True
  628. return SelectObjectResult(resp, progress_callback, crc_enabled)
  629. def get_object_to_file(self, key, filename,
  630. byte_range=None,
  631. headers=None,
  632. progress_callback=None,
  633. process=None,
  634. params=None):
  635. """下载一个文件到本地文件。
  636. :param key: 文件名
  637. :param filename: 本地文件名。要求父目录已经存在,且有写权限。
  638. :param byte_range: 指定下载范围。参见 :ref:`byte_range`
  639. :param headers: HTTP头部
  640. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  641. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  642. :param process: oss文件处理,如图像服务等。指定后process,返回的内容为处理后的文件。
  643. :param params: http 请求的查询字符串参数
  644. :type params: dict
  645. :return: 如果文件不存在,则抛出 :class:`NoSuchKey <oss2.exceptions.NoSuchKey>` ;还可能抛出其他异常
  646. """
  647. logger.debug("Start to get object to file, bucket: {0}, key: {1}, file path: {2}".format(
  648. self.bucket_name, to_string(key), filename))
  649. with open(to_unicode(filename), 'wb') as f:
  650. result = self.get_object(key, byte_range=byte_range, headers=headers, progress_callback=progress_callback,
  651. process=process, params=params)
  652. if result.content_length is None:
  653. shutil.copyfileobj(result, f)
  654. else:
  655. utils.copyfileobj_and_verify(result, f, result.content_length, request_id=result.request_id)
  656. if self.enable_crc and byte_range is None:
  657. if (headers is None) or ('Accept-Encoding' not in headers) or (headers['Accept-Encoding'] != 'gzip'):
  658. utils.check_crc('get', result.client_crc, result.server_crc, result.request_id)
  659. return result
  660. def get_object_with_url(self, sign_url,
  661. byte_range=None,
  662. headers=None,
  663. progress_callback=None):
  664. """使用加签的url下载文件
  665. :param sign_url: 加签的url
  666. :param byte_range: 指定下载范围。参见 :ref:`byte_range`
  667. :param headers: HTTP头部
  668. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict,必须和签名时保持一致
  669. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  670. :return: file-like object
  671. :raises: 如果文件不存在,则抛出 :class:`NoSuchKey <oss2.exceptions.NoSuchKey>` ;还可能抛出其他异常
  672. """
  673. headers = http.CaseInsensitiveDict(headers)
  674. range_string = _make_range_string(byte_range)
  675. if range_string:
  676. headers['range'] = range_string
  677. logger.debug("Start to get object with url, bucket: {0}, sign_url: {1}, range: {2}, headers: {3}".format(
  678. self.bucket_name, sign_url, range_string, headers))
  679. resp = self._do_url('GET', sign_url, headers=headers)
  680. return GetObjectResult(resp, progress_callback, self.enable_crc)
  681. def get_object_with_url_to_file(self, sign_url,
  682. filename,
  683. byte_range=None,
  684. headers=None,
  685. progress_callback=None):
  686. """使用加签的url下载文件
  687. :param sign_url: 加签的url
  688. :param filename: 本地文件名。要求父目录已经存在,且有写权限。
  689. :param byte_range: 指定下载范围。参见 :ref:`byte_range`
  690. :param headers: HTTP头部
  691. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict,,必须和签名时保持一致
  692. :param progress_callback: 用户指定的进度回调函数。参考 :ref:`progress_callback`
  693. :return: file-like object
  694. :raises: 如果文件不存在,则抛出 :class:`NoSuchKey <oss2.exceptions.NoSuchKey>` ;还可能抛出其他异常
  695. """
  696. logger.debug(
  697. "Start to get object with url, bucket: {0}, sign_url: {1}, file path: {2}, range: {3}, headers: {4}"
  698. .format(self.bucket_name, sign_url, filename, byte_range, headers))
  699. with open(to_unicode(filename), 'wb') as f:
  700. result = self.get_object_with_url(sign_url, byte_range=byte_range, headers=headers,
  701. progress_callback=progress_callback)
  702. if result.content_length is None:
  703. shutil.copyfileobj(result, f)
  704. else:
  705. utils.copyfileobj_and_verify(result, f, result.content_length, request_id=result.request_id)
  706. return result
  707. def select_object_to_file(self, key, filename, sql,
  708. progress_callback=None,
  709. select_params=None,
  710. headers=None
  711. ):
  712. """Select一个文件的内容到本地文件
  713. :param key: OSS文件名
  714. :param filename: 本地文件名。其父亲目录已经存在且有写权限。
  715. :param progress_callback: 调用进度的callback。参考 :ref:`progress_callback`
  716. :param select_params: select参数集合。参见 :ref:`select_params`
  717. :param headers: HTTP头部
  718. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  719. :return: 如果文件不存在, 抛出 :class:`NoSuchKey <oss2.exceptions.NoSuchKey>`
  720. """
  721. with open(to_unicode(filename), 'wb') as f:
  722. result = self.select_object(key, sql, progress_callback=progress_callback,
  723. select_params=select_params, headers=headers)
  724. for chunk in result:
  725. f.write(chunk)
  726. return result
  727. def head_object(self, key, headers=None, params=None):
  728. """获取文件元信息。
  729. HTTP响应的头部包含了文件元信息,可以通过 `RequestResult` 的 `headers` 成员获得。
  730. 用法 ::
  731. >>> result = bucket.head_object('readme.txt')
  732. >>> print(result.content_type)
  733. text/plain
  734. :param key: 文件名
  735. :param headers: HTTP头部
  736. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  737. :param params: HTTP请求参数,传入versionId,获取指定版本Object元信息
  738. :type params: 可以是dict,建议是oss2.CaseInsensitiveDict
  739. :return: :class:`HeadObjectResult <oss2.models.HeadObjectResult>`
  740. :raises: 如果Bucket不存在或者Object不存在,则抛出 :class:`NotFound <oss2.exceptions.NotFound>`
  741. """
  742. logger.debug("Start to head object, bucket: {0}, key: {1}, headers: {2}".format(
  743. self.bucket_name, to_string(key), headers))
  744. resp = self.__do_object('HEAD', key, headers=headers, params=params)
  745. logger.debug("Head object done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  746. return HeadObjectResult(resp)
  747. def create_select_object_meta(self, key, select_meta_params=None, headers=None):
  748. """获取或创建CSV,JSON LINES 文件元信息。如果元信息存在,返回之;不然则创建后返回之
  749. HTTP响应的头部包含了文件元信息,可以通过 `RequestResult` 的 `headers` 成员获得。
  750. CSV文件用法 ::
  751. >>> select_meta_params = { 'FieldDelimiter': ',',
  752. 'RecordDelimiter': '\r\n',
  753. 'QuoteCharacter': '"',
  754. 'OverwriteIfExists' : 'false'}
  755. >>> result = bucket.create_select_object_meta('csv.txt', select_meta_params)
  756. >>> print(result.rows)
  757. JSON LINES文件用法 ::
  758. >>> select_meta_params = { 'Json_Type':'LINES', 'OverwriteIfExists':'False'}
  759. >>> result = bucket.create_select_object_meta('jsonlines.json', select_meta_params)
  760. :param key: 文件名
  761. :param select_meta_params: 参数词典,可以是dict,参见ref:`csv_meta_params`
  762. :param headers: HTTP头部
  763. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  764. :return: :class:`GetSelectObjectMetaResult <oss2.models.HeadObjectResult>`.
  765. 除了 rows 和splits 属性之外, 它也返回head object返回的其他属性。
  766. rows表示该文件的总记录数。
  767. splits表示该文件的总Split个数,一个Split包含若干条记录,每个Split的总字节数大致相当。用户可以以Split为单位进行分片查询。
  768. :raises: 如果Bucket不存在或者Object不存在,则抛出:class:`NotFound <oss2.exceptions.NotFound>`
  769. """
  770. headers = http.CaseInsensitiveDict(headers)
  771. body = xml_utils.to_get_select_object_meta(select_meta_params)
  772. params = {'x-oss-process': 'csv/meta'}
  773. if select_meta_params is not None and 'Json_Type' in select_meta_params:
  774. params['x-oss-process'] = 'json/meta'
  775. self.timeout = 3600
  776. resp = self.__do_object('POST', key, data=body, headers=headers, params=params)
  777. return GetSelectObjectMetaResult(resp)
  778. def get_object_meta(self, key, params=None, headers=None):
  779. """获取文件基本元信息,包括该Object的ETag、Size(文件大小)、LastModified,并不返回其内容。
  780. HTTP响应的头部包含了文件基本元信息,可以通过 `GetObjectMetaResult` 的 `last_modified`,`content_length`,`etag` 成员获得。
  781. :param key: 文件名
  782. :param dict params: 请求参数
  783. :param headers: HTTP头部
  784. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  785. :return: :class:`GetObjectMetaResult <oss2.models.GetObjectMetaResult>`
  786. :raises: 如果文件不存在,则抛出 :class:`NoSuchKey <oss2.exceptions.NoSuchKey>` ;还可能抛出其他异常
  787. """
  788. headers = http.CaseInsensitiveDict(headers)
  789. logger.debug("Start to get object metadata, bucket: {0}, key: {1}".format(self.bucket_name, to_string(key)))
  790. if params is None:
  791. params = dict()
  792. if Bucket.OBJECTMETA not in params:
  793. params[Bucket.OBJECTMETA] = ''
  794. resp = self.__do_object('GET', key, params=params, headers=headers)
  795. logger.debug("Get object metadata done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  796. return GetObjectMetaResult(resp)
  797. def object_exists(self, key, headers=None):
  798. """如果文件存在就返回True,否则返回False。如果Bucket不存在,或是发生其他错误,则抛出异常。"""
  799. #:param key: 文件名
  800. #:param headers: HTTP头部
  801. #:type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  802. # 如果我们用head_object来实现的话,由于HTTP HEAD请求没有响应体,只有响应头部,这样当发生404时,
  803. # 我们无法区分是NoSuchBucket还是NoSuchKey错误。
  804. #
  805. # 2.2.0之前的实现是通过get_object的if-modified-since头部,把date设为当前时间24小时后,这样如果文件存在,则会返回
  806. # 304 (NotModified);不存在,则会返回NoSuchKey。get_object会受回源的影响,如果配置会404回源,get_object会判断错误。
  807. #
  808. # 目前的实现是通过get_object_meta判断文件是否存在。
  809. logger.debug("Start to check if object exists, bucket: {0}, key: {1}".format(self.bucket_name, to_string(key)))
  810. try:
  811. self.get_object_meta(key, headers=headers)
  812. except exceptions.NoSuchKey:
  813. return False
  814. except:
  815. raise
  816. return True
  817. def copy_object(self, source_bucket_name, source_key, target_key, headers=None, params=None):
  818. """拷贝一个文件到当前Bucket。
  819. :param str source_bucket_name: 源Bucket名
  820. :param str source_key: 源文件名
  821. :param str target_key: 目标文件名
  822. :param dict params: 请求参数
  823. :param headers: HTTP头部
  824. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  825. :return: :class:`PutObjectResult <oss2.models.PutObjectResult>`
  826. """
  827. headers = http.CaseInsensitiveDict(headers)
  828. if params and Bucket.VERSIONID in params:
  829. headers[OSS_COPY_OBJECT_SOURCE] = '/' + source_bucket_name + \
  830. '/' + urlquote(source_key, '') + '?versionId=' + params[Bucket.VERSIONID]
  831. else:
  832. headers[OSS_COPY_OBJECT_SOURCE] = '/' + source_bucket_name + '/' + urlquote(source_key, '')
  833. logger.debug(
  834. "Start to copy object, source bucket: {0}, source key: {1}, bucket: {2}, key: {3}, headers: {4}".format(
  835. source_bucket_name, to_string(source_key), self.bucket_name, to_string(target_key), headers))
  836. resp = self.__do_object('PUT', target_key, headers=headers)
  837. logger.debug("Copy object done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  838. return PutObjectResult(resp)
  839. def update_object_meta(self, key, headers):
  840. """更改Object的元数据信息,包括Content-Type这类标准的HTTP头部,以及以x-oss-meta-开头的自定义元数据。
  841. 用户可以通过 :func:`head_object` 获得元数据信息。
  842. :param str key: 文件名
  843. :param headers: HTTP头部,包含了元数据信息
  844. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  845. :return: :class:`RequestResult <oss2.models.RequestResults>`
  846. """
  847. if headers is not None:
  848. headers[OSS_METADATA_DIRECTIVE] = 'REPLACE'
  849. logger.debug("Start to update object metadata, bucket: {0}, key: {1}".format(self.bucket_name, to_string(key)))
  850. return self.copy_object(self.bucket_name, key, key, headers=headers)
  851. def delete_object(self, key, params=None, headers=None):
  852. """删除一个文件。
  853. :param str key: 文件名
  854. :param params: 请求参数
  855. :param headers: HTTP头部
  856. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  857. :return: :class:`RequestResult <oss2.models.RequestResult>`
  858. """
  859. headers = http.CaseInsensitiveDict(headers)
  860. logger.info("Start to delete object, bucket: {0}, key: {1}".format(self.bucket_name, to_string(key)))
  861. resp = self.__do_object('DELETE', key, params=params, headers=headers)
  862. logger.debug("Delete object done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  863. return RequestResult(resp)
  864. def restore_object(self, key, params=None, headers=None, input=None):
  865. """restore an object
  866. 如果是第一次针对该object调用接口,返回RequestResult.status = 202;
  867. 如果已经成功调用过restore接口,且服务端仍处于解冻中,抛异常RestoreAlreadyInProgress(status=409)
  868. 如果已经成功调用过restore接口,且服务端解冻已经完成,再次调用时返回RequestResult.status = 200,且会将object的可下载时间延长一天,最多延长7天。
  869. 如果object不存在,则抛异常NoSuchKey(status=404);
  870. 对非Archive类型的Object提交restore,则抛异常OperationNotSupported(status=400)
  871. 也可以通过调用head_object接口来获取meta信息来判断是否可以restore与restore的状态
  872. 代码示例::
  873. >>> meta = bucket.head_object(key)
  874. >>> if meta.resp.headers['x-oss-storage-class'] == oss2.BUCKET_STORAGE_CLASS_ARCHIVE:
  875. >>> bucket.restore_object(key)
  876. >>> while True:
  877. >>> meta = bucket.head_object(key)
  878. >>> if meta.resp.headers['x-oss-restore'] == 'ongoing-request="true"':
  879. >>> time.sleep(5)
  880. >>> else:
  881. >>> break
  882. :param str key: object name
  883. :param params: 请求参数
  884. :param headers: HTTP头部
  885. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  886. :param input: 解冻配置。
  887. :type input: class:`RestoreConfiguration <oss2.models.RestoreConfiguration>`
  888. :return: :class:`RequestResult <oss2.models.RequestResult>`
  889. """
  890. headers = http.CaseInsensitiveDict(headers)
  891. logger.debug("Start to restore object, bucket: {0}, key: {1}".format(self.bucket_name, to_string(key)))
  892. if params is None:
  893. params = dict()
  894. if Bucket.RESTORE not in params:
  895. params[Bucket.RESTORE] = ''
  896. data = self.__convert_data(RestoreConfiguration, xml_utils.to_put_restore_config, input)
  897. resp = self.__do_object('POST', key, params=params, headers=headers, data=data)
  898. logger.debug("Restore object done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  899. return RequestResult(resp)
  900. def put_object_acl(self, key, permission, params=None, headers=None):
  901. """设置文件的ACL。
  902. :param str key: 文件名
  903. :param str permission: 可以是oss2.OBJECT_ACL_DEFAULT、oss2.OBJECT_ACL_PRIVATE、oss2.OBJECT_ACL_PUBLIC_READ或
  904. oss2.OBJECT_ACL_PUBLIC_READ_WRITE。
  905. :param dict params: 请求参数
  906. :param headers: HTTP头部
  907. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  908. :return: :class:`RequestResult <oss2.models.RequestResult>`
  909. """
  910. logger.debug("Start to put object acl, bucket: {0}, key: {1}, acl: {2}".format(
  911. self.bucket_name, to_string(key), permission))
  912. headers = http.CaseInsensitiveDict(headers)
  913. headers[OSS_OBJECT_ACL] = permission
  914. if params is None:
  915. params = dict()
  916. if Bucket.ACL not in params:
  917. params[Bucket.ACL] = ''
  918. resp = self.__do_object('PUT', key, params=params, headers=headers)
  919. logger.debug("Put object acl done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  920. return RequestResult(resp)
  921. def get_object_acl(self, key, params=None, headers=None):
  922. """获取文件的ACL。
  923. :param key: 文件名
  924. :param params: 请求参数
  925. :param headers: HTTP头部
  926. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  927. :return: :class:`GetObjectAclResult <oss2.models.GetObjectAclResult>`
  928. """
  929. logger.debug("Start to get object acl, bucket: {0}, key: {1}".format(self.bucket_name, to_string(key)))
  930. headers = http.CaseInsensitiveDict(headers)
  931. if params is None:
  932. params = dict()
  933. if Bucket.ACL not in params:
  934. params[Bucket.ACL] = ''
  935. resp = self.__do_object('GET', key, params=params, headers=headers)
  936. logger.debug("Get object acl done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  937. return self._parse_result(resp, xml_utils.parse_get_object_acl, GetObjectAclResult)
  938. def batch_delete_objects(self, key_list, headers=None):
  939. """批量删除文件。待删除文件列表不能为空。
  940. :param key_list: 文件名列表,不能为空。
  941. :type key_list: list of str
  942. :param headers: HTTP头部
  943. :return: :class:`BatchDeleteObjectsResult <oss2.models.BatchDeleteObjectsResult>`
  944. """
  945. if not key_list:
  946. raise ClientError('key_list should not be empty')
  947. logger.debug("Start to delete objects, bucket: {0}, keys: {1}".format(self.bucket_name, key_list))
  948. data = xml_utils.to_batch_delete_objects_request(key_list, False)
  949. headers = http.CaseInsensitiveDict(headers)
  950. headers['Content-MD5'] = utils.content_md5(data)
  951. resp = self.__do_object('POST', '',
  952. data=data,
  953. params={'delete': '', 'encoding-type': 'url'},
  954. headers=headers)
  955. logger.debug("Delete objects done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  956. return self._parse_result(resp, xml_utils.parse_batch_delete_objects, BatchDeleteObjectsResult)
  957. def delete_object_versions(self, keylist_versions, headers=None):
  958. """批量删除带版本文件。待删除文件列表不能为空。
  959. :param key_list_with_version: 带版本的文件名列表,不能为空。(如果传入,则不能为空)
  960. :type key_list: list of BatchDeleteObjectsList
  961. :param headers: HTTP头部
  962. :return: :class:`BatchDeleteObjectsResult <oss2.models.BatchDeleteObjectsResult>`
  963. """
  964. if not keylist_versions:
  965. raise ClientError('keylist_versions should not be empty')
  966. logger.debug("Start to delete object versions, bucket: {0}".format(self.bucket_name))
  967. data = xml_utils.to_batch_delete_objects_version_request(keylist_versions, False)
  968. headers = http.CaseInsensitiveDict(headers)
  969. headers['Content-MD5'] = utils.content_md5(data)
  970. resp = self.__do_object('POST', '',
  971. data=data,
  972. params={'delete': '', 'encoding-type': 'url'},
  973. headers=headers)
  974. logger.debug("Delete object versions done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  975. return self._parse_result(resp, xml_utils.parse_batch_delete_objects, BatchDeleteObjectsResult)
  976. def init_multipart_upload(self, key, headers=None, params=None):
  977. """初始化分片上传。
  978. 返回值中的 `upload_id` 以及Bucket名和Object名三元组唯一对应了此次分片上传事件。
  979. :param str key: 待上传的文件名
  980. :param headers: HTTP头部
  981. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  982. :return: :class:`InitMultipartUploadResult <oss2.models.InitMultipartUploadResult>`
  983. """
  984. headers = utils.set_content_type(http.CaseInsensitiveDict(headers), key)
  985. if params is None:
  986. tmp_params = dict()
  987. else:
  988. tmp_params = params.copy()
  989. tmp_params['uploads'] = ''
  990. logger.debug("Start to init multipart upload, bucket: {0}, keys: {1}, headers: {2}, params: {3}".format(
  991. self.bucket_name, to_string(key), headers, tmp_params))
  992. resp = self.__do_object('POST', key, params=tmp_params, headers=headers)
  993. logger.debug("Init multipart upload done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  994. return self._parse_result(resp, xml_utils.parse_init_multipart_upload, InitMultipartUploadResult)
  995. def upload_part(self, key, upload_id, part_number, data, progress_callback=None, headers=None):
  996. """上传一个分片。
  997. :param str key: 待上传文件名,这个文件名要和 :func:`init_multipart_upload` 的文件名一致。
  998. :param str upload_id: 分片上传ID
  999. :param int part_number: 分片号,最小值是1.
  1000. :param data: 待上传数据。
  1001. :param progress_callback: 用户指定进度回调函数。可以用来实现进度条等功能。参考 :ref:`progress_callback` 。
  1002. :param headers: 用户指定的HTTP头部。可以指定Content-MD5头部等
  1003. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1004. :return: :class:`PutObjectResult <oss2.models.PutObjectResult>`
  1005. """
  1006. headers = http.CaseInsensitiveDict(headers)
  1007. if progress_callback:
  1008. data = utils.make_progress_adapter(data, progress_callback)
  1009. if self.enable_crc:
  1010. data = utils.make_crc_adapter(data)
  1011. logger.debug(
  1012. "Start to upload multipart, bucket: {0}, key: {1}, upload_id: {2}, part_number: {3}, headers: {4}".format(
  1013. self.bucket_name, to_string(key), upload_id, part_number, headers))
  1014. resp = self.__do_object('PUT', key,
  1015. params={'uploadId': upload_id, 'partNumber': str(part_number)},
  1016. headers=headers,
  1017. data=data)
  1018. logger.debug("Upload multipart done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1019. result = PutObjectResult(resp)
  1020. if self.enable_crc and result.crc is not None:
  1021. utils.check_crc('upload part', data.crc, result.crc, result.request_id)
  1022. return result
  1023. def complete_multipart_upload(self, key, upload_id, parts, headers=None):
  1024. """完成分片上传,创建文件。
  1025. :param str key: 待上传的文件名,这个文件名要和 :func:`init_multipart_upload` 的文件名一致。
  1026. :param str upload_id: 分片上传ID
  1027. :param parts: PartInfo列表。PartInfo中的part_number和etag是必填项。其中的etag可以从 :func:`upload_part` 的返回值中得到。
  1028. :type parts: list of `PartInfo <oss2.models.PartInfo>`
  1029. :param headers: HTTP头部
  1030. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1031. :return: :class:`PutObjectResult <oss2.models.PutObjectResult>`
  1032. """
  1033. headers = http.CaseInsensitiveDict(headers)
  1034. parts = sorted(parts, key=lambda p: p.part_number)
  1035. data = xml_utils.to_complete_upload_request(parts)
  1036. logger.debug("Start to complete multipart upload, bucket: {0}, key: {1}, upload_id: {2}, parts: {3}".format(
  1037. self.bucket_name, to_string(key), upload_id, data))
  1038. resp = self.__do_object('POST', key,
  1039. params={'uploadId': upload_id},
  1040. data=data,
  1041. headers=headers)
  1042. logger.debug(
  1043. "Complete multipart upload done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1044. result = PutObjectResult(resp)
  1045. if self.enable_crc:
  1046. object_crc = utils.calc_obj_crc_from_parts(parts)
  1047. utils.check_crc('multipart upload', object_crc, result.crc, result.request_id)
  1048. return result
  1049. def abort_multipart_upload(self, key, upload_id, headers=None):
  1050. """取消分片上传。
  1051. :param str key: 待上传的文件名,这个文件名要和 :func:`init_multipart_upload` 的文件名一致。
  1052. :param str upload_id: 分片上传ID
  1053. :param headers: HTTP头部
  1054. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1055. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1056. """
  1057. logger.debug("Start to abort multipart upload, bucket: {0}, key: {1}, upload_id: {2}".format(
  1058. self.bucket_name, to_string(key), upload_id))
  1059. headers = http.CaseInsensitiveDict(headers)
  1060. resp = self.__do_object('DELETE', key,
  1061. params={'uploadId': upload_id}, headers=headers)
  1062. logger.debug("Abort multipart done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1063. return RequestResult(resp)
  1064. def list_multipart_uploads(self,
  1065. prefix='',
  1066. delimiter='',
  1067. key_marker='',
  1068. upload_id_marker='',
  1069. max_uploads=1000,
  1070. headers=None):
  1071. """罗列正在进行中的分片上传。支持分页。
  1072. :param str prefix: 只罗列匹配该前缀的文件的分片上传
  1073. :param str delimiter: 目录分割符
  1074. :param str key_marker: 文件名分页符。第一次调用可以不传,后续设为返回值中的 `next_key_marker`
  1075. :param str upload_id_marker: 分片ID分页符。第一次调用可以不传,后续设为返回值中的 `next_upload_id_marker`
  1076. :param int max_uploads: 一次罗列最多能够返回的条目数
  1077. :param headers: HTTP头部
  1078. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1079. :return: :class:`ListMultipartUploadsResult <oss2.models.ListMultipartUploadsResult>`
  1080. """
  1081. logger.debug("Start to list multipart uploads, bucket: {0}, prefix: {1}, delimiter: {2}, key_marker: {3}, "
  1082. "upload_id_marker: {4}, max_uploads: {5}".format(self.bucket_name, to_string(prefix), delimiter,
  1083. to_string(key_marker), upload_id_marker,
  1084. max_uploads))
  1085. headers = http.CaseInsensitiveDict(headers)
  1086. resp = self.__do_object('GET', '',
  1087. params={'uploads': '',
  1088. 'prefix': prefix,
  1089. 'delimiter': delimiter,
  1090. 'key-marker': key_marker,
  1091. 'upload-id-marker': upload_id_marker,
  1092. 'max-uploads': str(max_uploads),
  1093. 'encoding-type': 'url'},
  1094. headers=headers)
  1095. logger.debug("List multipart uploads done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1096. return self._parse_result(resp, xml_utils.parse_list_multipart_uploads, ListMultipartUploadsResult)
  1097. def upload_part_copy(self, source_bucket_name, source_key, byte_range,
  1098. target_key, target_upload_id, target_part_number,
  1099. headers=None, params=None):
  1100. """分片拷贝。把一个已有文件的一部分或整体拷贝成目标文件的一个分片。
  1101. :source_bucket_name: 源文件所在bucket的名称
  1102. :source_key:源文件名称
  1103. :param byte_range: 指定待拷贝内容在源文件里的范围。参见 :ref:`byte_range`
  1104. :target_key: 目的文件的名称
  1105. :target_upload_id: 目的文件的uploadid
  1106. :target_part_number: 目的文件的分片号
  1107. :param params: 请求参数
  1108. :param headers: HTTP头部
  1109. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1110. :return: :class:`PutObjectResult <oss2.models.PutObjectResult>`
  1111. """
  1112. headers = http.CaseInsensitiveDict(headers)
  1113. if params and Bucket.VERSIONID in params:
  1114. headers[OSS_COPY_OBJECT_SOURCE] = '/' + source_bucket_name + \
  1115. '/' + urlquote(source_key, '') + '?versionId=' + params[Bucket.VERSIONID]
  1116. else:
  1117. headers[OSS_COPY_OBJECT_SOURCE] = '/' + source_bucket_name + '/' + urlquote(source_key, '')
  1118. range_string = _make_range_string(byte_range)
  1119. if range_string:
  1120. headers[OSS_COPY_OBJECT_SOURCE_RANGE] = range_string
  1121. logger.debug("Start to upload part copy, source bucket: {0}, source key: {1}, bucket: {2}, key: {3}, range"
  1122. ": {4}, upload id: {5}, part_number: {6}, headers: {7}".format(source_bucket_name,
  1123. to_string(source_key),self.bucket_name,to_string(target_key),
  1124. byte_range, target_upload_id,target_part_number, headers))
  1125. if params is None:
  1126. params = dict()
  1127. params['uploadId'] = target_upload_id
  1128. params['partNumber'] = str(target_part_number)
  1129. resp = self.__do_object('PUT', target_key,
  1130. params=params,headers=headers)
  1131. logger.debug("Upload part copy done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1132. return PutObjectResult(resp)
  1133. def list_parts(self, key, upload_id,
  1134. marker='', max_parts=1000, headers=None):
  1135. """列举已经上传的分片。支持分页。
  1136. :param headers: HTTP头部
  1137. :param str key: 文件名
  1138. :param str upload_id: 分片上传ID
  1139. :param str marker: 分页符
  1140. :param int max_parts: 一次最多罗列多少分片
  1141. :param headers: HTTP头部
  1142. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1143. :return: :class:`ListPartsResult <oss2.models.ListPartsResult>`
  1144. """
  1145. logger.debug("Start to list parts, bucket: {0}, key: {1}, upload_id: {2}, marker: {3}, max_parts: {4}".format(
  1146. self.bucket_name, to_string(key), upload_id, marker, max_parts))
  1147. headers = http.CaseInsensitiveDict(headers)
  1148. resp = self.__do_object('GET', key,
  1149. params={'uploadId': upload_id,
  1150. 'part-number-marker': marker,
  1151. 'max-parts': str(max_parts)},
  1152. headers=headers)
  1153. logger.debug("List parts done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1154. return self._parse_result(resp, xml_utils.parse_list_parts, ListPartsResult)
  1155. def put_symlink(self, target_key, symlink_key, headers=None):
  1156. """创建Symlink。
  1157. :param str target_key: 目标文件,目标文件不能为符号连接
  1158. :param str symlink_key: 符号连接类文件,其实质是一个特殊的文件,数据指向目标文件
  1159. :param headers: HTTP头部
  1160. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1161. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1162. """
  1163. headers = http.CaseInsensitiveDict(headers)
  1164. headers[OSS_SYMLINK_TARGET] = urlquote(target_key, '')
  1165. logger.debug("Start to put symlink, bucket: {0}, target_key: {1}, symlink_key: {2}, headers: {3}".format(
  1166. self.bucket_name, to_string(target_key), to_string(symlink_key), headers))
  1167. resp = self.__do_object('PUT', symlink_key, headers=headers, params={Bucket.SYMLINK: ''})
  1168. logger.debug("Put symlink done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1169. return RequestResult(resp)
  1170. def get_symlink(self, symlink_key, params=None, headers=None):
  1171. """获取符号连接文件的目标文件。
  1172. :param str symlink_key: 符号连接类文件
  1173. :param dict params: 请求参数
  1174. :param headers: HTTP头部
  1175. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1176. :return: :class:`GetSymlinkResult <oss2.models.GetSymlinkResult>`
  1177. :raises: 如果文件的符号链接不存在,则抛出 :class:`NoSuchKey <oss2.exceptions.NoSuchKey>` ;还可能抛出其他异常
  1178. """
  1179. logger.debug(
  1180. "Start to get symlink, bucket: {0}, symlink_key: {1}".format(self.bucket_name, to_string(symlink_key)))
  1181. headers = http.CaseInsensitiveDict(headers)
  1182. if params is None:
  1183. params = dict()
  1184. if Bucket.SYMLINK not in params:
  1185. params[Bucket.SYMLINK] = ''
  1186. resp = self.__do_object('GET', symlink_key, params=params, headers=headers)
  1187. logger.debug("Get symlink done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1188. return GetSymlinkResult(resp)
  1189. def create_bucket(self, permission=None, input=None):
  1190. """创建新的Bucket。
  1191. :param str permission: 指定Bucket的ACL。可以是oss2.BUCKET_ACL_PRIVATE(推荐、缺省)、oss2.BUCKET_ACL_PUBLIC_READ或是
  1192. oss2.BUCKET_ACL_PUBLIC_READ_WRITE。
  1193. :param input: :class:`BucketCreateConfig <oss2.models.BucketCreateConfig>` object
  1194. """
  1195. if permission:
  1196. headers = {OSS_CANNED_ACL: permission}
  1197. else:
  1198. headers = None
  1199. data = self.__convert_data(BucketCreateConfig, xml_utils.to_put_bucket_config, input)
  1200. logger.debug("Start to create bucket, bucket: {0}, permission: {1}, config: {2}".format(self.bucket_name,
  1201. permission, data))
  1202. resp = self.__do_bucket('PUT', headers=headers, data=data)
  1203. logger.debug("Create bucket done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1204. return RequestResult(resp)
  1205. def delete_bucket(self):
  1206. """删除一个Bucket。只有没有任何文件,也没有任何未完成的分片上传的Bucket才能被删除。
  1207. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1208. ":raises: 如果试图删除一个非空Bucket,则抛出 :class:`BucketNotEmpty <oss2.exceptions.BucketNotEmpty>`
  1209. """
  1210. logger.info("Start to delete bucket, bucket: {0}".format(self.bucket_name))
  1211. resp = self.__do_bucket('DELETE')
  1212. logger.debug("Delete bucket done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1213. return RequestResult(resp)
  1214. def put_bucket_acl(self, permission):
  1215. """设置Bucket的ACL。
  1216. :param str permission: 新的ACL,可以是oss2.BUCKET_ACL_PRIVATE、oss2.BUCKET_ACL_PUBLIC_READ或
  1217. oss2.BUCKET_ACL_PUBLIC_READ_WRITE
  1218. """
  1219. logger.debug("Start to put bucket acl, bucket: {0}, acl: {1}".format(self.bucket_name, permission))
  1220. resp = self.__do_bucket('PUT', headers={OSS_CANNED_ACL: permission}, params={Bucket.ACL: ''})
  1221. logger.debug("Put bucket acl done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1222. return RequestResult(resp)
  1223. def get_bucket_acl(self):
  1224. """获取Bucket的ACL。
  1225. :return: :class:`GetBucketAclResult <oss2.models.GetBucketAclResult>`
  1226. """
  1227. logger.debug("Start to get bucket acl, bucket: {0}".format(self.bucket_name))
  1228. resp = self.__do_bucket('GET', params={Bucket.ACL: ''})
  1229. logger.debug("Get bucket acl done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1230. return self._parse_result(resp, xml_utils.parse_get_bucket_acl, GetBucketAclResult)
  1231. def put_bucket_cors(self, input):
  1232. """设置Bucket的CORS。
  1233. :param input: :class:`BucketCors <oss2.models.BucketCors>` 对象或其他
  1234. """
  1235. data = self.__convert_data(BucketCors, xml_utils.to_put_bucket_cors, input)
  1236. logger.debug("Start to put bucket cors, bucket: {0}, cors: {1}".format(self.bucket_name, data))
  1237. resp = self.__do_bucket('PUT', data=data, params={Bucket.CORS: ''})
  1238. logger.debug("Put bucket cors done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1239. return RequestResult(resp)
  1240. def get_bucket_cors(self):
  1241. """获取Bucket的CORS配置。
  1242. :return: :class:`GetBucketCorsResult <oss2.models.GetBucketCorsResult>`
  1243. """
  1244. logger.debug("Start to get bucket CORS, bucket: {0}".format(self.bucket_name))
  1245. resp = self.__do_bucket('GET', params={Bucket.CORS: ''})
  1246. logger.debug("Get bucket CORS done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1247. return self._parse_result(resp, xml_utils.parse_get_bucket_cors, GetBucketCorsResult)
  1248. def delete_bucket_cors(self):
  1249. """删除Bucket的CORS配置。"""
  1250. logger.debug("Start to delete bucket CORS, bucket: {0}".format(self.bucket_name))
  1251. resp = self.__do_bucket('DELETE', params={Bucket.CORS: ''})
  1252. logger.debug("Delete bucket CORS done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1253. return RequestResult(resp)
  1254. def put_bucket_lifecycle(self, input):
  1255. """设置生命周期管理的配置。
  1256. :param input: :class:`BucketLifecycle <oss2.models.BucketLifecycle>` 对象或其他
  1257. """
  1258. data = self.__convert_data(BucketLifecycle, xml_utils.to_put_bucket_lifecycle, input)
  1259. logger.debug("Start to put bucket lifecycle, bucket: {0}, lifecycle: {1}".format(self.bucket_name, data))
  1260. resp = self.__do_bucket('PUT', data=data, params={Bucket.LIFECYCLE: ''})
  1261. logger.debug("Put bucket lifecycle done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1262. return RequestResult(resp)
  1263. def get_bucket_lifecycle(self):
  1264. """获取生命周期管理配置。
  1265. :return: :class:`GetBucketLifecycleResult <oss2.models.GetBucketLifecycleResult>`
  1266. :raises: 如果没有设置Lifecycle,则抛出 :class:`NoSuchLifecycle <oss2.exceptions.NoSuchLifecycle>`
  1267. """
  1268. logger.debug("Start to get bucket lifecycle, bucket: {0}".format(self.bucket_name))
  1269. resp = self.__do_bucket('GET', params={Bucket.LIFECYCLE: ''})
  1270. logger.debug("Get bucket lifecycle done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1271. return self._parse_result(resp, xml_utils.parse_get_bucket_lifecycle, GetBucketLifecycleResult)
  1272. def delete_bucket_lifecycle(self):
  1273. """删除生命周期管理配置。如果Lifecycle没有设置,也返回成功。"""
  1274. logger.debug("Start to delete bucket lifecycle, bucket: {0}".format(self.bucket_name))
  1275. resp = self.__do_bucket('DELETE', params={Bucket.LIFECYCLE: ''})
  1276. logger.debug("Delete bucket lifecycle done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1277. return RequestResult(resp)
  1278. def get_bucket_location(self):
  1279. """获取Bucket的数据中心。
  1280. :return: :class:`GetBucketLocationResult <oss2.models.GetBucketLocationResult>`
  1281. """
  1282. logger.debug("Start to get bucket location, bucket: {0}".format(self.bucket_name))
  1283. resp = self.__do_bucket('GET', params={Bucket.LOCATION: ''})
  1284. logger.debug("Get bucket location done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1285. return self._parse_result(resp, xml_utils.parse_get_bucket_location, GetBucketLocationResult)
  1286. def put_bucket_logging(self, input):
  1287. """设置Bucket的访问日志功能。
  1288. :param input: :class:`BucketLogging <oss2.models.BucketLogging>` 对象或其他
  1289. """
  1290. data = self.__convert_data(BucketLogging, xml_utils.to_put_bucket_logging, input)
  1291. logger.debug("Start to put bucket logging, bucket: {0}, logging: {1}".format(self.bucket_name, data))
  1292. resp = self.__do_bucket('PUT', data=data, params={Bucket.LOGGING: ''})
  1293. logger.debug("Put bucket logging done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1294. return RequestResult(resp)
  1295. def get_bucket_logging(self):
  1296. """获取Bucket的访问日志功能配置。
  1297. :return: :class:`GetBucketLoggingResult <oss2.models.GetBucketLoggingResult>`
  1298. """
  1299. logger.debug("Start to get bucket logging, bucket: {0}".format(self.bucket_name))
  1300. resp = self.__do_bucket('GET', params={Bucket.LOGGING: ''})
  1301. logger.debug("Get bucket logging done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1302. return self._parse_result(resp, xml_utils.parse_get_bucket_logging, GetBucketLoggingResult)
  1303. def delete_bucket_logging(self):
  1304. """关闭Bucket的访问日志功能。"""
  1305. logger.debug("Start to delete bucket loggging, bucket: {0}".format(self.bucket_name))
  1306. resp = self.__do_bucket('DELETE', params={Bucket.LOGGING: ''})
  1307. logger.debug("Put bucket lifecycle done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1308. return RequestResult(resp)
  1309. def put_bucket_referer(self, input):
  1310. """为Bucket设置防盗链。
  1311. :param input: :class:`BucketReferer <oss2.models.BucketReferer>` 对象或其他
  1312. """
  1313. data = self.__convert_data(BucketReferer, xml_utils.to_put_bucket_referer, input)
  1314. logger.debug("Start to put bucket referer, bucket: {0}, referer: {1}".format(self.bucket_name, to_string(data)))
  1315. resp = self.__do_bucket('PUT', data=data, params={Bucket.REFERER: ''})
  1316. logger.debug("Put bucket referer done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1317. return RequestResult(resp)
  1318. def get_bucket_referer(self):
  1319. """获取Bucket的防盗链配置。
  1320. :return: :class:`GetBucketRefererResult <oss2.models.GetBucketRefererResult>`
  1321. """
  1322. logger.debug("Start to get bucket referer, bucket: {0}".format(self.bucket_name))
  1323. resp = self.__do_bucket('GET', params={Bucket.REFERER: ''})
  1324. logger.debug("Get bucket referer done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1325. return self._parse_result(resp, xml_utils.parse_get_bucket_referer, GetBucketRefererResult)
  1326. def get_bucket_stat(self):
  1327. """查看Bucket的状态,目前包括bucket大小,bucket的object数量,bucket正在上传的Multipart Upload事件个数等。
  1328. :return: :class:`GetBucketStatResult <oss2.models.GetBucketStatResult>`
  1329. """
  1330. logger.debug("Start to get bucket stat, bucket: {0}".format(self.bucket_name))
  1331. resp = self.__do_bucket('GET', params={Bucket.STAT: ''})
  1332. logger.debug("Get bucket stat done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1333. return self._parse_result(resp, xml_utils.parse_get_bucket_stat, GetBucketStatResult)
  1334. def get_bucket_info(self):
  1335. """获取bucket相关信息,如创建时间,访问Endpoint,Owner与ACL等。
  1336. :return: :class:`GetBucketInfoResult <oss2.models.GetBucketInfoResult>`
  1337. """
  1338. logger.debug("Start to get bucket info, bucket: {0}".format(self.bucket_name))
  1339. resp = self.__do_bucket('GET', params={Bucket.BUCKET_INFO: ''})
  1340. logger.debug("Get bucket info done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1341. return self._parse_result(resp, xml_utils.parse_get_bucket_info, GetBucketInfoResult)
  1342. def put_bucket_website(self, input):
  1343. """为Bucket配置静态网站托管功能。
  1344. :param input: :class:`BucketWebsite <oss2.models.BucketWebsite>`
  1345. """
  1346. data = self.__convert_data(BucketWebsite, xml_utils.to_put_bucket_website, input)
  1347. headers = http.CaseInsensitiveDict()
  1348. headers['Content-MD5'] = utils.content_md5(data)
  1349. logger.debug("Start to put bucket website, bucket: {0}, website: {1}".format(self.bucket_name, to_string(data)))
  1350. resp = self.__do_bucket('PUT', data=data, params={Bucket.WEBSITE: ''}, headers=headers)
  1351. logger.debug("Put bucket website done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1352. return RequestResult(resp)
  1353. def get_bucket_website(self):
  1354. """获取Bucket的静态网站托管配置。
  1355. :return: :class:`GetBucketWebsiteResult <oss2.models.GetBucketWebsiteResult>`
  1356. :raises: 如果没有设置静态网站托管,那么就抛出 :class:`NoSuchWebsite <oss2.exceptions.NoSuchWebsite>`
  1357. """
  1358. logger.debug("Start to get bucket website, bucket: {0}".format(self.bucket_name))
  1359. resp = self.__do_bucket('GET', params={Bucket.WEBSITE: ''})
  1360. logger.debug("Get bucket website done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1361. return self._parse_result(resp, xml_utils.parse_get_bucket_website, GetBucketWebsiteResult)
  1362. def delete_bucket_website(self):
  1363. """关闭Bucket的静态网站托管功能。"""
  1364. logger.debug("Start to delete bucket website, bucket: {0}".format(self.bucket_name))
  1365. resp = self.__do_bucket('DELETE', params={Bucket.WEBSITE: ''})
  1366. logger.debug("Delete bucket website done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1367. return RequestResult(resp)
  1368. def create_live_channel(self, channel_name, input):
  1369. """创建推流直播频道
  1370. :param str channel_name: 要创建的live channel的名称
  1371. :param input: LiveChannelInfo类型,包含了live channel中的描述信息
  1372. :return: :class:`CreateLiveChannelResult <oss2.models.CreateLiveChannelResult>`
  1373. """
  1374. data = self.__convert_data(LiveChannelInfo, xml_utils.to_create_live_channel, input)
  1375. logger.debug("Start to create live-channel, bucket: {0}, channel_name: {1}, info: {2}".format(
  1376. self.bucket_name, to_string(channel_name), to_string(data)))
  1377. resp = self.__do_object('PUT', channel_name, data=data, params={Bucket.LIVE: ''})
  1378. logger.debug("Create live-channel done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1379. return self._parse_result(resp, xml_utils.parse_create_live_channel, CreateLiveChannelResult)
  1380. def delete_live_channel(self, channel_name):
  1381. """删除推流直播频道
  1382. :param str channel_name: 要删除的live channel的名称
  1383. """
  1384. logger.debug("Start to delete live-channel, bucket: {0}, live_channel: {1}".format(
  1385. self.bucket_name, to_string(channel_name)))
  1386. resp = self.__do_object('DELETE', channel_name, params={Bucket.LIVE: ''})
  1387. logger.debug("Delete live-channel done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1388. return RequestResult(resp)
  1389. def get_live_channel(self, channel_name):
  1390. """获取直播频道配置
  1391. :param str channel_name: 要获取的live channel的名称
  1392. :return: :class:`GetLiveChannelResult <oss2.models.GetLiveChannelResult>`
  1393. """
  1394. logger.debug("Start to get live-channel info: bucket: {0}, live_channel: {1}".format(
  1395. self.bucket_name, to_string(channel_name)))
  1396. resp = self.__do_object('GET', channel_name, params={Bucket.LIVE: ''})
  1397. logger.debug("Get live-channel done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1398. return self._parse_result(resp, xml_utils.parse_get_live_channel, GetLiveChannelResult)
  1399. def list_live_channel(self, prefix='', marker='', max_keys=100):
  1400. """列举出Bucket下所有符合条件的live channel
  1401. param: str prefix: list时channel_id的公共前缀
  1402. param: str marker: list时指定的起始标记
  1403. param: int max_keys: 本次list返回live channel的最大个数
  1404. return: :class:`ListLiveChannelResult <oss2.models.ListLiveChannelResult>`
  1405. """
  1406. logger.debug("Start to list live-channels, bucket: {0}, prefix: {1}, marker: {2}, max_keys: {3}".format(
  1407. self.bucket_name, to_string(prefix), to_string(marker), max_keys))
  1408. resp = self.__do_bucket('GET', params={Bucket.LIVE: '',
  1409. 'prefix': prefix,
  1410. 'marker': marker,
  1411. 'max-keys': str(max_keys)})
  1412. logger.debug("List live-channel done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1413. return self._parse_result(resp, xml_utils.parse_list_live_channel, ListLiveChannelResult)
  1414. def get_live_channel_stat(self, channel_name):
  1415. """获取live channel当前推流的状态
  1416. param str channel_name: 要获取推流状态的live channel的名称
  1417. return: :class:`GetLiveChannelStatResult <oss2.models.GetLiveChannelStatResult>`
  1418. """
  1419. logger.debug("Start to get live-channel stat, bucket: {0}, channel_name: {1}".format(
  1420. self.bucket_name, to_string(channel_name)))
  1421. resp = self.__do_object('GET', channel_name, params={Bucket.LIVE: '', Bucket.COMP: 'stat'})
  1422. logger.debug("Get live-channel stat done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1423. return self._parse_result(resp, xml_utils.parse_live_channel_stat, GetLiveChannelStatResult)
  1424. def put_live_channel_status(self, channel_name, status):
  1425. """更改live channel的status,仅能在“enabled”和“disabled”两种状态中更改
  1426. param str channel_name: 要更改status的live channel的名称
  1427. param str status: live channel的目标status
  1428. """
  1429. logger.debug("Start to put live-channel status, bucket: {0}, channel_name: {1}, status: {2}".format(
  1430. self.bucket_name, to_string(channel_name), status))
  1431. resp = self.__do_object('PUT', channel_name, params={Bucket.LIVE: '', Bucket.STATUS: status})
  1432. logger.debug("Put live-channel status done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1433. return RequestResult(resp)
  1434. def get_live_channel_history(self, channel_name):
  1435. """获取live channel中最近的最多十次的推流记录,记录中包含推流的起止时间和远端的地址
  1436. param str channel_name: 要获取最近推流记录的live channel的名称
  1437. return: :class:`GetLiveChannelHistoryResult <oss2.models.GetLiveChannelHistoryResult>`
  1438. """
  1439. logger.debug("Start to get live-channel history, bucket: {0}, channel_name: {1}".format(
  1440. self.bucket_name, to_string(channel_name)))
  1441. resp = self.__do_object('GET', channel_name, params={Bucket.LIVE: '', Bucket.COMP: 'history'})
  1442. logger.debug(
  1443. "Get live-channel history done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1444. return self._parse_result(resp, xml_utils.parse_live_channel_history, GetLiveChannelHistoryResult)
  1445. def post_vod_playlist(self, channel_name, playlist_name, start_time=0, end_time=0):
  1446. """根据指定的playlist name以及startTime和endTime生成一个点播的播放列表
  1447. param str channel_name: 要生成点播列表的live channel的名称
  1448. param str playlist_name: 要生成点播列表m3u8文件的名称
  1449. param int start_time: 点播的起始时间,Unix Time格式,可以使用int(time.time())获取
  1450. param int end_time: 点播的结束时间,Unix Time格式,可以使用int(time.time())获取
  1451. """
  1452. logger.debug("Start to post vod playlist, bucket: {0}, channel_name: {1}, playlist_name: {2}, start_time: "
  1453. "{3}, end_time: {4}".format(self.bucket_name, to_string(channel_name), playlist_name, start_time,
  1454. end_time))
  1455. key = channel_name + "/" + playlist_name
  1456. resp = self.__do_object('POST', key, params={Bucket.VOD: '', 'startTime': str(start_time),
  1457. 'endTime': str(end_time)})
  1458. logger.debug("Post vod playlist done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1459. return RequestResult(resp)
  1460. def get_vod_playlist(self, channel_name, start_time, end_time):
  1461. """查看指定时间段内的播放列表
  1462. param str channel_name: 要获取点播列表的live channel的名称
  1463. param int start_time: 点播的起始时间,Unix Time格式,可以使用int(time.time())获取
  1464. param int end_time: 点播的结束时间,Unix Time格式,可以使用int(time.time())获取
  1465. """
  1466. logger.debug("Start to get vod playlist, bucket: {0}, channel_name: {1}, start_time: "
  1467. "{2}, end_time: {3}".format(self.bucket_name, to_string(channel_name), start_time, end_time))
  1468. resp = self.__do_object('GET', channel_name, params={Bucket.VOD: '', 'startTime': str(start_time),
  1469. 'endTime': str(end_time)})
  1470. logger.debug("get vod playlist done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1471. result = GetVodPlaylistResult(resp)
  1472. return result
  1473. def process_object(self, key, process, headers=None):
  1474. """处理图片的接口,支持包括调整大小,旋转,裁剪,水印,格式转换等,支持多种方式组合处理。
  1475. :param str key: 处理的图片的对象名称
  1476. :param str process: 处理的字符串,例如"image/resize,w_100|sys/saveas,o_dGVzdC5qcGc,b_dGVzdA"
  1477. :param headers: HTTP头部
  1478. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1479. """
  1480. headers = http.CaseInsensitiveDict(headers)
  1481. logger.debug("Start to process object, bucket: {0}, key: {1}, process: {2}".format(
  1482. self.bucket_name, to_string(key), process))
  1483. process_data = "%s=%s" % (Bucket.PROCESS, process)
  1484. resp = self.__do_object('POST', key, params={Bucket.PROCESS: ''}, headers=headers, data=process_data)
  1485. logger.debug("Process object done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1486. return ProcessObjectResult(resp)
  1487. def put_object_tagging(self, key, tagging, headers=None, params=None):
  1488. """
  1489. :param str key: 上传tagging的对象名称,不能为空。
  1490. :param tagging: tag 标签内容
  1491. :type tagging: :class:`Tagging <oss2.models.Tagging>` 对象
  1492. :param headers: HTTP头部
  1493. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1494. :param dict params: HTTP请求参数
  1495. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1496. """
  1497. logger.debug("Start to put object tagging, bucket: {0}, key: {1}, tagging: {2}".format(
  1498. self.bucket_name, to_string(key), tagging))
  1499. if headers is not None:
  1500. headers = http.CaseInsensitiveDict(headers)
  1501. if params is None:
  1502. params = dict()
  1503. params[Bucket.TAGGING] = ""
  1504. data = self.__convert_data(Tagging, xml_utils.to_put_tagging, tagging)
  1505. resp = self.__do_object('PUT', key, data=data, params=params, headers=headers)
  1506. return RequestResult(resp)
  1507. def get_object_tagging(self, key, params=None, headers=None):
  1508. """
  1509. :param str key: 要获取tagging的对象名称
  1510. :param dict params: 请求参数
  1511. :param headers: HTTP头部
  1512. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1513. :return: :class:`GetTaggingResult <oss2.models.GetTaggingResult>`
  1514. """
  1515. logger.debug("Start to get object tagging, bucket: {0}, key: {1} params: {2}".format(
  1516. self.bucket_name, to_string(key), str(params)))
  1517. headers = http.CaseInsensitiveDict(headers)
  1518. if params is None:
  1519. params = dict()
  1520. params[Bucket.TAGGING] = ""
  1521. resp = self.__do_object('GET', key, params=params, headers=headers)
  1522. return self._parse_result(resp, xml_utils.parse_get_tagging, GetTaggingResult)
  1523. def delete_object_tagging(self, key, params=None, headers=None):
  1524. """
  1525. :param str key: 要删除tagging的对象名称
  1526. :param dict params: 请求参数
  1527. :param headers: HTTP头部
  1528. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1529. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1530. """
  1531. logger.debug("Start to delete object tagging, bucket: {0}, key: {1}".format(
  1532. self.bucket_name, to_string(key)))
  1533. headers = http.CaseInsensitiveDict(headers)
  1534. if params is None:
  1535. params = dict()
  1536. params[Bucket.TAGGING] = ""
  1537. resp = self.__do_object('DELETE', key, params=params, headers=headers)
  1538. logger.debug("Delete object tagging done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1539. return RequestResult(resp)
  1540. def put_bucket_encryption(self, rule):
  1541. """设置bucket加密配置。
  1542. :param rule: :class:` ServerSideEncryptionRule<oss2.models.ServerSideEncryptionRule>` 对象
  1543. """
  1544. data = self.__convert_data(ServerSideEncryptionRule, xml_utils.to_put_bucket_encryption, rule)
  1545. logger.debug("Start to put bucket encryption, bucket: {0}, rule: {1}".format(self.bucket_name, data))
  1546. resp = self.__do_bucket('PUT', data=data, params={Bucket.ENCRYPTION: ""})
  1547. logger.debug("Put bucket encryption done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1548. return RequestResult(resp)
  1549. def get_bucket_encryption(self):
  1550. """获取bucket加密配置。
  1551. :return: :class:`GetServerSideEncryptionResult <oss2.models.GetServerSideEncryptionResult>`
  1552. :raises: 如果没有设置Bucket encryption,则抛出 :class:`NoSuchServerSideEncryptionRule <oss2.exceptions.NoSuchServerSideEncryptionRule>`
  1553. """
  1554. logger.debug("Start to get bucket encryption, bucket: {0}".format(self.bucket_name))
  1555. resp = self.__do_bucket('GET', params={Bucket.ENCRYPTION: ''})
  1556. logger.debug("Get bucket encryption done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1557. return self._parse_result(resp, xml_utils.parse_get_bucket_encryption, GetServerSideEncryptionResult)
  1558. def delete_bucket_encryption(self):
  1559. """删除Bucket加密配置。如果Bucket加密没有设置,也返回成功。"""
  1560. logger.debug("Start to delete bucket encryption, bucket: {0}".format(self.bucket_name))
  1561. resp = self.__do_bucket('DELETE', params={Bucket.ENCRYPTION: ''})
  1562. logger.debug("Delete bucket encryption done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1563. return RequestResult(resp)
  1564. def put_bucket_tagging(self, tagging, headers=None):
  1565. """
  1566. :param str key: 上传tagging的对象名称,不能为空。
  1567. :param tagging: tag 标签内容
  1568. :type tagging: :class:`Tagging <oss2.models.Tagging>` 对象
  1569. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1570. """
  1571. logger.debug("Start to put object tagging, bucket: {0} tagging: {1}".format(
  1572. self.bucket_name, tagging))
  1573. headers = http.CaseInsensitiveDict(headers)
  1574. data = self.__convert_data(Tagging, xml_utils.to_put_tagging, tagging)
  1575. resp = self.__do_bucket('PUT', data=data, params={Bucket.TAGGING: ''}, headers=headers)
  1576. logger.debug("Put bucket tagging done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1577. return RequestResult(resp)
  1578. def get_bucket_tagging(self):
  1579. """
  1580. :param str key: 要获取tagging的对象名称
  1581. :param dict params: 请求参数
  1582. :return: :class:`GetTaggingResult<oss2.models.GetTaggingResult>`
  1583. """
  1584. logger.debug("Start to get bucket tagging, bucket: {0}".format(
  1585. self.bucket_name))
  1586. resp = self.__do_bucket('GET', params={Bucket.TAGGING: ''})
  1587. logger.debug("Get bucket tagging done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1588. return self._parse_result(resp, xml_utils.parse_get_tagging, GetTaggingResult)
  1589. def delete_bucket_tagging(self):
  1590. """
  1591. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1592. """
  1593. logger.debug("Start to delete bucket tagging, bucket: {0}".format(
  1594. self.bucket_name))
  1595. resp = self.__do_bucket('DELETE', params={Bucket.TAGGING: ''})
  1596. logger.debug("Delete bucket tagging done, req_id: {0}, status_code: {1}".format(
  1597. resp.request_id, resp.status))
  1598. return RequestResult(resp)
  1599. def list_object_versions(self, prefix='', delimiter='', key_marker='',
  1600. max_keys=100, versionid_marker='', headers=None):
  1601. """根据前缀罗列Bucket里的文件的版本。
  1602. :param str prefix: 只罗列文件名为该前缀的文件
  1603. :param str delimiter: 分隔符。可以用来模拟目录
  1604. :param str key_marker: 分页标志。首次调用传空串,后续使用返回值的next_marker
  1605. :param int max_keys: 最多返回文件的个数,文件和目录的和不能超过该值
  1606. :param str versionid_marker: 设定结果从key-marker对象的
  1607. versionid-marker之后按新旧版本排序开始返回,该版本不会在返回的结果当中。
  1608. :param headers: HTTP头部
  1609. :type headers: 可以是dict,建议是oss2.CaseInsensitiveDict
  1610. :return: :class:`ListObjectVersionsResult <oss2.models.ListObjectVersionsResult>`
  1611. """
  1612. logger.debug(
  1613. "Start to List object versions, bucket: {0}, prefix: {1}, delimiter: {2},"
  1614. +"key_marker: {3}, versionid_marker: {4}, max-keys: {5}".format(
  1615. self.bucket_name, to_string(prefix), delimiter, to_string(key_marker),
  1616. to_string(versionid_marker), max_keys))
  1617. headers = http.CaseInsensitiveDict(headers)
  1618. resp = self.__do_bucket('GET',
  1619. params={'prefix': prefix,
  1620. 'delimiter': delimiter,
  1621. 'key-marker': key_marker,
  1622. 'version-id-marker': versionid_marker,
  1623. 'max-keys': str(max_keys),
  1624. 'encoding-type': 'url',
  1625. Bucket.VERSIONS: ''},
  1626. headers=headers)
  1627. logger.debug("List object versions done, req_id: {0}, status_code: {1}"
  1628. .format(resp.request_id, resp.status))
  1629. return self._parse_result(resp, xml_utils.parse_list_object_versions, ListObjectVersionsResult)
  1630. def put_bucket_versioning(self, config, headers=None):
  1631. """
  1632. :param str operation: 设置bucket是否开启多版本特性,可取值为:[Enabled,Suspend]
  1633. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1634. """
  1635. logger.debug("Start to put object versioning, bucket: {0}".format(self.bucket_name))
  1636. data = self.__convert_data(BucketVersioningConfig, xml_utils.to_put_bucket_versioning, config)
  1637. headers = http.CaseInsensitiveDict(headers)
  1638. headers['Content-MD5'] = utils.content_md5(data)
  1639. resp = self.__do_bucket('PUT', data=data, params={Bucket.VERSIONING: ''}, headers=headers)
  1640. logger.debug("Put bucket versiong done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1641. return RequestResult(resp)
  1642. def get_bucket_versioning(self):
  1643. """
  1644. :return: :class:`GetBucketVersioningResult<oss2.models.GetBucketVersioningResult>`
  1645. """
  1646. logger.debug("Start to get bucket versioning, bucket: {0}".format(self.bucket_name))
  1647. resp = self.__do_bucket('GET', params={Bucket.VERSIONING: ''})
  1648. logger.debug("Get bucket versiong done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1649. return self._parse_result(resp, xml_utils.parse_get_bucket_versioning, GetBucketVersioningResult)
  1650. def put_bucket_policy(self, policy):
  1651. """设置bucket授权策略, 具体policy书写规则请参考官方文档
  1652. :param str policy: 授权策略
  1653. """
  1654. logger.debug("Start to put bucket policy, bucket: {0}, policy: {1}".format(self.bucket_name, policy))
  1655. resp = self.__do_bucket('PUT', data=policy, params={Bucket.POLICY: ''}, headers={'Content-MD5': utils.content_md5(policy)})
  1656. logger.debug("Put bucket policy done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1657. return RequestResult(resp)
  1658. def get_bucket_policy(self):
  1659. """获取bucket授权策略
  1660. :return: :class:`GetBucketPolicyResult <oss2.models.GetBucketPolicyResult>`
  1661. """
  1662. logger.debug("Start to get bucket policy, bucket: {0}".format(self.bucket_name))
  1663. resp = self.__do_bucket('GET', params={Bucket.POLICY:''})
  1664. logger.debug("Get bucket policy done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1665. return GetBucketPolicyResult(resp)
  1666. def delete_bucket_policy(self):
  1667. """删除bucket授权策略
  1668. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1669. """
  1670. logger.debug("Start to delete bucket policy, bucket: {0}".format(self.bucket_name))
  1671. resp = self.__do_bucket('DELETE', params={Bucket.POLICY: ''})
  1672. logger.debug("Delete bucket policy done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1673. return RequestResult(resp)
  1674. def put_bucket_request_payment(self, payer):
  1675. """设置付费者。
  1676. :param input: :class: str
  1677. """
  1678. data = xml_utils.to_put_bucket_request_payment(payer)
  1679. logger.debug("Start to put bucket request payment, bucket: {0}, payer: {1}".format(self.bucket_name, payer))
  1680. resp = self.__do_bucket('PUT', data=data, params={Bucket.REQUESTPAYMENT: ''}, headers={'Content-MD5': utils.content_md5(data)})
  1681. logger.debug("Put bucket request payment done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1682. return RequestResult(resp)
  1683. def get_bucket_request_payment(self):
  1684. """获取付费者设置。
  1685. :return: :class:`GetBucketRequestPaymentResult <oss2.models.GetBucketRequestPaymentResult>`
  1686. """
  1687. logger.debug("Start to get bucket request payment, bucket: {0}.".format(self.bucket_name))
  1688. resp = self.__do_bucket('GET', params={Bucket.REQUESTPAYMENT: ''})
  1689. logger.debug("Get bucket request payment done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1690. return self._parse_result(resp, xml_utils.parse_get_bucket_request_payment, GetBucketRequestPaymentResult)
  1691. def put_bucket_qos_info(self, bucket_qos_info):
  1692. """配置bucket的QoSInfo
  1693. :param bucket_qos_info :class:`BucketQosInfo <oss2.models.BucketQosInfo>`
  1694. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1695. """
  1696. logger.debug("Start to put bucket qos info, bucket: {0}".format(self.bucket_name))
  1697. data = self.__convert_data(BucketQosInfo, xml_utils.to_put_qos_info, bucket_qos_info)
  1698. headers = http.CaseInsensitiveDict()
  1699. headers['Content-MD5'] = utils.content_md5(data)
  1700. resp = self.__do_bucket('PUT', data=data, params={Bucket.QOS_INFO: ''}, headers=headers)
  1701. logger.debug("Get bucket qos info done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1702. return RequestResult(resp)
  1703. def get_bucket_qos_info(self):
  1704. """获取bucket的QoSInfo
  1705. :return: :class:`GetBucketQosInfoResult <oss2.models.GetBucketQosInfoResult>`
  1706. """
  1707. logger.debug("Start to get bucket qos info, bucket: {0}".format(self.bucket_name))
  1708. resp = self.__do_bucket('GET', params={Bucket.QOS_INFO: ''})
  1709. logger.debug("Get bucket qos info, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1710. return self._parse_result(resp, xml_utils.parse_get_qos_info, GetBucketQosInfoResult)
  1711. def delete_bucket_qos_info(self):
  1712. """删除bucket的QoSInfo
  1713. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1714. """
  1715. logger.debug("Start to delete bucket qos info, bucket: {0}".format(self.bucket_name))
  1716. resp = self.__do_bucket('DELETE', params={Bucket.QOS_INFO: ''})
  1717. logger.debug("Delete bucket qos info done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1718. return RequestResult(resp)
  1719. def set_bucket_storage_capacity(self, user_qos):
  1720. """设置Bucket的容量,单位GB
  1721. :param user_qos :class:`BucketUserQos <oss2.models.BucketUserQos>`
  1722. """
  1723. logger.debug("Start to set bucket storage capacity: {0}".format(self.bucket_name))
  1724. data = xml_utils.to_put_bucket_user_qos(user_qos)
  1725. resp = self.__do_bucket('PUT', data=data, params={Bucket.USER_QOS: ''})
  1726. logger.debug("Set bucket storage capacity done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1727. return RequestResult(resp)
  1728. def get_bucket_storage_capacity(self):
  1729. """获取bucket的容量信息。
  1730. :return: :class:`GetBucketUserQosResult <oss2.models.GetBucketUserQosResult>`
  1731. """
  1732. logger.debug("Start to get bucket storage capacity, bucket:{0}".format(self.bucket_name))
  1733. resp = self._Bucket__do_bucket('GET', params={Bucket.USER_QOS: ''})
  1734. logger.debug("Get bucket storage capacity done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1735. return self._parse_result(resp, xml_utils.parse_get_bucket_user_qos, GetBucketUserQosResult)
  1736. def put_async_fetch_task(self, task_config):
  1737. """创建一个异步获取文件到bucket的任务。
  1738. :param task_config: 任务配置
  1739. :type task_config: class:`AsyncFetchTaskConfiguration <oss2.models.AsyncFetchTaskConfiguration>`
  1740. :return: :class:`PutAsyncFetchTaskResult <oss2.models.PutAsyncFetchTaskResult>`
  1741. """
  1742. logger.debug("Start to put async fetch task, bucket:{0}".format(self.bucket_name))
  1743. data = xml_utils.to_put_async_fetch_task(task_config)
  1744. headers = http.CaseInsensitiveDict()
  1745. headers['Content-MD5'] = utils.content_md5(data)
  1746. resp = self._Bucket__do_bucket('POST', data=data, params={Bucket.ASYNC_FETCH: ''}, headers=headers)
  1747. logger.debug("Put async fetch task done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1748. return self._parse_result(resp, xml_utils.parse_put_async_fetch_task_result, PutAsyncFetchTaskResult)
  1749. def get_async_fetch_task(self, task_id):
  1750. """获取一个异步获取文件到bucket的任务信息。
  1751. :param str task_id: 任务id
  1752. :return: :class:`GetAsyncFetchTaskResult <oss2.models.GetAsyncFetchTaskResult>`
  1753. """
  1754. logger.debug("Start to get async fetch task, bucket:{0}, task_id:{1}".format(self.bucket_name, task_id))
  1755. resp = self._Bucket__do_bucket('GET', headers={OSS_TASK_ID: task_id}, params={Bucket.ASYNC_FETCH: ''})
  1756. logger.debug("Put async fetch task done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1757. return self._parse_result(resp, xml_utils.parse_get_async_fetch_task_result, GetAsyncFetchTaskResult)
  1758. def put_bucket_inventory_configuration(self, inventory_configuration):
  1759. """设置bucket清单配置
  1760. :param inventory_configuration :class:`InventoryConfiguration <oss2.models.InventoryConfiguration>`
  1761. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1762. """
  1763. logger.debug("Start to put bucket inventory configuration, bucket: {0}".format(self.bucket_name))
  1764. data = self.__convert_data(InventoryConfiguration, xml_utils.to_put_inventory_configuration, inventory_configuration)
  1765. headers = http.CaseInsensitiveDict()
  1766. headers['Content-MD5'] = utils.content_md5(data)
  1767. resp = self.__do_bucket('PUT', data=data, params={Bucket.INVENTORY: '', Bucket.INVENTORY_CONFIG_ID:inventory_configuration.inventory_id}, headers=headers)
  1768. logger.debug("Put bucket inventory configuration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1769. return RequestResult(resp)
  1770. def get_bucket_inventory_configuration(self, inventory_id):
  1771. """获取指定的清单配置。
  1772. :param str inventory_id : 清单配置id
  1773. :return: :class:`GetInventoryConfigurationResult <oss2.models.GetInventoryConfigurationResult>`
  1774. """
  1775. logger.debug("Start to get bucket inventory configuration, bucket: {0}".format(self.bucket_name))
  1776. resp = self.__do_bucket('GET', params={Bucket.INVENTORY: '', Bucket.INVENTORY_CONFIG_ID:inventory_id})
  1777. logger.debug("Get bucket inventory cinfguration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1778. return self._parse_result(resp, xml_utils.parse_get_bucket_inventory_configuration, GetInventoryConfigurationResult)
  1779. def list_bucket_inventory_configurations(self, continuation_token=None):
  1780. """罗列清单配置,默认单次最大返回100条配置,如果存在超过100条配置,罗列结果将会分页,
  1781. 分页信息保存在 class:`ListInventoryConfigurationResult <oss2.models.ListInventoryConfigurationResult>`中。
  1782. :param str continuation_token: 分页标识, 默认值为None, 如果上次罗列不完整,这里设置为上次罗列结果中的next_continuation_token值。
  1783. :return: :class:`ListInventoryConfigurationResult <oss2.models.ListInventoryConfigurationResult>`
  1784. """
  1785. logger.debug("Start to list bucket inventory configuration, bucket: {0}".format(self.bucket_name))
  1786. params = {Bucket.INVENTORY:''}
  1787. if continuation_token is not None:
  1788. params[Bucket.CONTINUATION_TOKEN] = continuation_token
  1789. resp = self.__do_bucket('GET', params=params)
  1790. logger.debug("List bucket inventory configuration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1791. return self._parse_result(resp, xml_utils.parse_list_bucket_inventory_configurations, ListInventoryConfigurationsResult)
  1792. def delete_bucket_inventory_configuration(self, inventory_id):
  1793. """删除指定的清单配置
  1794. :param str inventory_id : 清单配置id
  1795. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1796. """
  1797. logger.debug("Start to delete bucket inventory configuration, bucket: {0}, configuration id: {1}.".format(self.bucket_name, inventory_id))
  1798. resp = self.__do_bucket('DELETE', params={Bucket.INVENTORY:'', Bucket.INVENTORY_CONFIG_ID:inventory_id})
  1799. logger.debug("Delete bucket inventory configuration, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1800. return RequestResult(resp)
  1801. def init_bucket_worm(self, retention_period_days=None):
  1802. """创建一条合规保留策略
  1803. :param int retention_period_days : 指定object的保留天数
  1804. :return: :class:`InitBucketWormResult <oss2.models.InitBucketWormResult>`
  1805. """
  1806. logger.debug("Start to init bucket worm, bucket: {0}, retention_period_days: {1}."
  1807. .format(self.bucket_name, retention_period_days))
  1808. data = xml_utils.to_put_init_bucket_worm(retention_period_days)
  1809. headers = http.CaseInsensitiveDict()
  1810. headers['Content-MD5'] = utils.content_md5(data)
  1811. resp = self.__do_bucket('POST', data=data, params={Bucket.WORM: ''}, headers=headers)
  1812. logger.debug("init bucket worm done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1813. result = InitBucketWormResult(resp)
  1814. result.worm_id = resp.headers.get("x-oss-worm-id")
  1815. return result
  1816. def abort_bucket_worm(self):
  1817. """删除一条合规保留策略
  1818. 只有未锁定保留策略的状态下才能删除,一旦锁定bucket数据将处于保护状态。
  1819. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1820. """
  1821. logger.debug("Start to abort bucket worm, bucket: {0}.".format(self.bucket_name))
  1822. resp = self.__do_bucket('DELETE', params={Bucket.WORM: ''})
  1823. logger.debug("abort bucket worm done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1824. return RequestResult(resp)
  1825. def complete_bucket_worm(self, worm_id=None):
  1826. """锁定一条合规保留策略
  1827. :param str worm_id : 合规保留策略的id。
  1828. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1829. """
  1830. logger.debug("Start to complete bucket worm, bucket: {0}, worm_id: {1}.".format(self.bucket_name, worm_id))
  1831. resp = self.__do_bucket('POST', params={Bucket.WORM_ID: worm_id})
  1832. logger.debug("complete bucket worm done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1833. return RequestResult(resp)
  1834. def extend_bucket_worm(self, worm_id=None, retention_period_days=None):
  1835. """延长已经锁定的合规保留策略的object保护天数。
  1836. :param str worm_id : 合规保留策略的id。
  1837. :param int retention_period_days : 指定object的保留天数
  1838. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1839. """
  1840. data = xml_utils.to_put_extend_bucket_worm(retention_period_days)
  1841. headers = http.CaseInsensitiveDict()
  1842. headers['Content-MD5'] = utils.content_md5(data)
  1843. logger.debug("Start to extend bucket worm, bucket: {0}, worm_id: {1}, retention_period_days."
  1844. .format(self.bucket_name, worm_id, retention_period_days))
  1845. resp = self.__do_bucket('POST', data=data, params={Bucket.WORM_ID: worm_id, Bucket.WORM_EXTEND: ''}, headers=headers)
  1846. logger.debug("extend bucket worm done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1847. return RequestResult(resp)
  1848. def get_bucket_worm(self):
  1849. """获取合规保留策略
  1850. :return: :class:`GetBucketWormResult <oss2.models.GetBucketWormResult>`
  1851. """
  1852. logger.debug("Start to get bucket worm, bucket: {0}.".format(self.bucket_name))
  1853. resp = self.__do_bucket('GET', params={Bucket.WORM: ''})
  1854. logger.debug("get bucket worm done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1855. return self._parse_result(resp, xml_utils.parse_get_bucket_worm_result, GetBucketWormResult)
  1856. def put_bucket_replication(self, rule):
  1857. """设置bucket跨区域复制规则
  1858. :param rule :class:`ReplicationRule <oss2.models.ReplicationRule>`
  1859. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1860. """
  1861. logger.debug("Start to put bucket replication: {0}".format(self.bucket_name))
  1862. data = xml_utils.to_put_bucket_replication(rule)
  1863. headers = http.CaseInsensitiveDict()
  1864. headers['Content-MD5'] = utils.content_md5(data)
  1865. resp = self.__do_bucket('POST', data=data, params={Bucket.REPLICATION: '', 'comp': 'add'}, headers=headers)
  1866. logger.debug("Put bucket replication done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1867. return RequestResult(resp)
  1868. def get_bucket_replication(self):
  1869. """获取bucket跨区域复制规则
  1870. :return: :class:`GetBucketReplicationResult <oss2.models.GetBucketReplicationResult>`
  1871. """
  1872. logger.debug("Start to get bucket replication: {0}".format(self.bucket_name))
  1873. resp = self.__do_bucket('GET', params={Bucket.REPLICATION: ''})
  1874. logger.debug("Get bucket replication done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1875. return self._parse_result(resp, xml_utils.parse_get_bucket_replication_result, GetBucketReplicationResult)
  1876. def delete_bucket_replication(self, rule_id):
  1877. """停止Bucket的跨区域复制并删除Bucket的复制配置
  1878. :param str rule_id: Bucket跨区域复制规则的id。
  1879. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1880. """
  1881. logger.debug("Start to delete bucket replication: {0}".format(self.bucket_name))
  1882. data = xml_utils.to_delete_bucket_replication(rule_id)
  1883. headers = http.CaseInsensitiveDict()
  1884. headers['Content-MD5'] = utils.content_md5(data)
  1885. resp = self.__do_bucket('POST', data=data, params={Bucket.REPLICATION: '', 'comp': 'delete'}, headers=headers)
  1886. logger.debug("Delete bucket replication done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1887. return RequestResult(resp)
  1888. def get_bucket_replication_location(self):
  1889. """获取可复制到的Bucket所在的地域
  1890. :return: :class:`ReplicationLocation <oss2.models.GetBucketReplicationLocationResult>`
  1891. """
  1892. logger.debug("Start to get bucket replication location: {0}".format(self.bucket_name))
  1893. resp = self.__do_bucket('GET', params={Bucket.REPLICATION_LOCATION: ''})
  1894. logger.debug("Get bucket replication location done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1895. return self._parse_result(resp, xml_utils.parse_get_bucket_replication_location_result, GetBucketReplicationLocationResult)
  1896. def get_bucket_replication_progress(self, rule_id):
  1897. """获取获取某个Bucket的跨区域复制进度
  1898. :param str rule_id: Bucket跨区域复制规则的id。
  1899. :return: :class:`GetBucketReplicationProgressResult <oss2.models.GetBucketReplicationProgressResult>`
  1900. """
  1901. logger.debug("Start to get bucket replication progress: {0}".format(self.bucket_name))
  1902. resp = self.__do_bucket('GET', params={Bucket.REPLICATION_PROGRESS: '', 'rule-id': rule_id})
  1903. logger.debug("Get bucket replication progress done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1904. return self._parse_result(resp, xml_utils.parse_get_bucket_replication_progress_result, GetBucketReplicationProgressResult)
  1905. def _get_bucket_config(self, config):
  1906. """获得Bucket某项配置,具体哪种配置由 `config` 指定。该接口直接返回 `RequestResult` 对象。
  1907. 通过read()接口可以获得XML字符串。不建议使用。
  1908. :param str config: 可以是 `Bucket.ACL` 、 `Bucket.LOGGING` 等。
  1909. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1910. """
  1911. logger.debug("Start to get bucket config, bucket: {0}".format(self.bucket_name))
  1912. resp = self.__do_bucket('GET', params={config: ''})
  1913. logger.debug("Get bucket config done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1914. return resp
  1915. def put_bucket_transfer_acceleration(self, enabled):
  1916. """为存储空间(Bucket)配置传输加速
  1917. :param str enabled : 是否开启传输加速。true:开启传输加速; false:关闭传输加速.
  1918. :return: :class:`RequestResult <oss2.models.RequestResult>`
  1919. """
  1920. logger.debug("Start to bucket transfer acceleration, bucket: {0}, enabled: {1}."
  1921. .format(self.bucket_name, enabled))
  1922. data = xml_utils.to_put_bucket_transfer_acceleration(enabled)
  1923. headers = http.CaseInsensitiveDict()
  1924. headers['Content-MD5'] = utils.content_md5(data)
  1925. resp = self.__do_bucket('PUT', data=data, params={Bucket.TRANSFER_ACCELERATION: ''}, headers=headers)
  1926. logger.debug("bucket transfer acceleration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1927. return RequestResult(resp)
  1928. def get_bucket_transfer_acceleration(self):
  1929. """获取目标存储空间(Bucket)的传输加速配置
  1930. :return: :class:`GetBucketReplicationResult <oss2.models.GetBucketReplicationResult>`
  1931. """
  1932. logger.debug("Start to get bucket transfer acceleration: {0}".format(self.bucket_name))
  1933. resp = self.__do_bucket('GET', params={Bucket.TRANSFER_ACCELERATION: ''})
  1934. logger.debug("Get bucket transfer acceleration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
  1935. return self._parse_result(resp, xml_utils.parse_get_bucket_transfer_acceleration_result, GetBucketTransferAccelerationResult)
  1936. def __do_object(self, method, key, **kwargs):
  1937. return self._do(method, self.bucket_name, key, **kwargs)
  1938. def __do_bucket(self, method, **kwargs):
  1939. return self._do(method, self.bucket_name, '', **kwargs)
  1940. def __convert_data(self, klass, converter, data):
  1941. if isinstance(data, klass):
  1942. return converter(data)
  1943. else:
  1944. return data
  1945. def _normalize_endpoint(endpoint):
  1946. url = endpoint
  1947. if not endpoint.startswith('http://') and not endpoint.startswith('https://'):
  1948. url = 'http://' + endpoint
  1949. p = urlparse(url)
  1950. if p.port is not None:
  1951. return p.scheme + '://' + p.hostname + ':' + str(p.port)
  1952. else:
  1953. return p.scheme + '://' + p.hostname
  1954. _ENDPOINT_TYPE_ALIYUN = 0
  1955. _ENDPOINT_TYPE_CNAME = 1
  1956. _ENDPOINT_TYPE_IP = 2
  1957. def _make_range_string(range):
  1958. if range is None:
  1959. return ''
  1960. start = range[0]
  1961. last = range[1]
  1962. if start is None and last is None:
  1963. return ''
  1964. return 'bytes=' + _range(start, last)
  1965. def _range(start, last):
  1966. def to_str(pos):
  1967. if pos is None:
  1968. return ''
  1969. else:
  1970. return str(pos)
  1971. return to_str(start) + '-' + to_str(last)
  1972. def _determine_endpoint_type(netloc, is_cname, bucket_name):
  1973. if utils.is_ip_or_localhost(netloc):
  1974. return _ENDPOINT_TYPE_IP
  1975. if is_cname:
  1976. return _ENDPOINT_TYPE_CNAME
  1977. if utils.is_valid_bucket_name(bucket_name):
  1978. return _ENDPOINT_TYPE_ALIYUN
  1979. else:
  1980. return _ENDPOINT_TYPE_IP
  1981. class _UrlMaker(object):
  1982. def __init__(self, endpoint, is_cname):
  1983. p = urlparse(endpoint)
  1984. self.scheme = p.scheme
  1985. self.netloc = p.netloc
  1986. self.is_cname = is_cname
  1987. def __call__(self, bucket_name, key, slash_safe=False):
  1988. self.type = _determine_endpoint_type(self.netloc, self.is_cname, bucket_name)
  1989. safe = '/' if slash_safe is True else ''
  1990. key = urlquote(key, safe=safe)
  1991. if self.type == _ENDPOINT_TYPE_CNAME:
  1992. return '{0}://{1}/{2}'.format(self.scheme, self.netloc, key)
  1993. if self.type == _ENDPOINT_TYPE_IP:
  1994. if bucket_name:
  1995. return '{0}://{1}/{2}/{3}'.format(self.scheme, self.netloc, bucket_name, key)
  1996. else:
  1997. return '{0}://{1}/{2}'.format(self.scheme, self.netloc, key)
  1998. if not bucket_name:
  1999. assert not key
  2000. return '{0}://{1}'.format(self.scheme, self.netloc)
  2001. return '{0}://{1}.{2}/{3}'.format(self.scheme, bucket_name, self.netloc, key)