使用存储空间清单

当存储空间(Bucket)中的文件(Object)数量达到数百万甚至上百亿时,通过 ListObjects 接口逐一列举会变得非常缓慢且成本高昂。存储空间清单功能专为此类海量Object场景设计,它能够异步、定期地扫描 Bucket,并生成一份包含指定对象元数据(如大小、存储类型、加密状态等)的清单文件,可灵活应用于多种场景,如:资产盘点、成本分析、数据合规审计、批量处理任务的输入准备等。

工作原理

创建清单规则后,OSS 会在指定周期(每天或每周)自动执行清单生成任务。该任务在后台异步运行,不影响对 Bucket 的正常访问。其工作流程遵循以下步骤:

  1. 获取权限:OSS 通过扮演一个预先授权的 RAM 角色,来获得扫描源存储桶和写入目标存储桶的权限。

  2. 扫描对象:OSS 根据规则中定义的筛选条件(如对象前缀、版本状态、创建时间或大小),扫描源存储桶中的所有匹配对象。

  3. 生成清单报告:OSS 将扫描结果聚合生成清单报告,并以 Gzip 压缩的 CSV 文件格式,写入指定的 Bucket 中。

使用此功能时,请注意:

  • 报告快照:清单报告是任务启动时刻存储桶状态的快照。在扫描执行期间的对象变更(如新增、覆盖或删除)不保证会反映在当次的报告中。

  • 首份清单会在配置后立即执行,后续按客户设置的每日或每周的周期在北京时间凌晨批量执行,导出延迟受对象数量与任务队列影响。

步骤一:配置权限

根据当前使用的身份,需要完成的授权步骤有所不同:

  • 阿里云账号:只需要 创建 RAM 角色并为其授权,以便 OSS 服务可以扮演该角色。

  • RAM 用户:需要管理员或主账户先为您的账号授权,获得创建清单和管理 RAM 角色的权限,再创建 RAM 角色并为其授权

第一步:为 RAM 用户授权(如适用)

此步骤旨在授予 RAM 用户配置清单规则及创建后续所需 RAM 角色的权限。

如果是阿里云账号,可跳过此步骤,直接进入第二步。

安全建议:由于授予 RAM 用户创建角色(ram:CreateRole)等权限存在一定的安全风险,推荐通过阿里云账号预先创建好所需的 RAM 角色。授权完成后,RAM 用户可以直接在创建清单规则时选用该角色,无需自己创建,这是一种更安全的实践。如果仍需为 RAM 用户授予创建角色的权限,请按以下步骤操作:

  1. 创建自定义权限策略 通过脚本编辑模式创建以下自定义策略。

    以下策略中的 oss:ListBuckets 权限仅在通过控制台操作时需要。如果通过 SDK 或 ossutil 等工具访问,则无需此权限。
    {
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "oss:PutBucketInventory",
                    "oss:GetBucketInventory",
                    "oss:DeleteBucketInventory",
                    "oss:ListBuckets",
                    "ram:CreateRole",
                    "ram:AttachPolicyToRole",
                    "ram:GetRole",
                    "ram:ListPoliciesForRole"
                ],
                "Resource": "*"
            }
        ],
        "Version": "1"
    }

    提示:如果当前 RAM 用户已拥有 AliyunOSSFullAccess 系统权限,则仅需为其补充授予角色管理的相关权限即可:

    {
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "ram:CreateRole",
                    "ram:AttachPolicyToRole",
                    "ram:GetRole",
                    "ram:ListPoliciesForRole"
                ],
                "Resource": "*"
            }
        ],
        "Version": "1"
    }
  2. 为 RAM 用户授权 将刚刚创建的自定义策略授予目标 RAM 用户

第二步:创建并配置 RAM 角色

允许 OSS 服务将生成的清单报告文件写入目标 Bucket。

  1. 前往 RAM 控制台,创建角色。选择信任的实体类型为云服务,信任主体名称选择对象存储

  2. 为该角色创建并附加一个自定义权限策略。策略内容如下,将 your-destination-bucket 替换为实际用于存放清单报告的 Bucket 名称。

    {
      "Version": "1",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "oss:PutObject",
          "Resource": [
            "acs:oss:*:*:your-destination-bucket/*" 
          ]
        }
      ]
    }
    KMS 加密:如果计划使用 KMS 密钥加密清单文件,请务必为RAM 角色额外授予 AliyunKMSFullAccess 权限或更精细的 KMS 相关权限。
  3. 记录下该角色的 ARN(例如 acs:ram::1234567890:role/oss-inventory-role),后续步骤将用到。

安全建议:当通过控制台首次配置清单规则时,系统会引导自动创建一个名为 AliyunOSSRole 的服务关联角色。不推荐在生产环境中使用控制台自动创建的AliyunOSSRole。该角色拥有对所有 Bucket 的完全管理权限,存在较大安全风险,仅建议在临时测试环境中使用。

