Skip to content

Commit 554dfa3

Browse files
liyanzhang505huiguangjun
authored andcommitted
1, Support object versions lifecycle.
2, Fix resumable download file checkpoint filename with versionid if it specifed.
1 parent 6b24a72 commit 554dfa3

File tree

4 files changed

+246
-16
lines changed

4 files changed

+246
-16
lines changed

oss2/models.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -776,27 +776,36 @@ class LifecycleExpiration(object):
776776
"""过期删除操作。
777777
778778
:param days: 表示在文件修改后过了这么多天,就会匹配规则,从而被删除
779-
:param date: 表示在该日期之后,规则就一直生效。即每天都会对符合前缀的文件执行删除操作(如,删除),而不管文件是什么时候生成的。
780-
*不建议使用*
781-
:param created_before_date: delete files if their last modified time earlier than created_before_date
779+
:type days: int
782780
781+
:param date: 表示在该日期之后,规则就一直生效。即每天都会对符合前缀的文件执行删除操作(如,删除),而不管文件是什么时候生成的。*不建议使用*
783782
:type date: `datetime.date`
783+
784+
:param created_before_date: delete files if their last modified time earlier than created_before_date
785+
:type created_before_date: `datetime.date`
786+
787+
:param expired_detete_marker: 真实文件删除之后是否自动移除删除标记,适用于多版本场景。
788+
:param expired_detete_marker: bool
789+
784790
"""
785-
def __init__(self, days=None, date=None, created_before_date=None):
791+
def __init__(self, days=None, date=None, created_before_date=None, expired_detete_marker=None):
786792
not_none_fields = 0
787793
if days is not None:
788794
not_none_fields += 1
789795
if date is not None:
790796
not_none_fields += 1
791797
if created_before_date is not None:
792798
not_none_fields += 1
799+
if expired_detete_marker is not None:
800+
not_none_fields += 1
793801

794802
if not_none_fields > 1:
795-
raise ClientError('More than one field(days, date and created_before_date) has been specified')
803+
raise ClientError('More than one field(days, date and created_before_date, expired_detete_marker) has been specified')
796804

797805
self.days = days
798806
self.date = date
799807
self.created_before_date = created_before_date
808+
self.expired_detete_marker = expired_detete_marker
800809

801810

802811
class AbortMultipartUpload(object):
@@ -830,18 +839,52 @@ def __init__(self, days=None, created_before_date=None, storage_class=None):
830839
self.storage_class = storage_class
831840

832841

842+
class NoncurrentVersionExpiration(object):
843+
"""OSS何时将非当前版本的object删除
844+
845+
:param noncurrent_days: 指定多少天之后删除
846+
:type noncurrent_days: int
847+
"""
848+
def __init__(self, noncurrent_days):
849+
self.noncurrent_days = noncurrent_days
850+
851+
852+
class NoncurrentVersionStorageTransition(object):
853+
"""生命周期内,OSS何时将指定Object的非当前版本转储为IA或者Archive存储类型。
854+
855+
:param noncurrent_days: 多少天之后转存储
856+
:type noncurrent_days: int
857+
"""
858+
def __init__(self, noncurrent_days, storage_class):
859+
self.noncurrent_days = noncurrent_days
860+
self.storage_class = storage_class
861+
862+
833863
class LifecycleRule(object):
834864
"""生命周期规则。
835865
836866
:param id: 规则名
867+
:type id: str
868+
837869
:param prefix: 只有文件名匹配该前缀的文件才适用本规则
870+
:type prefix: str
871+
838872
:param expiration: 过期删除操作。
839873
:type expiration: :class:`LifecycleExpiration`
874+
840875
:param status: 启用还是禁止该规则。可选值为 `LifecycleRule.ENABLED` 或 `LifecycleRule.DISABLED`
876+
841877
:param storage_transitions: 存储类型转换规则
842-
:type storage_transitions: :class:`StorageTransition`
878+
:type storage_transitions: list of class:`StorageTransition <oss2.models.StorageTransition>`
879+
843880
:param tagging: object tagging 规则
844-
:type tagging: :class:`Tagging`
881+
:type tagging: :class:`Tagging <oss2.models.StorageTransition>`
882+
883+
:param noncurrent_version_expiration: 指定Object非当前版本生命周期规则的过期属性。适用于多版本场景。
884+
:type noncurrent_version_expiration class:`NoncurrentVersionExpiration <oss2.models.NoncurrentVersionExpiration>`
885+
886+
:param noncurrent_version_sotrage_transitions: 在有效生命周期中,OSS何时将指定Object的非当前版本转储为IA或者Archive存储类型,适用于多版本场景。
887+
:type noncurrent_version_sotrage_transitions: list of class:`NoncurrentVersionStorageTransition <oss2.models.NoncurrentVersionStorageTransition>`
845888
"""
846889

