保留策略(WORM)

OSS保留策略具有WORM(Write Once Read Many)特性,满足用户以不可删除、不可篡改方式保存和使用数据。如果您希望指定时间内任何用户(包括资源拥有者)均不能修改和删除OSS某个Bucket中的Object,您可以选择为Bucket配置保留策略。在保留策略指定的Object保留时间到期之前,仅支持在Bucket中上传和读取Object。Object保留时间到期后,才可以修改或删除Object。

前提条件

确保需要设置保留策略的Bucket未开启版本控制。关于版本控制的更多信息,请参见版本控制介绍

使用场景

OSS保留策略支持的WORM特性符合美国证券交易委员会(SEC)和金融业监管局(FINRA)的合规要求。适用于金融、保险、医疗、证券、日志数据等保审查等场景。

说明

OSS已通过Cohasset Associates审计认证,满足SEC Rule 17a-4(f)、FINRA 4511、CFTC 1.31等合规要求。更多信息,请参见OSS Cohasset Assessment Report

注意事项

  • 目前仅支持针对Bucket级别设置保留策略。

  • 同一个Bucket中,不支持同时开通OSS-HDFS服务并设置保留策略。

  • Bucket内的Object在保留策略生效期间,可通过设置生命周期规则进行存储类型转化,在保证合规性的前提下,降低存储成本。

规则说明

  • 生效规则

    当基于时间的保留策略创建后,该策略默认处于InProgress状态,有效期为24小时。在此期间,Bucket资源处于保护状态。

    • 启动保留策略24小时内

      • 如果24小时内未提交锁定保留策略,则Bucket所有者以及授权用户可以删除该策略。

      • 如果24小时内已提交锁定保留策略,则不允许删除该策略,且无法缩短保护周期,仅可延长保护周期。同时,Bucket内的数据处于保护状态,尝试删除或修改这些数据时,OSS将返回409 FileImmutable错误信息。

    • 启动保留策略24小时后

      如果超过24小时未锁定该保留策略,则该策略自动失效,您可以删除该策略。

  • 删除规则

    • 基于时间的保留策略是Bucket的一种Metadata属性。当删除某个Bucket时,该Bucket对应的保留策略也会被删除。

    • 启动保留策略24小时内,如果该保留策略未提交锁定,则Bucket所有者以及授权用户可以删除该策略。

    • 如果Bucket内有Object处于保护周期内,那么您将无法删除保留策略,同时也无法删除Bucket。

  • 规则示例

    假设您在20220601日为某个Bucket创建了保护周期为30天的保留策略,且该策略在创建后进入锁定状态。您在不同时间上传了file1.txt、file2.txt、file3.txt三个Object。具体上传时间及到期时间如下:

    Object名称

    上传时间

    Object到期时间

    file1.txt

    20220401

    20220430

    file2.txt

    20220601

    20220630

    file3.txt

    20220901

    20220930

操作方式

使用OSS控制台

  1. 创建保留策略。

    1. 登录OSS管理控制台

    2. 单击Bucket 列表,然后单击目标Bucket名称。

    3. 在左侧导航栏,选择数据安全>保留策略

    4. 保留策略页面,单击创建策略

    5. 创建策略对话框,指定保留周期

      说明

      保留周期以天为单位,取值范围为1~25,550。

    6. 单击确定

      说明

      策略状态为待锁定。该状态的有效期为24小时。在此期间,相关Bucket资源将受到保护。若您决定不保留该策略,可在24小时内将其删除。

  2. 锁定保留策略。

    1. 单击锁定

    2. 在弹出的对话框,单击确定

      重要

      锁定后无法修改或删除保留策略,且在保留周期内无法修改或删除Bucket中的数据。

  3. (可选)修改保留周期。

    1. 单击编辑

    2. 在弹出的对话框,修改保留周期。

      重要

      只能延长保留周期,无法缩短。

使用阿里云SDK

以下仅列举常见SDK的设置保留策略的代码示例。关于其他SDK的设置保留策略代码示例,请参见SDK简介

Java

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.InitiateBucketWormRequest;
import com.aliyun.oss.model.InitiateBucketWormResult;

public class Demo {

    public static void main(String[] args) throws Exception {
        // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "examplebucket";
        // 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
        String region = "cn-hangzhou";

        // 创建OSSClient实例。
        // 当OSSClient实例不再使用时,调用shutdown方法以释放资源。
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);        
        OSS ossClient = OSSClientBuilder.create()
        .endpoint(endpoint)
        .credentialsProvider(credentialsProvider)
        .clientConfiguration(clientBuilderConfiguration)
        .region(region)               
        .build();