步骤二:配置清单规则

可以通过 OSS 控制台、SDK 或命令行工具 ossutil 等进行配置,设置清单的扫描范围、频率、报告内容等。

控制台

  1. 登录 OSS 管理控制台

  2. 进入需要生成清单的源 Bucket,在左侧导航栏选择 数据管理 > Bucket 清单

  3. Bucket 清单页面,单击创建清单

  4. 设置清单报告规则面板,按以下说明配置各项参数。

    参数

    说明

    状态

    设置清单任务的状态,选择启动

    规则名称

    设置清单任务的名称。只能包含小写字母、数字、短划线(-),且不能以短划线(-)开头或结尾。

    存储清单 Bucket

    选择存储清单文件的Bucket。

    配置清单的 Bucket 与存放清单 Bucket 可相同或不同,但必须同账号、同地域。

    清单报告存储路径

    设置清单报告的存储路径。

    • 若需将报告保存到存储空间 examplebucket 的 exampledir1 路径,请填写 exampledir1/,指定路径在 Bucket 中不存在时,OSS 会自动创建该路径。

    • 若留空,报告将保存在根目录。

    重要

    为避免影响OSS-HDFS服务的正常使用或者引发数据污染的风险,在开通了OSS-HDFS服务的Bucket设置清单报告规则时,禁止将清单报告目录填写为.dlsdata/

    清单报告导出周期

    设置清单报告的生成周期。可选择每周每天。文件数超百亿时,建议选择每周以降低成本和扫描压力。

    清单报告加密选项

    选择是否加密清单文件。

    说明

    使用KMS密钥功能时会产生少量的KMS密钥API调用费用,费用详情请参考KMS 1.0计费说明

    清单内容

    选择希望导出的文件信息,包括Object大小存储类型最后更新日期ETag分片上传状态加密状态Object ACL标签个数文件类型Crc64

    按前缀匹配

    可选。用于仅扫描指定前缀下的文件,例如 exampledir1/。留空则扫描整个 Bucket。如果设置的前缀没有匹配Bucket内的任意Object,则不生成清单文件。

    配置高级筛选功能

    重要

    仅华北1(青岛)、华北5(呼和浩特)和德国(法兰克福)地域支持配置以下过滤选项。

    如果需要根据文件大小、存储类型等条件过滤导出的文件,需要打开配置高级筛选功能开关。

    支持的过滤选项说明如下:

    • 时间范围:设置待导出文件最后一次修改的起始日期和结束日期,时间精确到秒。

    • 文件大小范围:设置待导出文件的文件大小最小值和最大值。

      重要

      设置文件大小范围时,确保文件大小的最小值以及最大值均大于0 B,且最大值不超过48.8 TB。

    • 存储类型:设置待导出哪些存储类型的文件。可以选择导出标准存储、低频访问、归档存储、冷归档存储以及深度冷归档存储的文件。

    对象版本

    若 Bucket 开启了版本控制,可选择导出当前版本所有版本

  5. 选中我知晓并同意授予阿里云 OSS 服务访问 Bucket 资源的权限,然后单击确定

    涉及Object较多时,生成清单文件需要一定的时间。可以通过两种方式判断是否已生成清单文件,详情请参见如何判断是否已生成清单文件?

SDK

