# Channel Metadata (/zh-CN/realtime-media/rtm/build/state-and-attributes/channel-metadata)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `onStorageEvent` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `didReceiveStorageEvent` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `storage` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `storage` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `OnStorageEvent` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `storage` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `onStorageEvent` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `didReceiveStorageEvent` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
Storage 服务为你提供了 Channel Metadata 的能力，可以用来存储和分发你应用中的上下文数据，例如：道具、公告、成员列表和关系链等。Channel Metadata 在被设置、更新和删除的时候会触发 `storage` 事件通知，频道中的其他人会在 100 ms 内收到此信息。Storage 服务对于 Message Channel 和 Stream Channel 都有效，在使用的时候可以通过 `channelType` 参数区分。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

Channel Metadata 属于永久存储数据，一旦被设置将会长期保存在 RTM 数据库，不会因为你的频道被销毁而丢失，除非你手动删除。这将影响你的存储计费项，详见[计费规则](/zh-CN/realtime-media/rtm/reference/billing/billing-strategy)。

## 设置 Channel Metadata [#设置-channel-metadata]

你可以为指定频道设置一组 Channel Metadata，用以实现业务上房间级别的数据存储和实时通知，例如房间内的道具信息、拍卖品价格更新、群公告等。每个频道只能有一组 Channel Metadata，但每组 Channel Metadata 可以包含一个或多个 Metadata Item，相关限制详见 [API 使用限制](/zh-CN/realtime-media/rtm/reference/usage-limits-and-errors/api-limits)。每个 Metadata Item 包含 `key`、`value`、`revision` 等预定义字段。

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `setChannelMetadata` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/android/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `setChannelMetadata` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/ios/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `setChannelMetadata` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/web/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `setChannelMetadata` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/harmonyos/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `SetChannelMetadataAsync` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/unity/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `setChannelMetadata` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/flutter/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `setChannelMetadata` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/cpp/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `setChannelMetadata` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/swift/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
如果当前 Channel Metadata 或者 Metadata Item 不存在，该方法将为指定频道新增属性；如果 Channel Metadata 或者 Metadata Item 已存在，则会使用新值覆盖原有值。关于 `setChannelMetadata` 接口的更多信息详见 [Storage API 参考](/zh-CN/api-reference/rtm/web/storage)。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

以下示例展示了如何设置 Channel Metadata：

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
```java
Metadata metadata = new Metadata();
metadata.getItems().add(new MetadataItem("Quantity", "20"));
metadata.getItems().add(new MetadataItem("Announcement", "Welcome to our shop!"));
metadata.getItems().add(new MetadataItem("T-shirt", "100"));

mRtmClient.getStorage().setChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, new MetadataOptions(true, true), nullptr, new ResultCallback<Void>() {
    @Override
    public void onSuccess(Void responseInfo) {
        log(CALLBACK, "set channel metadata success");
    }

    @Override
    public void onFailure(ErrorInfo errorInfo) {
        log(ERROR, errorInfo.toString());
    }
});
```

上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。

当上述操作成功后，RTM SDK 会返回如下数据结构：

```java
@Override
public void onSuccess(Void responseInfo) {
}
```

同时，RTM 还会触发一个 `UPDATE` 类型的 `onStorageEvent` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
```objc
//get metadata
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];

//set metadata items
AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"20";
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"100";

NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;

AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;

[[rtm getStorage] setChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"setChannelMetadata success!!");
            } else {
                NSLog(@"setChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
```

上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。

当上述操作成功后，RTM SDK 会返回 `AgoraRtmCommonResponse` 数据结构。

同时，RTM 还会触发一个 `AgoraRtmStorageEventTypeUpdate` 类型的 `didReceiveStorageEvent` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
```javascript
const properties = {
    key : "Quantity",
    value : "20"
};

const announcement = {
    key : "Announcement",
    value : "Welcome to our shop!"
};

const price = {
    key : "T-shirt",
    value : "100"
};

const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true };

try {
    const result = await rtm.storage.setChannelMetadata("channel1", "MESSAGE", data, options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```

上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。上述代码使用了 `await/async` 编程模式，所以需要被定义在一个异步函数中。

当上述操作成功后，RTM SDK 会返回如下数据结构：

```javascript
{
    timestamp : "",                // 本次操作成功时间戳
    channelName : "channel1",      // 频道名
    channelType : "MESSAGE",       // 频道类型
    totalCount : 3                 // Metadata Item 个数
}
```

同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
```typescript
import { RtmChannelType, MetadataItem, MetadataOptions } from '@shengwang/rtm-full';
import { RtmError } from '@shengwang/rtm-full';

const metadata: MetadataItem[] = [
  { key: "Quantity", value: "20" },
  { key: "Announcement", value: "Welcome to our shop!" },
  { key: "T-shirt", value: "100" }
];

const options: MetadataOptions = {
  recordTs: true,
  recordUserId: true
};

try {
  const result = await rtmClient.storage.setChannelMetadata(
    "channel1",
    RtmChannelType.MESSAGE,
    {
      majorRevision: -1,
      items: metadata
    },
    options
  );
  console.log("Channel metadata set successfully:", result.channelName);
} catch (error) {
  console.error("setChannelMetadata failed:", error);
  if (error instanceof RtmError) {
    console.error(`setChannelMetadata failed: ${error.reason} (code: ${error.errorCode})`);
  }
}
```

上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。

当上述操作成功后，RTM SDK 会返回如下数据结构：

```typescript
{
    channelName: "channel1",              // 频道名
    channelType: RtmChannelType.MESSAGE   // 频道类型
}
```

同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。上述代码使用了 `await/async` 编程模式，所以需要被定义在一个异步函数中。