        try {
            // 创建InitiateBucketWormRequest对象。
            InitiateBucketWormRequest initiateBucketWormRequest = new InitiateBucketWormRequest(bucketName);
            // 指定Object保护天数为1天。
            initiateBucketWormRequest.setRetentionPeriodInDays(1);

            // 创建合规保留策略。
            InitiateBucketWormResult initiateBucketWormResult = ossClient.initiateBucketWorm(initiateBucketWormRequest);

            // 查看合规保留策略ID。
            String wormId = initiateBucketWormResult.getWormId();
            System.out.println(wormId);
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

PHP

<?php

// 引入自动加载文件 加载依赖库
require_once __DIR__ . '/../vendor/autoload.php';

use AlibabaCloud\Oss\V2 as Oss;

// 定义命令行参数描述
$optsdesc = [
    "region" => ['help' => 'The region in which the bucket is located', 'required' => True], // 区域是必填项 存储空间所在的区域
    "endpoint" => ['help' => 'The domain names that other services can use to access OSS', 'required' => False], // 终端节点是可选项 其他服务可以用来访问OSS的域名
    "bucket" => ['help' => 'The name of the bucket', 'required' => True], // 存储空间名称是必填项
];

// 生成长选项列表 用于解析命令行参数
$longopts = \array_map(function ($key) {
    return "$key:"; // 每个参数后面加冒号 表示需要值
}, array_keys($optsdesc));

// 解析命令行参数
$options = getopt("", $longopts); 

// 检查必填参数是否缺失
foreach ($optsdesc as $key => $value) {
    if ($value['required'] === True && empty($options[$key])) {
        $help = $value['help'];
        echo "Error: the following arguments are required: --$key, $help"; // 提示用户缺少必填参数
        exit(1); 
    }
}

// 获取命令行参数值
$region = $options["region"]; // 存储空间所在区域
$bucket = $options["bucket"]; // 存储空间名称

// 使用环境变量加载凭证信息 AccessKeyId 和 AccessKeySecret
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();

// 使用SDK的默认配置
$cfg = Oss\Config::loadDefault();

// 设置凭证提供者
$cfg->setCredentialsProvider($credentialsProvider);

// 设置区域
$cfg->setRegion($region);

// 如果提供了终端节点 则设置终端节点
if (isset($options["endpoint"])) {
    $cfg->setEndpoint($options["endpoint"]);
}

// 创建OSS客户端实例
$client = new Oss\Client($cfg);

// 创建初始化存储空间合规保留策略的请求对象 设置保留天数为3天
$request = new Oss\Models\InitiateBucketWormRequest(
    bucket: $bucket, 
    initiateWormConfiguration: new Oss\Models\InitiateWormConfiguration(
        retentionPeriodInDays: 3 // 保留天数
));

// 调用initiateBucketWorm方法初始化存储空间的合规保留策略
$result = $client->initiateBucketWorm($request);

// 打印返回结果
printf(
    'status code:' . $result->statusCode . PHP_EOL . // HTTP响应状态码
    'request id:' . $result->requestId . PHP_EOL . // 请求的唯一标识
    'worm id:' . $result->wormId // 合规保留策略的ID
);

Node.js

const OSS = require('ali-oss');

const client = new OSS({
  // yourregion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
  region: 'yourregion',
  // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
  accessKeyId: process.env.OSS_ACCESS_KEY_ID,
  accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,  
  authorizationV4: true,
  // yourBucketName填写Bucket名称。
  bucket: 'yourBucketName',
});
// 创建保留策略。
async function initiateBucketWorm() {
 // yourbucketname填写存储空间名称。
  const bucket = 'yourbucketname'
  // 指定Object保护天数。
  const days = '<Retention Days>'
    const res = await client.initiateBucketWorm(bucket, days)
  console.log(res.wormId)
}

initiateBucketWorm()

Python

import argparse
import alibabacloud_oss_v2 as oss

# 创建命令行参数解析器,并描述脚本用途:示例展示如何初始化OSS存储空间的WORM配置
parser = argparse.ArgumentParser(description="initiate bucket worm sample")

# 添加命令行参数 --region,表示存储空间所在的区域,必需参数
parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
# 添加命令行参数 --bucket,表示要操作的存储空间名称,必需参数
parser.add_argument('--bucket', help='The name of the bucket.', required=True)
# 添加命令行参数 --endpoint,表示其他服务可用来访问OSS的域名,非必需参数
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
# 添加命令行参数 --retention_period_in_days,表示对象保留天数,必需参数
parser.add_argument('--retention_period_in_days', help='The number of days for which objects can be retained.', required=True)

def main():
    # 解析命令行提供的参数,获取用户输入的值
    args = parser.parse_args()

    # 从环境变量中加载访问OSS所需的认证信息,用于身份验证
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    # 使用SDK的默认配置创建配置对象,并设置认证提供者
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = args.region

    # 如果提供了自定义endpoint,则更新配置对象中的endpoint属性
    if args.endpoint is not None:
        cfg.endpoint = args.endpoint

    # 使用上述配置初始化OSS客户端,准备与OSS交互
    client = oss.Client(cfg)

    # 发送请求以初始化指定存储空间的WORM配置
    result = client.initiate_bucket_worm(oss.InitiateBucketWormRequest(
        bucket=args.bucket,  # 存储空间名
        initiate_worm_configuration=oss.InitiateWormConfiguration(
            retention_period_in_days=int(args.retention_period_in_days),  # 对象保留天数,转换为整数
        ),
    ))

    # 打印操作结果的状态码、请求ID和WORM ID,以便确认请求状态
    print(f'status code: {result.status_code},'
          f' request id: {result.request_id},'
          f' worm id: {result.worm_id}'
    )

# 当此脚本被直接执行时,调用main函数开始处理逻辑
if __name__ == "__main__":
    main()  # 脚本入口点,控制程序流程从这里开始

Go

package main

import (
	"log"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
	// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Fatalf("Error creating credentials provider: %v", err)
	}

	// 创建OSSClient实例。
	// yourEndpoint填写Bucket对应的Endpoint,以华东1(杭州)为例,填写为https://oss-cn-hangzhou.aliyuncs.com。其它Region请按实际情况填写。
	// yourRegion填写Bucket所在地域,以华东1(杭州)为例,填写为cn-hangzhou。其它Region请按实际情况填写。
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("yourRegion"))
	// 设置签名版本
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("yourEndpoint", "", "", clientOptions...)
	if err != nil {
		log.Fatalf("Error creating OSS client: %v", err)
	}