以下仅列举常见SDK配置Bucket清单的代码示例。关于其他SDK的配置Bucket清单代码示例,请参见SDK简介

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.*;
import java.util.ArrayList;
import java.util.List;

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名称。
        String destBucketName ="yourDestinationBucketName";
        // 填写Bucket所有者授予的账户ID。
        String accountId ="yourDestinationBucketAccountId";
        // 填写具有读取源Bucket所有文件和向目标Bucket写入文件权限的角色名称。
        String roleArn ="yourDestinationBucketRoleArn";
        // 填写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 {
            // 创建清单配置。
            InventoryConfiguration inventoryConfiguration = new InventoryConfiguration();

            // 设置清单规则名称。
            String inventoryId = "testid";
            inventoryConfiguration.setInventoryId(inventoryId);

            // 设置清单中包含的Object属性。
            List<String> fields = new ArrayList<String>();
            fields.add(InventoryOptionalFields.Size);
            fields.add(InventoryOptionalFields.LastModifiedDate);
            fields.add(InventoryOptionalFields.IsMultipartUploaded);
            fields.add(InventoryOptionalFields.StorageClass);
            fields.add(InventoryOptionalFields.ETag);
            fields.add(InventoryOptionalFields.EncryptionStatus);
            inventoryConfiguration.setOptionalFields(fields);

            // 设置清单的生成计划,以下示例为每周一次。其中,Weekly表示每周一次,Daily表示每天一次。
            inventoryConfiguration.setSchedule(new InventorySchedule().withFrequency(InventoryFrequency.Weekly));

            // 设置清单中包含的Object的版本为当前版本。如果设置为InventoryIncludedObjectVersions.All则表示Object的所有版本在版本控制状态下生效。
            inventoryConfiguration.setIncludedObjectVersions(InventoryIncludedObjectVersions.Current);

            // 清单配置是否启用的标识,取值为true或者false,设置为true表示启用清单配置,设置为false表示关闭清单配置。
            inventoryConfiguration.setEnabled(true);

            // 设置清单筛选规则,指定筛选Object的前缀。
            InventoryFilter inventoryFilter = new InventoryFilter().withPrefix("obj-prefix");
            inventoryConfiguration.setInventoryFilter(inventoryFilter);

            // 创建存放清单结果的目标Bucket配置。
            InventoryOSSBucketDestination ossInvDest = new InventoryOSSBucketDestination();
            // 设置存放清单结果的存储路径前缀。
            ossInvDest.setPrefix("destination-prefix");
            // 设置清单格式。
            ossInvDest.setFormat(InventoryFormat.CSV);
            // 设置目标Bucket的用户accountId。
            ossInvDest.setAccountId(accountId);
            // 设置目标Bucket的roleArn。
            ossInvDest.setRoleArn(roleArn);
            // 设置目标Bucket的名称。
            ossInvDest.setBucket(destBucketName);

            // 如果需要使用KMS加密清单,请参考如下设置。
            // InventoryEncryption inventoryEncryption = new InventoryEncryption();
            // InventoryServerSideEncryptionKMS serverSideKmsEncryption = new InventoryServerSideEncryptionKMS().withKeyId("test-kms-id");
            // inventoryEncryption.setServerSideKmsEncryption(serverSideKmsEncryption);
            // ossInvDest.setEncryption(inventoryEncryption);

            // 如果需要使用OSS服务端加密清单,请参考如下设置。
            // InventoryEncryption inventoryEncryption = new InventoryEncryption();
            // inventoryEncryption.setServerSideOssEncryption(new InventoryServerSideEncryptionOSS());
            // ossInvDest.setEncryption(inventoryEncryption);

            // 设置清单的目的地。
            InventoryDestination destination = new InventoryDestination();
            destination.setOssBucketDestination(ossInvDest);
            inventoryConfiguration.setDestination(destination);

            // 上传清单配置。
            ossClient.setBucketInventoryConfiguration(bucketName, inventoryConfiguration);
        } 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();
            }
        }
    }
}
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,
  // 填写存储空间名称。
  bucket: 'yourbucketname'
});

const inventory = {
  // 设置清单配置ID。
  id: 'default', 
  // 清单配置是否启用的标识,取值为true或false。
  isEnabled: false, 
  //(可选)设置清单筛选规则,指定筛选object的前缀。
  prefix: 'ttt',
  OSSBucketDestination: {
     // 设置清单格式。
    format: 'CSV',
   // 目标Bucket拥有者的账号ID。
    accountId: '<Your AccountId>', 
   // 目标Bucket的角色名称。
    rolename: 'AliyunOSSRole',
    // 目标Bucket的名称。
    bucket: '<Your BucketName>',
    //(可选)清单结果的存储路径前缀。
    prefix: '<Your Prefix>',
    // 如果需要使用SSE-OSS加密清单,请参考以下代码。
    //encryption: {'SSE-OSS': ''},
    // 如果需要使用SSE-KMS加密清单,请参考以下代码。
           /*
            encryption: {
      'SSE-KMS': {
        keyId: 'test-kms-id',
      };, 
    */
  },
  // 设置清单的生成计划,WEEKLY对应每周一次,DAILY对应每天一次。
  frequency: 'Daily', 
  // 设置清单结果中包含了Object的所有版本, 如果设置为Current,则表示仅包含Object的当前版本。
  includedObjectVersions: 'All', 
  optionalFields: {
    //(可选)设置清单中包含的Object属性。
    field: ["Size", "LastModifiedDate", "ETag", "StorageClass", "IsMultipartUploaded", "EncryptionStatus"]
  },
}

async function putInventory(){
  // 需要添加清单配置的Bucket名称。
  const bucket = '<Your BucketName>'; 
        try {
    await client.putBucketInventory(bucket, inventory);
    console.log('清单配置添加成功')
  } catch(err) {
    console.log('清单配置添加失败: ', err);
  }
}

putInventory()
import argparse
import alibabacloud_oss_v2 as oss

# 创建命令行参数解析器,并描述脚本用途:设置存储空间清单(Inventory)
parser = argparse.ArgumentParser(description="put bucket inventory sample")