同时，RTM 还会触发一个 `UPDATE` 类型的 `OnStorageEvent` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
```dart
var properties = MetadataItem(
  key: 'Quantity',
  value: '20');

var announcement = MetadataItem(
  key: 'Announcement',
  value: 'Welcome to our shop!');

var price = MetadataItem(
  key: 'T-shirt',
  value: '200');

var metadata = [properties,announcement,price];

try{
    var (status,response) = await rtmClient.getStorage.setChannelMetadata(
        'channel1',
        RtmChannelType.message,
        metadata,
        recordTs: true,
        recordUserId: true);

    if (status.error == true) {
        print(status);
    } else {
        print(response);
    }
} catch (e) {
    print('something went wrong: $e');
}
```

上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。

当上述操作成功后，RTM SDK 会返回如下数据结构：

```dart
{
    channelName : 'channel1',                   // 频道名
    channelType : RtmChannelType.message,       // 频道类型
}
```

同时，RTM 还会触发一个 `update` 类型的 `storage` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
```cpp
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
MetadataItem item0("Quantity", "20");
MetadataItem item1("Announcement", "Welcome to our shop!");
MetadataItem item2("T-shirt", "100");
items.emplace_back(item0);
items.emplace_back(item1);
items.emplace_back(item2);

MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;

uint64_t requestId;
rtmClient->getStorage()->setChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);
```

调用该方法后，SDK 会触发 `onSetChannelMetadataResult` 回调并返回 API 调用结果。

```cpp
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
    void onSetChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
        if (errorCode != RTM_ERROR_OK) {
            // set channel metadata failed
        } else {
            // set channel metadata success
        }
    }
};
```

上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。

同时，RTM 还会触发一个 `RTM_STORAGE_EVENT_TYPE_UPDATE` 类型的 `onStorageEvent` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
```swift
// 获取元数据
let metadata = AgoraRtmMetadata()!

// 设置元数据项
let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "20"

let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"

let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "100"

// 创建元数据项数组
let itemArray = [properties, announcement, price]
metadata.items = itemArray

let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true

rtm.getStorage()?.setChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
    if errorInfo == nil {
        print("setChannelMetadata success!!")
    } else {
        if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
            print("setChannelMetadata failed, errorCode: \(errorCode), reason: \(reason)")
        }
    }
}

```

上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。

当上述操作成功后，RTM SDK 会返回 `AgoraRtmCommonResponse` 数据结构。

同时，RTM 还会触发一个 `update` 类型的 `didReceiveStorageEvent` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
```javascript
const properties = {
    key : "Quantity",
    value : "20"
};

const announcement = {
    key : "Announcement",
    value : "Welcome to our shop!"
};

const price = {
    key : "T-shirt",
    value : "100"
};

const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true };

try {
    const result = await rtm.storage.setChannelMetadata("channel1", "MESSAGE", data, options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```

上述示例代码中，我们为 Message Channel 类型的频道 `channel1` 设置了一组 Channel Metadata，其中包含三个 Metadata Item，分别为 `properties`、`announcement` 和 `price`。要求 RTM 服务在存储上述三个 Metadata Item 的同时，在每个 Metadata Item 上都额外添加上时间戳和修改者信息。上述代码使用了 `await/async` 编程模式，所以需要被定义在一个异步函数中。

当上述操作成功后，RTM SDK 会返回如下数据结构：

```javascript
{
    timestamp : "",                // 本次操作成功时间戳
    channelName : "channel1",      // 频道名
    channelType : "MESSAGE",       // 频道类型
    totalCount : 3                 // Metadata Item 个数
}
```

同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100 ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

## 获取 Channel Metadata [#获取-channel-metadata]

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
你可以调用 `getChannelMetadata` 方法获得指定频道的全部 Channel Metadata。关于 `getChannelMetadata` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/android/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
你可以调用 `getChannelMetadata` 方法获得指定频道的全部 Channel Metadata。关于 `getChannelMetadata` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/ios/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
你可以调用 `getChannelMetadata` 方法获得指定频道的全部 Channel Metadata。关于 `getChannelMetadata` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/web/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
你可以调用 `getChannelMetadata` 方法获得指定频道的全部 Channel Metadata。关于 `getChannelMetadata` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/harmonyos/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
你可以调用 `GetChannelMetadataAsync` 方法获得指定频道的全部 Channel Metadata。关于 `GetChannelMetadataAsync` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/unity/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
你可以调用 `getChannelMetadata` 方法获得指定频道的全部 Channel Metadata。关于 `getChannelMetadata` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/flutter/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
你可以调用 `getChannelMetadata` 方法获得指定频道的全部 Channel Metadata。关于 `getChannelMetadata` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/cpp/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
你可以调用 `getChannelMetadata` 方法获得指定频道的全部 Channel Metadata。关于 `getChannelMetadata` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/swift/storage)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
你可以调用 `getChannelMetadata` 方法获得指定频道的全部 Channel Metadata。关于 `getChannelMetadata` 接口的更多信息见 [Storage API 参考](/zh-CN/api-reference/rtm/web/storage)。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