847890
ENABLED = 'Enabled'
@@ -850,21 +893,25 @@ class LifecycleRule(object):
850893
def __init__(self, id, prefix,
851894
status=ENABLED, expiration=None,
852895
abort_multipart_upload=None,
853-
storage_transitions=None, tagging=None):
896+
storage_transitions=None, tagging=None,
897+
noncurrent_version_expiration=None,
898+
noncurrent_version_sotrage_transitions=None):
854899
self.id = id
855900
self.prefix = prefix
856901
self.status = status
857902
self.expiration = expiration
858903
self.abort_multipart_upload = abort_multipart_upload
859904
self.storage_transitions = storage_transitions
860905
self.tagging = tagging
906+
self.noncurrent_version_expiration = noncurrent_version_expiration
907+
self.noncurrent_version_sotrage_transitions = noncurrent_version_sotrage_transitions
861908

862909

863910
class BucketLifecycle(object):
864911
"""Bucket的生命周期配置。
865912
866913
:param rules: 规则列表,
867-
:type rules: list of :class:`LifecycleRule`
914+
:type rules: list of :class:`LifecycleRule <oss2.models.LifecycleRule>`
868915
"""
869916
def __init__(self, rules=None):
870917
self.rules = rules or []

oss2/resumable.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ def _populate_valid_headers(headers=None, valid_keys=None):
240240

241241
class _ResumableOperation(object):
242242
def __init__(self, bucket, key, filename, size, store,
243-
progress_callback=None):
243+
progress_callback=None, versionid=None):
244244
self.bucket = bucket
245245
self.key = to_string(key)
246246
self.filename = filename
@@ -249,7 +249,12 @@ def __init__(self, bucket, key, filename, size, store,
249249
self._abspath = os.path.abspath(filename)
250250

251251
self.__store = store
252-
self.__record_key = self.__store.make_store_key(bucket.bucket_name, self.key, self._abspath)
252+
253+
if versionid is None:
254+
self.__record_key = self.__store.make_store_key(bucket.bucket_name, self.key, self._abspath)
255+
else:
256+
self.__record_key = self.__store.make_store_key(bucket.bucket_name, self.key, self._abspath, versionid)
257+
253258
logger.debug("Init _ResumableOperation, record_key: {0}".format(self.__record_key))
254259

255260
# protect self.__progress_callback
@@ -295,9 +300,13 @@ def __init__(self, bucket, key, filename, objectInfo,
295300
num_threads=None,
296301
params=None,
297302
headers=None):
303+
versionid = None
304+
if params is not None and params.get('versionId') is not None:
305+
versionid = params.get('versionId')
298306
super(_ResumableDownloader, self).__init__(bucket, key, filename, objectInfo.size,
299307
store or ResumableDownloadStore(),
300-
progress_callback=progress_callback)
308+
progress_callback=progress_callback,
309+
versionid=versionid)
301310
self.objectInfo = objectInfo
302311

303312
self.__part_size = defaults.get(part_size, defaults.multiget_part_size)
@@ -718,10 +727,15 @@ def __init__(self, root=None, dir=None):
718727
super(ResumableDownloadStore, self).__init__(root or os.path.expanduser('~'), dir or _DOWNLOAD_TEMP_DIR)
719728

720729
@staticmethod
721-
def make_store_key(bucket_name, key, filename):
730+
def make_store_key(bucket_name, key, filename, versionid=None):
722731
filepath = _normalize_path(filename)
732+
oss_pathname = None
723733

724-
oss_pathname = 'oss://{0}/{1}'.format(bucket_name, key)
734+
if versionid is None:
735+
oss_pathname = 'oss://{0}/{1}'.format(bucket_name, key)
736+
else:
737+
oss_pathname = 'oss://{0}/{1}?versionid={2}'.format(bucket_name, key, versionid)
738+
725739
return utils.md5_string(oss_pathname) + '-' + utils.md5_string(filepath) + '-download'
726740

727741

oss2/xml_utils.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
REDIRECT_TYPE_MIRROR,
4848
REDIRECT_TYPE_EXTERNAL,
4949
REDIRECT_TYPE_INTERNAL,
50-
REDIRECT_TYPE_ALICDN)
50+
REDIRECT_TYPE_ALICDN,
51+
NoncurrentVersionStorageTransition,
52+
NoncurrentVersionExpiration)
5153

5254
from .select_params import (SelectJsonTypes, SelectParameters)
5355

@@ -583,6 +585,10 @@ def parse_lifecycle_expiration(expiration_node):
583585
expiration.days = _find_int(expiration_node, 'Days')
584586
elif expiration_node.find('Date') is not None:
585587
expiration.date = iso8601_to_date(_find_tag(expiration_node, 'Date'))
588+
elif expiration_node.find('CreatedBeforeDate') is not None:
589+
expiration.created_before_date = iso8601_to_date(_find_tag(expiration_node, 'CreatedBeforeDate'))
590+
elif expiration_node.find('ExpiredObjectDeleteMarker') is not None:
591+
expiration.expired_detete_marker = _find_bool(expiration_node, 'ExpiredObjectDeleteMarker')
586592