# 定义命令行参数,包括必需的区域、存储空间名称、endpoint、用户ID、角色ARN以及清单名称
parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
parser.add_argument('--bucket', help='The name of the bucket.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
parser.add_argument('--user_id', help='User account ID.', required=True)
parser.add_argument('--arn', help='The Alibaba Cloud Resource Name (ARN) of the role that has the permissions to read all objects from the source bucket and write objects to the destination bucket. Format: `acs:ram::uid:role/rolename`.', required=True)
parser.add_argument('--inventory_id', help='The name of the inventory.', required=True)

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

    # 从环境变量中加载访问凭证信息,用于身份验证
    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)

    # 发送请求以配置指定存储空间的清单设置
    result = client.put_bucket_inventory(oss.PutBucketInventoryRequest(
            bucket=args.bucket,  # 存储空间名
            inventory_id=args.inventory_id,  # 存储空间清单ID
            inventory_configuration=oss.InventoryConfiguration(
                included_object_versions='All',  # 包含所有版本的对象
                optional_fields=oss.OptionalFields(
                    fields=[  # 可选字段,如大小和最后修改日期
                        oss.InventoryOptionalFieldType.SIZE,
                        oss.InventoryOptionalFieldType.LAST_MODIFIED_DATE,
                    ],
                ),
                id=args.inventory_id,  # 存储空间清单ID
                is_enabled=True,  # 启用存储空间清单
                destination=oss.InventoryDestination(
                    oss_bucket_destination=oss.InventoryOSSBucketDestination(
                        format=oss.InventoryFormatType.CSV,  # 输出格式为CSV
                        account_id=args.user_id,  # 用户账户ID
                        role_arn=args.arn,  # 角色ARN,具有读取源存储空间和写入目标存储空间的权限
                        bucket=f'acs:oss:::{args.bucket}',  # 目标存储空间
                        prefix='aaa',  # 清单文件前缀
                    ),
                ),
                schedule=oss.InventorySchedule(
                    frequency=oss.InventoryFrequencyType.DAILY,  # 清单频率,这里设置为每天
                ),
                filter=oss.InventoryFilter(
                    lower_size_bound=1024,  # 对象大小下限(字节)
                    upper_size_bound=1048576,  # 对象大小上限(字节)
                    storage_class='ColdArchive',  # 存储类别筛选条件
                    prefix='aaa',  # 对象前缀筛选条件
                    last_modify_begin_time_stamp=1637883649,  # 最后修改时间戳开始范围
                    last_modify_end_time_stamp=1638347592,  # 最后修改时间戳结束范围
                ),
            ),
    ))

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

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

// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
var endpoint = "yourEndpoint";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");
// 填写Bucket名称。
var bucketName = "examplebucket";
// 填写Bucket所有者授予的账户ID。
var accountId ="yourDestinationBucketAccountId";
// 填写具有读取源Bucket所有文件和向目标Bucket写入文件权限的角色名称。
var roleArn ="yourDestinationBucketRoleArn";
// 填写存放清单结果的Bucket名称。
var destBucketName ="yourDestinationBucketName";
// 填写Bucket所在地域对应的Region。以华东1(杭州)为例,Region填写为cn-hangzhou。
const string region = "cn-hangzhou";

// 创建ClientConfiguration实例,按照您的需要修改默认参数。
var conf = new ClientConfiguration();

// 设置v4签名。
conf.SignatureVersion = SignatureVersion.V4;