以下示例代码展示了如何获得 Channel Metadata：

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
```java
mRtmClient.getStorage().getChannelMetadata("channel1", RtmChannelType.MESSAGE, new ResultCallback<Metadata>() {
    @Override
    public void onSuccess(Metadata data) {
        log(CALLBACK, "get channel metadata success");
        log(INFO, "metadata major revision: " + data.getMajorRevision());
        for (MetadataItem item : data.getItems()) {
            log(INFO, item.toString());
        }
    }

    @Override
    public void onFailure(ErrorInfo errorInfo) {
        log(ERROR, errorInfo.toString());
    }
});
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
```objc
[[rtm getStorage] getChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage completion:^(AgoraRtmGetMetadataResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
    if (errorInfo == nil) {
        NSLog(@"getChannelMetadata success!!");
        AgoraRtmMetadata* data = response.data; //get storage data;
    } else {
        NSLog(@"getChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
    }
}];
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
```javascript
try {
    const result = await rtm.storage.getChannelMetadata("channel1","MESSAGE");
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
```typescript
import { RtmChannelType } from '@shengwang/rtm-full';
import { RtmError } from '@shengwang/rtm-full';

try {
  const result = await rtmClient.storage.getChannelMetadata("channel1", RtmChannelType.MESSAGE);
  console.log("Channel metadata retrieved:");
  console.log("  Major revision:", result.data.majorRevision);
  console.log("  Items:");
  result.data.items.forEach(item => {
    console.log(`    ${item.key}: ${item.value}`);
    console.log(`      Revision: ${item.revision}`);
    console.log(`      Author: ${item.authorUserId}`);
    console.log(`      Updated at: ${item.updateTs}`);
  });
} catch (error) {
  console.error("getChannelMetadata failed:", error);
  if (error instanceof RtmError) {
    console.error(`getChannelMetadata failed: ${error.reason} (code: ${error.errorCode})`);
  }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
```dart
try{
    var (status,response) = await rtmClient.getStorage.getChannelMetadata(
        'channel1',
        RtmChannelType.message);

    if (status.error == true) {
        print(status);
    } else {
        print(response);
    }
} catch (e) {
    print('something went wrong: $e');
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
```cpp
uint64_t requestId;
rtmClient->getStorage()->getChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, requestId);
```

调用该方法后，SDK 会触发 `onGetChannelMetadataResult` 回调并返回 API 调用结果。

```cpp
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
    void onGetChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, const Metadata& data, RTM_ERROR_CODE errorCode) override {
        if (errorCode != RTM_ERROR_OK) {
            printf("GetChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
        } else {
            printf("GetChannelMetadata success %d channel: %s, channel type: %d\n");
            for (int i = 0 ; i < data.itemCount; i++) {
                printf("key: %s value: %s revision: %lld\n", data.items[i].key, data.items[i].value, data.items[i].revision);
            }
        }
    }
};
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
```swift
rtm.getStorage()?.getChannelMetadata(channelName: "channel1", channelType: .message) { response, errorInfo in
    if errorInfo == nil {
        print("getChannelMetadata success!!")
        if let data = response?.data {
        }
    } else {
        if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
            print("getChannelMetadata failed, errorCode: \(errorCode), reason: \(reason)")
        }
    }
}

```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
```javascript
try {
    const result = await rtm.storage.getChannelMetadata("channel1","MESSAGE");
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

当上述操作成功后，RTM SDK 会返回如下数据结构：

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
```java
{
    majorRevision: 734874892,
    metadata:{
        {
            key:"Quantity",
            value:"20",
            revision:734874888,
            updateTs:1688978391900,
            authorUserId:"Tony"
        },
        {
            key:"Announcement",
            value:"Welcome to our Shop!",
            revision:734874333,
            updated:1688978391800,
            authorUid:"Tomas"
        },
        {
            key:"T-shirt",
            value:"100",
            revision:734874222,
            updated:168897839100,
            authorUid:"Adam"
        }
    }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
```objc
{
    majorRevision: 734874892,
    metadata:{
        "Quantity":{
            value:"20",
            revision:734874888,
            updated:1688978391900,
            authorUid:"Tony"
        },
        "Announcement":{
            value:"Welcome to our Shop!",
            revision:734874333,
            updated:1688978391800,
            authorUid:"Tomas"
        },
        "T-shirt":{
            value:"100",
            revision:734874222,
            updated:168897839100,
            authorUid:"Adam"
        }
    }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
```javascript
{
    totalCount: 3,
    majorRevision: 734874892,
    metadata:{
        "Quantity":{
            value:"20",
            revision:734874888,
            updated:1688978391900,
            authorUid:"Tony"
        },
        "Announcement":{
            value:"Welcome to our Shop!",
            revision:734874333,
            updated:1688978391800,
            authorUid:"Tomas"
        },
        "T-shirt":{
            value:"100",
            revision:734874222,
            updated:168897839100,
            authorUid:"Adam"
        }
    },
    channelName: "channel1",
    channelType: "MESSAGE"
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
```typescript
{
    channelName: "channel1",
    channelType: RtmChannelType.MESSAGE,
    data: {
        majorRevision: 734874892,
        items: [
            {
                key: "Quantity",
                value: "20",
                revision: 734874888,
                updateTs: 1688978391900,
                authorUserId: "Tony"
            },
            {
                key: "Announcement",
                value: "Welcome to our Shop!",
                revision: 734874333,
                updateTs: 1688978391800,
                authorUserId: "Tomas"
            },
            {
                key: "T-shirt",
                value: "100",
                revision: 734874222,
                updateTs: 168897839100,
                authorUserId: "Adam"
            }
        ]
    }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
```dart
{
    data:{
        majorRevision: 734874892,
        itemCount: 3,
        items: [
            {
                key: 'Quantity',
                value:'20',
                authorUid: 'Tony',
                revision: 734874888,
                updateTs: 1688978391900
            },{
                key:'Announcement',
                value:'Welcome to our Shop!',
                authorUid:'Tony',
                revision: 734874333,
                updateTs: 1688978391800
            },{
                key: 'T-shirt',
                value: '100',
                authorUid: 'Tony',
                revision: 734874222,
                updateTs: 168897839100
            }]
    },
    channelName: 'channel1',
    channelType: RtmChannelType.message
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
```cpp
{
    majorRevision: 734874892,
    metadata:{
        "Quantity":{
            value:"20",
            revision:734874888,
            updated:1688978391900,
            authorUid:"Tony"
        },
        "Announcement":{
            value:"Welcome to our Shop!",
            revision:734874333,
            updated:1688978391800,
            authorUid:"Tomas"
        },
        "T-shirt":{
            value:"100",
            revision:734874222,
            updated:168897839100,
            authorUid:"Adam"
        }
    }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
```swift
{
    majorRevision: 734874892,
    metadata:{
        "Quantity":{
            value:"20",
            revision:734874888,
            updated:1688978391900,
            authorUid:"Tony"
        },
        "Announcement":{
            value:"Welcome to our Shop!",
            revision:734874333,
            updated:1688978391800,
            authorUid:"Tomas"
        },
        "T-shirt":{
            value:"100",
            revision:734874222,
            updated:168897839100,
            authorUid:"Adam"
        }
    }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
```javascript
{
    totalCount: 3,
    majorRevision: 734874892,
    metadata:{
        "Quantity":{
            value:"20",
            revision:734874888,
            updated:1688978391900,
            authorUid:"Tony"
        },
        "Announcement":{
            value:"Welcome to our Shop!",
            revision:734874333,
            updated:1688978391800,
            authorUid:"Tomas"
        },
        "T-shirt":{
            value:"100",
            revision:734874222,
            updated:168897839100,
            authorUid:"Adam"
        }
    },
    channelName: "channel1",
    channelType: "MESSAGE"
}
```
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

## 更新 Channel Metadata [#更新-channel-metadata]

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
你可以使用 `updateChannelMetadata` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
你可以使用 `updateChannelMetadata` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
你可以使用 `updateChannelMetadata` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
你可以使用 `updateChannelMetadata` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
你可以使用 `UpdateChannelMetadataAsync` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
你可以使用 `updateChannelMetadata` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
你可以使用 `updateChannelMetadata` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
你可以使用 `updateChannelMetadata` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
你可以使用 `updateChannelMetadata` 来更新更新频道中已存在的 Metadata Item。如果该 Metadata Item 不存在，则会返回错误。该接口可用于如下需要权限控制的业务场景：
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

- 在电商拍卖的场景中，只有管理员或者商品的拥有者才具备上架新商品和设置新属性的权限，而竞拍者只有修改价格属性（竞价）的权限。
- 在游戏场景中，只有管理员才能设置房间内道具的权限。

以下示例代码展示了如何更新 Metadata Item 值：

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
```java
Metadata metadata = new Metadata();
metadata.getItems().add(new MetadataItem("T-shirt", "299"));
MetadataOptions options = new MetadataOptions()
options.setRecordTs(true);
options.setRecordUserId(true);

mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, "", new ResultCallback<Void>() {
    @Override
    public void onSuccess(Void responseInfo) {
        log(CALLBACK, "update channel metadata success");
    }

    @Override
    public void onFailure(ErrorInfo errorInfo) {
        log(ERROR, errorInfo.toString());
    }
});
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
```objc
//get metadata
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];

//set metadata items
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"299";

NSArray *item_array = [NSArray arrayWithObjects:price];
metadata.items = item_array;

AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;

[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"updateChannelMetadata success!!");
            } else {
                NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
```javascript
const price = {
    key : "T-shirt",
    value : "299"
};

const data = [price];
const options = { addTimeStamp : true, addUserId : true };

try {
    const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
```typescript
import { RtmChannelType, MetadataItem, MetadataOptions } from '@shengwang/rtm-full';
import { RtmError } from '@shengwang/rtm-full';

const metadata: MetadataItem[] = [
  { key: "T-shirt", value: "299" }
];

const options: MetadataOptions = {
  recordTs: true,
  recordUserId: true
};

try {
  const result = await rtmClient.storage.updateChannelMetadata(
    "channel1",
    RtmChannelType.MESSAGE,
    {
      majorRevision: -1,
      items: metadata
    },
    options
  );
  console.log("Channel metadata updated successfully:", result.channelName);
} catch (error) {
  console.error("updateChannelMetadata failed:", error);
  if (error instanceof RtmError) {
    console.error(`updateChannelMetadata failed: ${error.reason} (code: ${error.errorCode})`);
  }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
```dart
var price = MetadataItem(
  key: 'T-shirt',
  value: '299');

var metadata = [price];

try{
    var (status,response) = await rtmClient.getStorage.updateChannelMetadata(
        'channel1',
        RtmChannelType.message,
        metadata,
        recordTs: true,
        recordUserId: true);

    if (status.error == true) {
        print(status);
    } else {
        print(response);
    }
} catch (e) {
    print('something went wrong: $e');
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
```cpp
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
items.push_back(MetadataItem("T-shirt", "299"));
metadata.items = items.data();
metadata.itemCount = items.size();

MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;

uint64_t requestId;
rtmClient->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);
```

调用该方法后，SDK 会触发 `onUpdateChannelMetadataResult` 回调并返回 API 调用结果。

```cpp
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
    void onUpdateChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
        if (errorCode != RTM_ERROR_OK) {
            printf("UpdateChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
        } else {
            printf("UpdateChannelMetadata success\n");
        }
    }
};
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
```swift
// 获取元数据
let metadata = AgoraRtmMetadata()!

// 设置元数据项
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "299"

// 创建元数据项数组
let itemArray = [price]
metadata.items = itemArray

let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true

// 更新频道元数据
rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
    if errorInfo == nil {
        print("updateChannelMetadata success!!")
    } else {
        if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
            print("updateChannelMetadata failed, errorCode: \(errorCode), reason: \(reason)")
        }
    }
}

```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
```javascript
const price = {
    key : "T-shirt",
    value : "299"
};

const data = [price];
const options = { addTimeStamp : true, addUserId : true };

try {
    const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

上述示例代码将 Metadata Item `price` 中 `T-shirt` 对应的值更新为 `299`，即把 T 恤的价格更改为 299。

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
当调用成功后，RTM SDK 会返回如下数据结构：

```java
@Override
public void onSuccess(Void responseInfo) {
    log(CALLBACK, "update channel metadata success");
}
```

同时，RTM 还会触发一个 `UPDATE` 类型的 `onStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人。`onStorageEvent` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
当上述操作成功后，RTM SDK 会返回 `AgoraRtmCommonResponse` 数据结构。

同时，RTM 还会触发一个 `AgoraRtmStorageEventTypeUpdate` 类型的 `didReceiveStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人。`didReceiveStorageEvent` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
当调用成功后，RTM SDK 会返回如下数据结构：

```javascript
{
    timestamp : "",                // 本次操作成功时间戳
    channelName : "channel1",      // 频道名
    channelType : "MESSAGE",       // 频道类型
    totalCount : 3                 // Metadata Item 个数
}
```

同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100ms 内通知到频道中的其他人。`storage` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
当调用成功后，RTM SDK 会返回如下数据结构：

```typescript
{
    channelName: "channel1",              // 频道名
    channelType: RtmChannelType.MESSAGE   // 频道类型
}
```

同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100ms 内通知到频道中的其他人。`storage` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
同时，RTM 还会触发一个 `UPDATE` 类型的 `OnStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人。`OnStorageEvent` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
当调用成功后，RTM SDK 会返回如下数据结构：

```dart
{
    channelName : 'channel1',                   // 频道名
    channelType : RtmChannelType.message,       // 频道类型
}
```

同时，RTM 还会触发一个 `update` 类型的 `storage` 事件通知，并在 100ms 内通知到频道中的其他人。`storage` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
同时，RTM 还会触发一个 `RTM_STORAGE_EVENT_TYPE_UPDATE` 类型的 `onStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人。`onStorageEvent` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
当上述操作成功后，RTM SDK 会返回 `AgoraRtmCommonResponse` 数据结构。

同时，RTM 还会触发一个 `update` 类型的 `didReceiveStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人。`didReceiveStorageEvent` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
当调用成功后，RTM SDK 会返回如下数据结构：

```javascript
{
    timestamp : "",                // 本次操作成功时间戳
    channelName : "channel1",      // 频道名
    channelType : "MESSAGE",       // 频道类型
    totalCount : 3                 // Metadata Item 个数
}
```

同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100ms 内通知到频道中的其他人。`storage` 详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

## 删除 Channel Metadata [#删除-channel-metadata]

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `removeChannelMetadata` 进行删除操作：

```java
Metadata metadata = new Metadata();
MetadataItem announcement = new MetadataItem();
announcement.setKey("Announcement");
metadata.getItems().add(announcement);

mRtmClient.getStorage().removeChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, new MetadataOptions(false, false), "", new ResultCallback<Void>() {
    @Override
    public void onSuccess(Void responseInfo) {
        log(CALLBACK, "remove channel metadata success");
    }

    @Override
    public void onFailure(ErrorInfo errorInfo) {
        log(ERROR, errorInfo.toString());
    }
});
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `removeChannelMetadata` 进行删除操作：

```objc
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
NSArray *item_array = [NSArray arrayWithObjects:announcement];
metadata.items = item_array;

[[rtm getStorage] removeChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:nil lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"removeChannelMetadata success!!");
            } else {
                NSLog(@"removeChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `removeChannelMetadata` 进行删除操作：

```javascript
const announcement = {
    key : "Announcement",
};

const data = [announcement];

const options = {
    data : data,
};

try {
    const result = await rtm.storage.removeChannelMetadata("channel1", "MESSAGE", options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `removeChannelMetadata` 进行删除操作：

```typescript
import { RtmChannelType, MetadataItem } from '@shengwang/rtm-full';
import { RtmError } from '@shengwang/rtm-full';

const metadata: MetadataItem[] = [
  { key: "Announcement", value: "" }  // value is ignored for remove
];

try {
  await rtmClient.storage.removeChannelMetadata(
    "channel1",
    RtmChannelType.MESSAGE,
    {
      majorRevision: -1,
      items: metadata
    }
  );
  console.log("Channel metadata removed successfully");
} catch (error) {
  console.error("removeChannelMetadata failed:", error);
  if (error instanceof RtmError) {
    console.error(`removeChannelMetadata failed: ${error.reason} (code: ${error.errorCode})`);
  }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `RemoveChannelMetadataAsync` 进行删除操作：
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `removeChannelMetadata` 进行删除操作：

```dart
var announcement = MetadataItem(key: 'Announcement');
var metadata = [announcement];

try{
    var (status,response) = await rtmClient.getStorage.removeChannelMetadata(
        'channel1',
        RtmChannelType.message,
        metadata: metadata);

    if (status.error == true) {
        print(status);
    } else {
        print(response);
    }
} catch (e) {
    print('something went wrong: $e');
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `removeChannelMetadata` 进行删除操作：

```cpp
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
MetadataItem item;
item.key = "Announcement";
items.push_back(item);
metadata.items = items.data();
metadata.itemCount = items.size();

MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;

uint64_t requestId;
rtmClient->getStorage()->removeChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);
```

调用该方法后，SDK 会触发 `onRemoveChannelMetadataResult` 回调并返回 API 调用结果。

```cpp
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
    void onRemoveChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
        if (errorCode != RTM_ERROR_OK) {
            printf("RemoveChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
        } else {
            printf("RemoveChannelMetadata success\n");
        }
    }
};
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `removeChannelMetadata` 进行删除操作：

```swift
let metadata = AgoraRtmMetadata()!

let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"

let itemArray = [announcement]
metadata.items = itemArray

rtm.getStorage()?.removeChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: nil, lock: nil) { response, errorInfo in
    if errorInfo == nil {
        print("removeChannelMetadata success!!")
    } else {
        if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
            print("removeChannelMetadata failed, errorCode: \(errorCode), reason: \(reason)")
        }
    }
}

```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
如果你不再需要 Channel Metadata 或者某几个 Metadata Item，你可以参考以下示例代码使用 `removeChannelMetadata` 进行删除操作：

```javascript
const announcement = {
    key : "Announcement",
};

const data = [announcement];

const options = {
    data : data,
};

try {
    const result = await rtm.storage.removeChannelMetadata("channel1", "MESSAGE", options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

上述示例代码中，通过 Metadata Item `announcement` 中的 `key` 删除对应的元数据，其 `value` 设为任何数值都不影响输出操作。

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
如果你不填充任何 `MetadataItem`，则会删除该频道中整组 Channel Metadata，示例代码如下：

```java
Metadata metadata = new Metadata();
mRtmClient.getStorage().removeChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, new MetadataOptions(true, true), "", new ResultCallback<Void>() {
    @Override
    public void onSuccess(Void responseInfo) {
        log(CALLBACK, "remove channel metadata success");
    }

    @Override
    public void onFailure(ErrorInfo errorInfo) {
        log(ERROR, errorInfo.toString());
    }
});
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
如果你不填充任何 `AgoraRtmMetadataItem`，则会删除该频道中整组 Channel Metadata，示例代码如下：

```objc
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];;

[[rtm getStorage] removeChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:nil lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"removeChannelMetadata success!!");
            } else {
                NSLog(@"removeChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
如果你不填写 `options` 中的 `data` 参数，则会删除该频道中整组 Channel Metadata，示例代码如下：

```javascript
const options = {};

try {
    const result = await rtm.storage.removeChannelMetadata("channel1", "MESSAGE", options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
如果你传入空的 `items` 数组，则会删除该频道中整组 Channel Metadata，示例代码如下：

```typescript
import { RtmChannelType } from '@shengwang/rtm-full';
import { RtmError } from '@shengwang/rtm-full';

try {
  // Empty items array = remove all metadata
  await rtmClient.storage.removeChannelMetadata(
    "channel1",
    RtmChannelType.MESSAGE,
    {
      majorRevision: -1,
      items: []  // 空数组表示删除所有元数据
    }
  );
  console.log("All channel metadata removed successfully");
} catch (error) {
  console.error("removeChannelMetadata failed:", error);
  if (error instanceof RtmError) {
    console.error(`removeChannelMetadata failed: ${error.reason} (code: ${error.errorCode})`);
  }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
如果你不填写 `metadata` 字段，则会删除该频道中整组 Channel Metadata，示例代码如下：

```dart
try{
    var (status,response) = await rtmClient.getStorage.removeChannelMetadata(
        'channel1',
        RtmChannelType.message);

    if (status.error == true) {
        print(status);
    } else {
        print(response);
    }
} catch (e) {
    print('something went wrong: $e');
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
如果你不填充任何 `MetadataItem`，则会删除该频道中整组 Channel Metadata，示例代码如下：

```cpp
Metadata metadata;

uint64_t requestId;
rtmClient->getStorage()->removeChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, MetadataOptions(), "lockName", requestId);
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
如果你不填充任何 `AgoraRtmMetadataItem`，则会删除该频道中整组 Channel Metadata，示例代码如下：

```objc
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];;

[[rtm getStorage] removeChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:nil lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"removeChannelMetadata success!!");
            } else {
                NSLog(@"removeChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
如果你不填写 `options` 中的 `data` 参数，则会删除该频道中整组 Channel Metadata，示例代码如下：

```javascript
const options = {};

try {
    const result = await rtm.storage.removeChannelMetadata("channel1", "MESSAGE", options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

通常在销毁频道的过程中你会需要删除整组 Channel Metadata。Channel Metadata 数据一旦被删除将无法恢复，如果你对数据具有恢复的需求，那么你需要在删除 Channel Metadata 之前做好数据备份。

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
同时，RTM 还会触发一个 `UPDATE` 类型的 `onStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
同时，RTM 还会触发一个 `AgoraRtmStorageEventTypeUpdate` 类型的 `didReceiveStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
同时，RTM 还会触发一个 `UPDATE` 类型的 `OnStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
同时，RTM 还会触发一个 `update` 类型的 `storage` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
同时，RTM 还会触发一个 `RTM_STORAGE_EVENT_TYPE_UPDATE` 类型的 `onStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
同时，RTM 还会触发一个 `update` 类型的 `didReceiveStorageEvent` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
同时，RTM 还会触发一个 `UPDATE` 类型的 `storage` 事件通知，并在 100ms 内通知到频道中的其他人，详见[事件通知](/zh-CN/realtime-media/rtm/build/state-and-attributes/metadata-events)。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

## CAS 控制 [#cas-控制]

Channel Metadata 同时也引进了版本控制逻辑 CAS（Compare And Set），该方法提供两种独立的版本控制字段，你可以根据实际业务场景设置任意一种或多种：

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
- 通过 `Metadata` 中的 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
- 通过 `MetadataItem` 中的 `revision` 属性开启某个 Metadata Item 的版本号校验。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
- 通过 `AgoraRtmMetadata` 中的 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
- 通过 `AgoraRtmMetadataItem` 中的 `revision` 属性开启某个 Metadata Item 的版本号校验。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
- 通过 `options` 中的 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
- 通过 `Metadata` 中的 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
- 通过 `MetadataItem` 中的 `revision` 属性开启某个 Metadata Item 的版本号校验。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
- 通过 `RtmMetadata` 中的 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
- 通过 `MetadataItem` 中的 `revision` 属性开启某个 Metadata Item 的版本号校验。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
- 通过 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
- 通过 `Metadata` 中的 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
- 通过 `MetadataItem` 中的 `revision` 属性开启某个 Metadata Item 的版本号校验。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
- 通过 `AgoraRtmMetadata` 中的 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
- 通过 `AgoraRtmMetadataItem` 中的 `revision` 属性开启某个 Metadata Item 的版本号校验。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
- 通过 `options` 中的 `majorRevision` 属性开启整组 Channel Metadata 的版本号校验。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

设置 Channel Metadata 或频道 Metadata Item 时，配合版本属性可以控制本次调用是否开启版本号校验，逻辑如下：

- 版本属性默认为 `-1` ，本次调用不开启 CAS 验证。如果 Channel Metadata 或频道 Metadata Item 已存在，则该值会被最新值覆盖；如果 Channel Metadata 或频道 Metadata Item 不存在，则 SDK 会创建该值。
- 版本属性为正整数时，本次调用开启 CAS 验证。如果 Channel Metadata 或频道 Metadata Item 已存在，则 SDK 会在版本号验证成功后更新对应的值；否则 SDK 会返回错误码。

你可以在以下场景中应用 CAS 控制：

- 在竞拍场景中，多人同时对一件商品竞拍时，最先出价者操作成功，而其余人返回错误，同时也能获得最新价格信息。
- 在抢红包场景中，红包被第一个人抢到且只会被占有一次，而其余人返回错误。

以下示例代码展示了如何使用 `majorRevision` 和 `revision` 更新 Channel Metadata 和 Metadata Item：

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
```java
Metadata metadata = new Metadata();
metadata.setMajorRevision(734874892);
metadata.getItems().add(new MetadataItem("Quantity", "30", 734874888));
metadata.getItems().add(new MetadataItem("Announcement", "Welcome to our shop!"));
metadata.getItems().add(new MetadataItem("T-shirt", "101", 734874222));
MetadataOptions options = new MetadataOptions()
options.setRecordTs(true);
options.setRecordUserId(true);

mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, "", new ResultCallback<Void>() {
    @Override
    public void onSuccess(Void responseInfo) {
        log(CALLBACK, "update channel metadata success");
    }

    @Override
    public void onFailure(ErrorInfo errorInfo) {
        log(ERROR, errorInfo.toString());
    }
});
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
```objc
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];

AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"30";
properties.revision = 734874888;
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"101";
price.revision = 734874222;
NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;
metadata.majorRevision = 734874892; // set marjor revision

AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;

[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"updateChannelMetadata success!!");
            } else {
                NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
```javascript
const properties = {
    key : "Quantity",
    value : "30",
    revision : 734874888
};

const announcement = {
    key : "Announcement",
    value : "Welcome to our shop!"
};

const price = {
    key : "T-shirt",
    value : "101",
    revision : 734874222
};

const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true, majorRevision : 734874892 };

try {
    const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
```typescript
import { RtmChannelType, Metadata, MetadataItem } from '@shengwang/rtm-full';

// Define metadata items with revision control
const metadata: Metadata = {
  majorRevision: 734874892,
  items: [
    { key: 'Quantity', value: '30', revision: 734874888 },
    { key: 'Announcement', value: 'Welcome to our shop!' },
    { key: 'T-shirt', value: '101', revision: 734874222 }
  ]
};

try {
  const result = await rtmClient.storage.updateChannelMetadata(
    'channel1',
    RtmChannelType.MESSAGE,
    metadata,
    {
      recordTs: true,
      recordUserId: true
    }
  );
  console.log('Update channel metadata success:', result.channelName);
} catch (error) {
  if (error instanceof RtmError) {
    console.error('Update channel metadata failed:', error.errorCode, error.reason);
  }
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
```dart
var properties = MetadataItem(
  key: 'Quantity',
  value: '20'
  revision:734874888);

var announcement = MetadataItem(
  key: 'Announcement',
  value: 'Welcome to our shop!');

var price = MetadataItem(
  key: 'T-shirt',
  value: '200'
  revision:734874222);

var metadata = [properties,announcement,price];

try{
    var (status,response) = await rtmClient.getStorage.setChannelMetadata(
        'channel1',
        RtmChannelType.message,
        metadata,
        majorRevision: 734874892,
        recordTs: true,
        recordUserId: true);

    if (status.error == true) {
        print(status);
    } else {
        print(response);
    }
} catch (e) {
    print('something went wrong: $e');
}
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
```cpp
Metadata metadata;
metadata.majorRevision = 734874892;
std::vector<MetadataItem> items;
items.emplace_back(MetadataItem("Quantity", "30", 734874888));
items.emplace_back(MetadataItem("Announcement", "Welcome to our shop!"));
items.emplace_back(MetadataItem("T-shirt", "101", 734874222));
metadata.items = items.data();
metadata.itemCount = items.size();

MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;

uint64_t requestId;
rtmClient->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);
```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
```swift
let metadata = AgoraRtmMetadata()

let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "30"
properties.revision = 734874888

let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"

let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "101"
price.revision = 734874222

let itemArray = [properties, announcement, price]
metadata.items = itemArray

metadata.majorRevision = 734874892

let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true

rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
    if errorInfo == nil {
        print("updateChannelMetadata success!!")
    } else {
        if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
            print("updateChannelMetadata failed, errorCode: \(errorCode), reason: \(reason)")
        }
    }
}

```
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
```javascript
const properties = {
    key : "Quantity",
    value : "30",
    revision : 734874888
};

const announcement = {
    key : "Announcement",
    value : "Welcome to our shop!"
};

const price = {
    key : "T-shirt",
    value : "101",
    revision : 734874222
};

const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true, majorRevision : 734874892 };

try {
    const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

上述例子中，我们对 Channel Metadata 和 Metadata Item `properties` 和 `price` 开启 CAS 验证并设置 `majorRevision` 为 `734874892`。服务器在接收到接口调用请求后，会首先验证接口调用提供的 `majorRevision` 与数据库最新值是否匹配，如果不匹配，则返回错误；如果匹配，则继续验证 Metadata Item 的 `revision`，逻辑与验证 Channel Metadata 相同。

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
如果你开启了版本控制能力，那么你需要关注 `onStorageEvent` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
如果你开启了版本控制能力，那么你需要关注 `didReceiveStorageEvent` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
如果你开启了版本控制能力，那么你需要关注 `storage` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
如果你开启了版本控制能力，那么你需要关注 `storage` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
如果你开启了版本控制能力，那么你需要关注 `OnStorageEvent` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
如果你开启了版本控制能力，那么你需要关注 `storage` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
如果你开启了版本控制能力，那么你需要关注 `onStorageEvent` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
如果你开启了版本控制能力，那么你需要关注 `didReceiveStorageEvent` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
如果你开启了版本控制能力，那么你需要关注 `storage` 事件通知以获取 `majorRevision` 和 `revision` 更新后的值，以便于在接口调用时提供最新值。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

## Lock 的使用 [#lock-的使用]

Lock 为用户提供了对临界资源互斥访问的能力，你可以解决用户在访问共享资源时的竞争问题。例如，在频道中特定时间只能允许一位管理员存在，且只有管理员才能对 Channel Metadata 进行设置、删除和修改等操作。

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `setChannelMetadata`、`updateChannelMetadata`、`removeChannelMetadata` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `setChannelMetadata`、`updateChannelMetadata`、`removeChannelMetadata` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `setChannelMetadata`、`updateChannelMetadata`、`removeChannelMetadata` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `setChannelMetadata`、`updateChannelMetadata`、`removeChannelMetadata` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `SetChannelMetadataAsync`、`UpdateChannelMetadataAsync`、`RemoveChannelMetadataAsync` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `setChannelMetadata`、`updateChannelMetadata`、`removeChannelMetadata` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `setChannelMetadata`、`updateChannelMetadata`、`removeChannelMetadata` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `setChannelMetadata`、`updateChannelMetadata`、`removeChannelMetadata` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
相比于 CAS 对于 Channel Metadata 数据版本的控制能力，Lock 为你提供了更为上层的控制能力，它直接决定了你是否具备调用 `setChannelMetadata`、`updateChannelMetadata`、`removeChannelMetadata` 三个接口的权限。如果你不是 Lock 的拥有者，则无法通过调用上述三个接口对 Channel Metadata 进行设置和修改。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>

以下代码展示了如何使用 Lock 来更新 Channel Metadata：

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
```java
Metadata metadata = new Metadata();
metadata.getItems().add(new MetadataItem("Quantity", "40"));
metadata.getItems().add(new MetadataItem("Announcement", "Welcome to our Shop!"));
metadata.getItems().add(new MetadataItem("T-shirt", "300"));
MetadataOptions options = new MetadataOptions()
options.setRecordTs(true);
options.setRecordUserId(true);

String lockName = "manage";
mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, lockName, new ResultCallback<Void>() {
    @Override
    public void onSuccess(Void responseInfo) {
        log(CALLBACK, "update channel metadata success");
    }

    @Override
    public void onFailure(ErrorInfo errorInfo) {
        log(ERROR, errorInfo.toString());
    }
});
```

上述示例代码的前提条件之一是调用 `updateChannelMetadata` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
```objc
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];

AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"40";
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"300";

NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;

AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;

[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:@"manage" completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"updateChannelMetadata success!!");
            } else {
                NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
```

上述示例代码的前提条件之一是调用 `updateChannelMetadata` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
```javascript
const properties = {
    key : "Quantity",
    value : "40"
};

const announcement = {
    key : "Announcement",
    value : "Welcome to our Shop!"
};

const price = {
    key : "T-shirt",
    value : "300"
};

const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true, lockName : "manage" };