	// 填写待配置合规保留策略的Bucket名称。
	bucketName := "<yourBucketName>"

	// 指定Object保护天数为60天。
	result, err := client.InitiateBucketWorm(bucketName, 60)
	if err != nil {
		log.Fatalf("Error initiating bucket WORM: %v", err)
	}

	log.Println("WORM policy initiated successfully:", result)
}

C++

#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

int main(void)
{
    /* 初始化OSS账号信息。*/
            
    /* yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。*/
    std::string Endpoint = "yourEndpoint";
    /* yourRegion填写Bucket所在地域对应的Region。以华东1(杭州)为例,Region填写为cn-hangzhou。*/
    std::string Region = "yourRegion";
    /* 填写Bucket名称,例如examplebucket。*/
    std::string BucketName = "examplebucket";

      /* 初始化网络等资源。*/
      InitializeSdk();

      ClientConfiguration conf;
      conf.signatureVersion = SignatureVersionType::V4;
      /* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);
    client.SetRegion(Region);
  
      /* 创建保留策略,指定Object保护天数为1天。*/
      auto outcome = client.InitiateBucketWorm(InitiateBucketWormRequest(BucketName, 1));

      if (outcome.isSuccess()) {      
            std::cout << " InitiateBucketWorm success " << std::endl;
            std::cout << "WormId:" << outcome.result().WormId() << std::endl;
      }
      else {
        /* 异常处理。*/
        std::cout << "InitiateBucketWorm fail" <<
        ",code:" << outcome.error().Code() <<
        ",message:" << outcome.error().Message() <<
        ",requestId:" << outcome.error().RequestId() << std::endl;
        return -1;
      }

      /* 释放网络等资源。*/
      ShutdownSdk();
      return 0;
}

使用命令行工具ossutil

您可以使用命令行工具ossutil来新建WORM策略,ossutil的安装请参见安装ossutil

以下命令用于在存储空间 examplebucket 中创建一条新的 WORM策略,并将WORM的保留期设置为 365 天。

ossutil api initiate-bucket-worm --bucket examplebucket --initiate-worm-configuration "{\"RetentionPeriodInDays\":\"365\"}"

关于该命令的更多信息,请参见initiate-bucket-worm

相关API

以上操作方式底层基于API实现,如果您的程序自定义要求较高,您可以直接发起REST API请求。直接发起REST API请求需要手动编写代码计算签名。更多信息,请参见InitiateBucketWorm

常见问题

保留策略有哪些优势?

保留策略提供数据合规存储,保护周期内数据不可删除或修改。相比之下,RAM policyBucket Policy保护的数据可能被修改或删除。

什么情况下需要设置保留策略?

当需要长期存储且不允许修改或删除的重要数据,如医疗档案、技术文件、合同文书等,建议在指定Bucket内开启保留策略。

是否支持取消保留策略?

是否支持取消保留策略取决于其状态。

  • 未提交锁定:Bucket拥有者和授权用户可删除。

  • 已提交锁定:任何人均无法删除。

是否支持针对Object设置保留策略?

仅支持针对Bucket设置保留策略,不支持目录或单个对象。

如何计算Object的保留时间

结合Object的最后修改时间和保留策略的保留周期计算。例如,Bucket A的保留策略为10天,Object最后修改时间为20220215日,则保留时间为20220225日。

如何删除已开启保留策略的Bucket ?

  • 未存储Object:可直接删除。

  • 已存储Object且过保护期:先删除所有Object,再删除Bucket。

  • 已存储Object且在保护期内:无法删除。

如果OSS欠费,但仍有Object处于保留策略的保护期内,这些Object会被保留么?

请注意:欠费情况下,即使Bucket内的Object处于保留策略的保护期内,阿里云仍然会根据欠费停服规则进行数据删除。

授权的RAM用户是否可以设置保留策略?

保留策略API已开放,支持RAM policy。授权的RAM用户可通过控制台、API、SDK等方式创建或删除保留策略。