// 创建OssClient实例。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
client.SetRegion(region);
try
{
    // 添加Bucket清单。
    var config = new InventoryConfiguration();
    // 设置清单规则名称。
    config.Id = "report1";
    // 清单配置是否启用的标识,取值为true或false。设置为true,表示启用清单配置。
    config.IsEnabled = true;
    // 设置清单筛选规则,指定筛选Object的前缀。
    config.Filter = new InventoryFilter("filterPrefix");
    // 创建清单的bucket目的地配置。
    config.Destination = new InventoryDestination();
    config.Destination.OSSBucketDestination = new InventoryOSSBucketDestination();
    // 设置清单格式。
    config.Destination.OSSBucketDestination.Format = InventoryFormat.CSV;
    // 存放清单结果的目标Bucket的用户accountId。
    config.Destination.OSSBucketDestination.AccountId = accountId;
    // 存放清单结果的目标Bucket的roleArn。
    config.Destination.OSSBucketDestination.RoleArn = roleArn;
    // 存放清单结果的目标Bucket名称。
    config.Destination.OSSBucketDestination.Bucket = destBucketName;
    // 设置存放清单结果的存储路径前缀。
    config.Destination.OSSBucketDestination.Prefix = "prefix1";
    
    // 设置清单的生成计划,以下示例为每周一次。其中,Weekly对应每周一次,Daily对应每天一次。
    config.Schedule = new InventorySchedule(InventoryFrequency.Daily);
    // 设置清单中包含的object的版本为当前版本。如果设置为InventoryIncludedObjectVersions.All则表示object的所有版本,在版本控制状态下生效。
    config.IncludedObjectVersions = InventoryIncludedObjectVersions.All;
    
    // 设置清单中包含的Object属性。
    config.OptionalFields.Add(InventoryOptionalField.Size);
    config.OptionalFields.Add(InventoryOptionalField.LastModifiedDate);
    config.OptionalFields.Add(InventoryOptionalField.StorageClass);
    config.OptionalFields.Add(InventoryOptionalField.IsMultipartUploaded);
    config.OptionalFields.Add(InventoryOptionalField.EncryptionStatus);
    config.OptionalFields.Add(InventoryOptionalField.ETag);
    var req = new SetBucketInventoryConfigurationRequest(bucketName, config);
    client.SetBucketInventoryConfiguration(req);
    Console.WriteLine("Set bucket:{0} InventoryConfiguration succeeded", bucketName);
}
catch (OssException ex)
{
    Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
        ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
#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);

    InventoryConfiguration inventoryConf;
    /* 指定清单规则名称,该名称在当前Bucket下必须全局唯一。*/
    inventoryConf.setId("inventoryId");

    /* 清单配置是否启用的标识,可选值为true或false。*/
    inventoryConf.setIsEnabled(true);

    /* (可选)清单筛选的前缀。指定前缀后,清单将筛选出符合前缀的Object。*/
    inventoryConf.setFilter(InventoryFilter("objectPrefix"));

    InventoryOSSBucketDestination dest;
    /* 导出清单文件的文件格式。*/
    dest.setFormat(InventoryFormat::CSV);
    /* 存储空间拥有者的账户UID。*/
    dest.setAccountId("10988548********");
    /* 指定角色名称,该角色需要拥有读取源存储空间所有文件以及向目标存储空间写入文件的权限,格式为acs:ram::uid:role/rolename。*/
    dest.setRoleArn("acs:ram::10988548********:role/inventory-test");
    /* 存放导出的清单文件的存储空间。*/
    dest.setBucket("yourDstBucketName");
    /* 清单文件的存储路径前缀。*/
    dest.setPrefix("yourPrefix");
    /* (可选)清单文件的加密方式, 可选SSEOSS或者SSEKMS方式加密。*/
    //dest.setEncryption(InventoryEncryption(InventorySSEOSS()));
    //dest.setEncryption(InventoryEncryption(InventorySSEKMS("yourKmskeyId")));
    inventoryConf.setDestination(dest);

    /* 清单文件导出的周期, 可选为Daily或者Weekly。*/
    inventoryConf.setSchedule(InventoryFrequency::Daily);

    /* 是否在清单中包含Object版本信息, 可选为All或者Current。*/
    inventoryConf.setIncludedObjectVersions(InventoryIncludedObjectVersions::All);

    /* (可选)设置清单结果中应包含的配置项, 请按需配置。*/
    InventoryOptionalFields field { 
        InventoryOptionalField::Size, InventoryOptionalField::LastModifiedDate, 
        InventoryOptionalField::ETag, InventoryOptionalField::StorageClass, 
        InventoryOptionalField::IsMultipartUploaded, InventoryOptionalField::EncryptionStatus
    };
    inventoryConf.setOptionalFields(field);

    /* 设置清单配置。*/
    auto outcome = client.SetBucketInventoryConfiguration(
        SetBucketInventoryConfigurationRequest(BucketName, inventoryConf));

    if (!outcome.isSuccess()) {
        /* 异常处理。*/
        std::cout << "Set Bucket Inventory fail" <<
        ",code:" << outcome.error().Code() <<
        ",message:" << outcome.error().Message() <<
        ",requestId:" << outcome.error().RequestId() << std::endl;
        return -1;
    }

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

import (
	"context"
	"flag"
	"log"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)

// 定义全局变量
var (
	region     string // 存储区域
	bucketName string // 存储空间名称
)

// init函数用于初始化命令行参数
func init() {
	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
}

func main() {
	// 解析命令行参数
	flag.Parse()

	var (
		accountId   = "account id of the bucket" // 存储空间所有者授予的账户ID,例如109885487000****
		inventoryId = "inventory id"             // 由用户指定的清单名称,清单名称在当前Bucket下必须全局唯一
	)

	// 检查bucket名称是否为空
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, bucket name required")
	}

	// 检查region是否为空
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// 加载默认配置并设置凭证提供者和区域
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// 创建OSS客户端
	client := oss.NewClient(cfg)

	// 创建设置存储空间清单的请求
	putRequest := &oss.PutBucketInventoryRequest{
		Bucket:      oss.Ptr(bucketName),  // 存储空间名称
		InventoryId: oss.Ptr(inventoryId), // 由用户指定的清单名称
		InventoryConfiguration: &oss.InventoryConfiguration{
			Id:        oss.Ptr(inventoryId), // 由用户指定的清单名称
			IsEnabled: oss.Ptr(true),        // 启用清单配置
			Filter: &oss.InventoryFilter{
				Prefix:                   oss.Ptr("filterPrefix"),    // 设置清单筛选规则,指定筛选Object的前缀
				LastModifyBeginTimeStamp: oss.Ptr(int64(1637883649)), // 最后修改开始时间戳
				LastModifyEndTimeStamp:   oss.Ptr(int64(1638347592)), // 最后修改结束时间戳
				LowerSizeBound:           oss.Ptr(int64(1024)),       // 文件大小下限(字节)
				UpperSizeBound:           oss.Ptr(int64(1048576)),    // 文件大小上限(字节)
				StorageClass:             oss.Ptr("Standard,IA"),     // 存储类型
			},
			Destination: &oss.InventoryDestination{
				OSSBucketDestination: &oss.InventoryOSSBucketDestination{
					Format:    oss.InventoryFormatCSV,                                   // 导出清单文件的文件格式
					AccountId: oss.Ptr(accountId),                                       // 存储空间所有者授予的账户ID,例如109885487000****
					RoleArn:   oss.Ptr("acs:ram::" + accountId + ":role/AliyunOSSRole"), // 存储空间所有者授予操作权限的角色名,比如acs:ram::109885487000****:role/ram-test
					Bucket:    oss.Ptr("acs:oss:::" + bucketName),                       // 存放导出的清单结果的Bucket名称
					Prefix:    oss.Ptr("export/"),                                       // 存放清单结果的存储路径前缀
				},
			},
			Schedule: &oss.InventorySchedule{
				Frequency: oss.InventoryFrequencyDaily, // 清单文件导出的周期(每天)
			},
			IncludedObjectVersions: oss.Ptr("All"), // 是否在清单中包含Object的所有版本信息
		},
	}

	// 执行设置存储空间清单的请求
	putResult, err := client.PutBucketInventory(context.TODO(), putRequest)
	if err != nil {
		log.Fatalf("failed to put bucket inventory %v", err)
	}

	// 打印设置存储空间清单的结果
	log.Printf("put bucket inventory result:%#v\n", putResult)
}