587593
return expiration
588594

@@ -629,6 +635,25 @@ def parse_lifecycle_object_taggings(lifecycle_tagging_nodes):
629635

630636
return Tagging(tagging_rule)
631637

638+
def parse_lifecycle_version_expiration(version_expiration_node):
639+
if version_expiration_node is None:
640+
return None
641+
642+
noncurrent_days = _find_int(version_expiration_node, 'NoncurrentDays')
643+
expiration = NoncurrentVersionExpiration(noncurrent_days)
644+
645+
return expiration
646+
647+
def parse_lifecycle_verison_storage_transitions(version_storage_transition_nodes):
648+
version_storage_transitions = []
649+
for transition_node in version_storage_transition_nodes:
650+
storage_class = _find_tag(transition_node, 'StorageClass')
651+
non_crurrent_days = _find_int(transition_node, 'NoncurrentDays')
652+
version_storage_transition = NoncurrentVersionStorageTransition(non_crurrent_days, storage_class)
653+
version_storage_transitions.append(version_storage_transition)
654+
655+
return version_storage_transitions
656+
632657
def parse_get_bucket_lifecycle(result, body):
633658

634659
root = ElementTree.fromstring(body)
@@ -639,14 +664,19 @@ def parse_get_bucket_lifecycle(result, body):
639664
abort_multipart_upload = parse_lifecycle_abort_multipart_upload(rule_node.find('AbortMultipartUpload'))
640665
storage_transitions = parse_lifecycle_storage_transitions(rule_node.findall('Transition'))
641666
tagging = parse_lifecycle_object_taggings(rule_node.findall('Tag'))
667+
noncurrent_version_expiration = parse_lifecycle_version_expiration(rule_node.find('NoncurrentVersionExpiration'))
668+
noncurrent_version_sotrage_transitions = parse_lifecycle_verison_storage_transitions(rule_node.findall('NoncurrentVersionTransition'))
669+
642670
rule = LifecycleRule(
643671
_find_tag(rule_node, 'ID'),
644672
_find_tag(rule_node, 'Prefix'),
645673
status=_find_tag(rule_node, 'Status'),
646674
expiration=expiration,
647675
abort_multipart_upload=abort_multipart_upload,
648676
storage_transitions=storage_transitions,
649-
tagging=tagging
677+
tagging=tagging,
678+
noncurrent_version_expiration = noncurrent_version_expiration,
679+
noncurrent_version_sotrage_transitions = noncurrent_version_sotrage_transitions
650680
)
651681
result.rules.append(rule)
652682

@@ -852,6 +882,8 @@ def to_put_bucket_lifecycle(bucket_lifecycle):
852882
_add_text_child(expiration_node, 'Date', date_to_iso8601(expiration.date))
853883
elif expiration.created_before_date is not None:
854884
_add_text_child(expiration_node, 'CreatedBeforeDate', date_to_iso8601(expiration.created_before_date))
885+
elif expiration.expired_detete_marker is not None:
886+
_add_text_child(expiration_node, 'ExpiredObjectDeleteMarker', str(expiration.expired_detete_marker))
855887

856888
abort_multipart_upload = rule.abort_multipart_upload
857889
if abort_multipart_upload:
@@ -880,6 +912,19 @@ def to_put_bucket_lifecycle(bucket_lifecycle):
880912
tag_node = ElementTree.SubElement(rule_node, 'Tag')
881913
_add_text_child(tag_node, 'Key', key)
882914
_add_text_child(tag_node, 'Value', tagging_rule[key])
915+
916+
noncurrent_version_expiration = rule.noncurrent_version_expiration
917+
if noncurrent_version_expiration is not None:
918+
version_expiration_node = ElementTree.SubElement(rule_node, 'NoncurrentVersionExpiration')
919+
_add_text_child(version_expiration_node, 'NoncurrentDays', str(noncurrent_version_expiration.noncurrent_days))
920+
921+
noncurrent_version_sotrage_transitions = rule.noncurrent_version_sotrage_transitions
922+
if noncurrent_version_sotrage_transitions is not None:
923+
for noncurrent_version_sotrage_transition in noncurrent_version_sotrage_transitions:
924+
version_transition_node = ElementTree.SubElement(rule_node, 'NoncurrentVersionTransition')
925+
_add_text_child(version_transition_node, 'NoncurrentDays', str(noncurrent_version_sotrage_transition.noncurrent_days))
926+
_add_text_child(version_transition_node, 'StorageClass', str(noncurrent_version_sotrage_transition.storage_class))
927+
883928
return _node_to_string(root)
884929

885930

0 commit comments

Comments
 (0)