try {
    const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```

上述示例代码的前提条件之一是调用 `updateChannelMetadata` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="harmonyos" />
```typescript
import { RtmChannelType, Metadata } from '@shengwang/rtm-full';

// Define metadata items
const metadata: Metadata = {
  majorRevision: -1,
  items: [
    { key: 'Quantity', value: '40' },
    { key: 'Announcement', value: 'Welcome to our Shop!' },
    { key: 'T-shirt', value: '300' }
  ]
};

try {
  const result = await rtmClient.storage.updateChannelMetadata(
    'channel1',
    RtmChannelType.MESSAGE,
    metadata,
    {
      lockName: 'manage',
      recordTs: true,
      recordUserId: true
    }
  );
  console.log('Update channel metadata with lock success:', result.channelName);
} catch (error) {
  if (error instanceof RtmError) {
    console.error('Update channel metadata failed:', error.errorCode, error.reason);
  }
}
```

上述示例代码的前提条件之一是调用 `updateChannelMetadata` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />
上述示例代码的前提条件之一是调用 `UpdateChannelMetadataAsync` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />
```dart
var properties = MetadataItem(
  key: 'Quantity',
  value: '20');

var announcement = MetadataItem(
  key: 'Announcement',
  value: 'Welcome to our shop!');

var price = MetadataItem(
  key: 'T-shirt',
  value: '200');

var metadata = [properties,announcement,price];

try{
    var (status,response) = await rtmClient.getStorage.setChannelMetadata(
        'channel1',
        RtmChannelType.message,
        metadata,
        lockName:'manage',
        recordTs: true,
        recordUserId: true);

    if (status.error == true) {
        print(status);
    } else {
        print(response);
    }
} catch (e) {
    print('something went wrong: $e');
}
```

上述示例代码的前提条件之一是调用 `updateChannelMetadata` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="cpp" />
```cpp
Metadata metadata;
std::vector<MetadataItem> items;
items.emplace_back(MetadataItem("Quantity", "40"));
items.emplace_back(MetadataItem("Announcement", "Welcome to our shop!"));
items.emplace_back(MetadataItem("T-shirt", "300"));
metadata.items = items.data();
metadata.itemCount = items.size();

MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;

uint64_t requestId;
std::string lockName = "manage";
rtm_client->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, lockName, requestId);
```

上述示例代码的前提条件之一是调用 `updateChannelMetadata` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="swift" />
```swift
let metadata = AgoraRtmMetadata()

let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "30"
properties.revision = 734874888

let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"

let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "101"
price.revision = 734874222

let itemArray = [properties, announcement, price]
metadata.items = itemArray

metadata.majorRevision = 734874892

let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true

rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: "manage") { response, errorInfo in
    if errorInfo == nil {
        print("updateChannelMetadata success!!")
    } else {
        if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
            print("updateChannelMetadata failed, errorCode: \(errorCode), reason: \(reason)")
        }
    }
}
```

上述示例代码的前提条件之一是调用 `updateChannelMetadata` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="mini-program" />
```javascript
const properties = {
    key : "Quantity",
    value : "40"
};

const announcement = {
    key : "Announcement",
    value : "Welcome to our Shop!"
};

const price = {
    key : "T-shirt",
    value : "300"
};

const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true, lockName : "manage" };

try {
    const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
    console.log(JSON.stringify(result));
} catch (status) {
    console.log(JSON.stringify(status));
}
```

上述示例代码的前提条件之一是调用 `updateChannelMetadata` 的用户必须是锁 `manage` 的拥有者，否则会返回错误。
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>