ossutil

通过命令行工具进行配置,适用于脚本化、批量化的运维操作。使用前请先安装并配置ossutil2.0

命令格式

ossutil api put-bucket-inventory --bucket <bucket_name> --inventory-id <inventory_id> --inventory-configuration <json_config>

示例

# 此命令为名为 examplebucket 的 Bucket 创建一个名为 daily-report 的清单规则
# 报告每日生成,存储到 destbucket 的 reports/ 路径下
# 需要提前配置好 ossutil 的认证信息
ossutil api put-bucket-inventory --bucket examplebucket --inventory-id daily-report --inventory-configuration '{
    "Id": "daily-report",
    "IsEnabled": "true",
    "Destination": {
        "OSSBucketDestination": {
            "Format": "CSV",
            "AccountId": "100000000000000",

            "RoleArn": "acs:ram::100000000000000:role/oss-inventory-role",
            "Bucket": "acs:oss:::destbucket",
            "Prefix": "reports/"
        }
    },
    "Schedule": {
        "Frequency": "Daily"
    },
    "IncludedObjectVersions": "Current",
    "OptionalFields": {
        "Field": ["Size", "LastModifiedDate", "StorageClass"]
    }
}'
注意:关于 put-bucket-inventory 命令的详细用法,请参考 put-bucket-inventory

API

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

步骤三:解析清单

清单任务以异步方式执行。清单报告的所有文件都存储在一个以扫描启动时间命名的文件夹中。核心文件包括 manifest.jsondata/ 目录下的 .csv.gz 数据文件。要确认任务是否完成,可以检查目标是否已生成 manifest.json 文件。

第一步:读取 manifest.json 文件

清单报告的列顺序是动态的,它取决于在配置清单规则时选择的字段。需要先解析 manifest.json 文件来获取正确的列顺序,然后再处理数据文件。该文件包含了本次清单任务的核心元数据。需要重点关注以下两个字段:

  • fileSchema: 一个字符串,定义了 CSV 文件中各列的名称和确切顺序。

  • files: 一个数组,列出了本次报告生成的所有 .csv.gz 数据文件的详细信息,包括其路径(key)、大小(size)和 MD5 校验值(MD5checksum)。

manifest.json 示例:

{
  "sourceBucket": "your-source-bucket",
  "destinationBucket": "your-destination-bucket",
  "version": "2019-09-01",
  "creationTimestamp": "1642994594",
  "fileFormat": "CSV",
  "fileSchema": "Bucket, Key, Size, LastModifiedDate, ETag, StorageClass, EncryptionStatus",
  "files": [
    {
      "key": "inventory-reports/your-source-bucket/daily-report/data/a1b2c3d4-....csv.gz",
      "size": 20480,
      "MD5checksum": "F77449179760C3B13F1E76110F07****"
    }
  ]
}

第二步:根据 fileSchema 解析 CSV 数据文件

  1. 获取并解压文件

    manifest.jsonfiles 数组中获取每个数据文件的 key(即文件路径)。根据该路径下载对应的 .csv.gz 压缩包。解压文件,得到 CSV 格式的原始数据。

    重要Key在 CSV 文件中经过了 URL 编码,在使用前需要先进行 URL 解码。
  2. 按序解析数据

    fileSchema 字段中定义的顺序作为 CSV 文件的列标题。逐行读取解压后的 CSV 文件。每一行代表一个对象(文件)的完整记录,每一列则对应 fileSchema 中指定的一个字段。

    CSV 内容示例:

    假设 fileSchema 为: Bucket,Key,Size,StorageClass,LastModifiedDate。那么对应的 CSV 内容格式如下:

    "my-source-bucket","images/photo.jpg",102400,"Standard","2023-10-26T08:00:00.000Z"
    "my-source-bucket","docs/report.pdf",2048000,"IA","2023-10-25T10:30:00.000Z"

清单文件

清单任务配置完成后,OSS会按清单规则指定的导出周期生成清单文件。清单文件的目录结构如下:

<dest-bucket-name>/
└── <dest-prefix>/
    └── <source-bucket-name>/
        └── <inventory-id>/
            ├── YYYY-MM-DDTHH-MMZ/  (扫描开始的UTC时间)
            │   ├── manifest.json   (清单任务的元数据文件)
            │   └── manifest.checksum (manifest.json 文件的 MD5 校验和)
            └── data/
                └── <uuid>.csv.gz   (GZIP 压缩的清单数据文件)

目录结构

说明

dest-prefix

该目录根据设置的清单报告名前缀生成,如果清单报告名前缀设置为空,将省略该目录。

source-bucket-name/

该目录根据配置清单报告的源Bucket名生成。

inventory_id/

该目录根据清单任务的规则名称生成。

YYYY-MM-DDTHH-MMZ/

该目录是标准的格林威治时间戳,表示开始扫描Bucket的时间,例如2020-05-17T16-00Z。该目录下包含了manifest.jsonmanifest.checksum文件。

data/

该目录下存放了包含源Bucket中的对象列表以及每个对象的元数据的清单文件,清单文件格式为使用GZIP压缩的CSV文件。

重要
  • 当导出的源BucketObject数量较多时,为方便用户下载和处理数据,程序会自动将清单文件切分成多个CSV压缩文件。CSV压缩文件按照uuid.csv.gzuuid-1.csv.gzuuid-2.csv.gz的顺序依次递增。可以从manifest.json文件中获取CSV文件列表,然后按照以上顺序依次解压CSV文件并读取清单数据。

  • Object的单条记录信息仅出现在一个清单文件内,不会分布到不同的清单文件。

清单功能生成的具体文件说明如下:

manifest文件

manifest文件包含manifest.jsonmanifest.checksum,详细说明如下:

  • manifest.json:提供了有关清单的元数据和其他基本信息。

    {
        "creationTimestamp": "1642994594",
        "destinationBucket": "destbucket",
        "fileFormat": "CSV",
        "fileSchema": "Bucket, Key, VersionId, IsLatest, IsDeleteMarker, Size, StorageClass, LastModifiedDate, ETag, IsMultipartUploaded, EncryptionStatus, ObjectAcl, TaggingCount, ObjectType, Crc64",
        "files": [{
                "MD5checksum": "F77449179760C3B13F1E76110F07****",
                "key": "destprefix/srcbucket/configid/data/a1574226-b5e5-40ee-91df-356845777c04.csv.gz",
                "size": 2046}],
        "sourceBucket": "srcbucket",
        "version": "2019-09-01"}

    各字段详细说明如下:

    字段名称

    说明

    creationTimestamp

    以纪元日期格式创建的时间戳,显示开始扫描源Bucket的时间。

    destinationBucket

    存放清单文件的目标Bucket。

    fileFormat

    清单文件的格式。

    fileSchema

    清单文件包含的字段,分为固定字段和可选字段。其中,固定字段的顺序是固定的,可选字段的排列顺序取决于配置清单规则时清单内容字段的排列顺序(控制台配置时以字段的勾选先后顺序为准)。 因此,建议以fileSchema中的字段顺序去解析csv.gz中的数据列,避免出现列和属性对应错误的情况。

    • 配置清单规则时如果对象版本选择了当前版本,则fileSchema中,先排列固定字段Bucket, Key,后续为可选字段。

    • 配置清单规则时如果对象版本选择了所有版本,则fileSchema中,先排列固定字段Bucket, Key, VersionId, IsLatest, IsDeleteMarker,后续为可选字段。

    files

    包含清单文件的MD5值、文件名完整路径及文件大小。

    sourceBucket

    配置清单规则的源Bucket。

    version

    清单版本号。

  • manifest.checksum:包含manifest.json文件的MD5值,例如8420A430CBD6B659A1C0DFC1C11A****

清单报告

清单报告存储在data/目录中,包含清单功能导出的文件信息。清单报告示例如下:

DCDD87B7-7499-4D22-B684-8B25B6F1C232.png

清单报告具体的字段顺序取决于配置清单规则时的清单内容字段排列顺序。以上清单报告示例中,各字段按从左到右的顺序说明如下。

字段名称

说明

Bucket

执行清单任务的源Bucket名称。

Key

BucketObject的名称。

Object名称使用URL编码,可按需解码。

VersionId

Object的版本ID。仅当配置的清单规则为导出所有版本时出现此字段。

  • 如果配置清单规则的Bucket未开启版本控制,则该字段显示为空。

  • 如果配置清单规则的Bucket已开启版本控制,则该字段显示为ObjectversionId。

IsLatest

Object版本是否为最新版本。仅当配置的清单规则为导出所有版本时出现此字段。

  • 如果配置清单规则的Bucket未开启版本控制,则该字段显示为True

  • 如果配置清单规则的Bucket已开启版本控制,且Object为最新版本时,则该字段显示为True。如果Object为历史版本,则该字段显示为False。

IsDeleteMarker

Object版本是否为删除标记。仅当配置的清单规则为导出所有版本时出现此字段。

  • 如果配置清单规则的Bucket未开启版本控制,则该字段默认显示为False

  • 如果配置清单规则的Bucket已开启版本控制,且Object为删除标记时,则该字段显示为True。如果Object不是删除标记,则该字段显示为False

Size

Object大小。

StorageClass

Object的存储类型。

LastModifiedDate

Object的最后修改时间,格式是格林威治时间,与北京时间相差8小时。

ETag

ObjectETag。

Object生成时会创建相应的ETag,用于标识一个Object的内容。

  • 通过PutObject接口创建的Object,ETag值是其内容的MD5值。

  • 通过其他方式创建的Object,ETag值是基于一定计算规则生成的唯一值,但不是其内容的MD5值。

IsMultipartUploaded

Object是否通过分片上传生成。如果是,则该字段值为True,否则为False

EncryptionStatus

Object是否已加密。若Object已加密,则该字段值为True,否则为False

ObjectAcl

Object的读写权限。更多信息,请参见Object ACL

TaggingCount

Object的标签个数。

ObjectType

Object类型。更多信息,请参见Object类型

Crc64

ObjectCRC64。

使用限制

  • 规则数量:单个 Bucket 最多支持 1000 条清单规则(通过 API/SDK)或 10 条(通过控制台)。

  • 地域限制:源 Bucket 和目标 Bucket 必须位于同一地域、同一阿里云账号下。金融云和无地域属性的 Bucket 不支持此功能。

计费说明

存储空间清单功能本身免费,但会产生以下关联费用:

  • API 请求费:配置和获取清单规则时产生的 Put 请求费用和Get请求费用。OSS 写入清单报告到目标 Bucket 时会产生 PUT 请求费用。下载和读取清单报告时,会产生 GET 请求费用。

  • 存储费用:生成的清单报告(manifest 文件和 csv.gz 文件)会占用目标 Bucket 的存储空间,按标准存储费用计费。

  • 外网流出费用:使用外网 Endpoint 下载和读取清单报告时,会产生外网流出流量费用。

为避免不必要的开销,请及时删除不再需要的清单规则,并使用生命周期规则自动清理过期的清单报告文件。

应用于生产环境

最佳实践

  • 最小权限:始终使用专用的、具备最小权限的 RAM 角色,切勿在生产环境中使用 AliyunOSSRole

  • 性能建议:对于高流量的源 Bucket,应将清单报告存储到另一个专用的 Bucket,避免因写入报告而产生的带宽竞争影响线上业务。

  • 成本优化:存储空间清单支持按天或按周导出清单文件。对于超过百亿文件的 Bucket,优先使用每周清单。同时,在目标 Bucket 上配置生命周期规则,自动删除超过 N 天(例如 30 天)的清单报告以节省存储成本。

    Bucket内的文件数量

    导出建议

    <100亿

    按需配置按天或按周导出

    100亿~500亿

    按周导出

    ≥500亿

    • 按前缀匹配分批导出

    • 通过提交工单提升导出Bucket文件数量的限制

  • 前缀分区:对于超大规模(如千亿级)的 Bucket,可按业务前缀创建多条清单规则,分而治之地生成报告,便于管理和处理。

风险防范

  • 数据审计:导出清单文件的过程中,由于Object的创建、删除或覆盖等操作,可能会导致最终输出的清单列表中不一定包含所有的Object。最后修改时间早于manifest.json文件中createTimeStamp字段显示时间的Object会出现在清单文件中;最后修改时间晚于createTimeStamp字段显示时间的Object可能不会出现在清单文件中。建议对清单列表中的Object进行操作之前,先使用HeadObject接口检查Object的属性。

  • 监控告警:监控目标 Bucket 的存储用量,防止清单文件无限制增长导致成本失控。监控 PutBucketInventory 等 API 的调用,以便追踪配置变更。

  • 变更管理:清单规则的变更(如修改前缀、频率)会影响下游的数据分析流程。所有变更应纳入版本控制和评审流程。

常见问题