

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 在设备中使用 Amazon IoT 基于 MQTT 的文件传输
在设备中使用 Amazon IoT 基于 MQTT 的文件传输

要启动数据传输过程，设备必须接收**初始数据集**，其中至少包含一个流 ID。您可以通过在任务文档中包含初始数据集来使用 [Amazon IoT 职位](iot-jobs.md) 计划设备的数据传输任务。当设备收到初始数据集时，它应开始与 Amazon IoT 基于 MQTT 的文件传输进行交互。要使用 Amazon IoT 基于 MQTT 的文件传输交换数据，设备应该：
+ 使用 MQTT 协议订阅 [基于 MQTT 的文件传输主题](reserved-topics.md#reserved-topics-mqtt-based-file-delivery)。
+ 发送请求，然后使用 MQTT 消息等待接收响应。

您可以选择在初始数据集中包含流文件 ID 和流版本。将流文件 ID 发送到设备可以简化设备固件/软件的编程，因为设备无需发送 `DescribeStream` 请求来获取此 ID。设备可以在 `GetStream` 请求中指定流版本以强制执行一致性检查，以防流意外更新。

## DescribeStream 用于获取直播数据


Amazon IoT 基于 MQTT 的文件传输提供了向设备发送流数据的 `DescribeStream` API。此 API 返回的流数据包括流 ID、流版本、流描述和流文件列表，每个流文件都有一个文件 ID 和文件大小信息（以字节为单位）。通过这些信息，设备可以选择任意文件来启动数据传输过程。

**注意**  
如果您的设备在初始数据集中收到了所有必需的流文件 IDs ，则无需使用 `DescribeStream` API。

请按照以下步骤发送 `DescribeStream` 请求。

1. 订阅“已接受”主题筛选条件 `$aws/things/ThingName/streams/StreamId/description/json`。

1. 订阅“已被拒绝”主题筛选条件 `$aws/things/ThingName/streams/StreamId/rejected/json`。

1. 发送消息到 `$aws/things/ThingName/streams/StreamId/describe/json` 以发布 `DescribeStream` 请求。

1. 如果请求被接受，则您的设备会在“已接受”主题筛选条件中收到 `DescribeStream` 响应。

1. 如果请求被拒绝，则您的设备会在“已被拒绝”主题筛选条件中收到的错误响应。

**注意**  
如果您在显示的主题和主题筛选条件中将 `json` 替换为 `cbor`，则您的设备将收到 CBOR 格式的消息，该格式比 JSON 更紧凑。

### DescribeStream 请求


使用 JSON 格式的典型 `DescribeStream` 请求类似于以下示例。

```
{
    "c": "ec944cfb-1e3c-49ac-97de-9dc4aaad0039"
}
```
+ （可选）“`c`”是客户端令牌字段。

  客户端令牌不能超过 64 字节。超过 64 字节的客户端令牌将导致错误响应和 `InvalidRequest` 错误消息。

### DescribeStream 响应


使用 JSON 格式的 `DescribeStream` 响应类似于以下示例。

```
{
    "c": "ec944cfb-1e3c-49ac-97de-9dc4aaad0039",
    "s": 1,
    "d": "This is the description of stream ABC.",
    "r": [
        {
            "f": 0,
            "z": 131072
        },
        {
            "f": 1,
            "z": 51200
        }
    ]
}
```
+ “`c`”是客户端令牌字段。如果它在 `DescribeStream` 请求中给出，则将会返回。使用客户端令牌将响应与其请求相关联。
+ “`s`”是整数形式的流版本。您可以使用此版本对 `GetStream` 请求执行一致性检查。
+ “`r`”包含流中的文件列表。
  + “`f`”是整数形式的流文件 ID。
  + “`z`”是以字节为单位的流文件大小。
+ “`d`”包含流的描述。

## 从流文件中获取数据块


您可以使用 `GetStream` API，以便设备可以以小数据块的形式接收流文件，因此可以用于那些对处理大型数据块大小有限制的设备。要接收整个数据文件，设备可能需要发送或接收多个请求和响应，直到接收和处理了所有数据块。

### GetStream 请求


请按照以下步骤发送 `GetStream` 请求。

1. 订阅“已接受”主题筛选条件 `$aws/things/ThingName/streams/StreamId/data/json`。

1. 订阅“已被拒绝”主题筛选条件 `$aws/things/ThingName/streams/StreamId/rejected/json`。

1. 发布 `GetStream` 请求到主题 `$aws/things/ThingName/streams/StreamId/get/json`。

1. 如果请求被接受，您的设备将在“已接受”主题筛选条件下收到一个或多个 `GetStream` 响应。每个响应消息都包含单个数据块的基本信息和数据有效载荷。

1. 重复步骤 3 和 4 以接收所有数据块。如果请求的数据量大于 128 KB，则必须重复这些步骤。您必须对您的设备进行编程，以使用多个 `GetStream` 请求接收所有请求的数据。

1. 如果请求被拒绝，您的设备将在“已被拒绝”主题筛选条件下收到错误响应。

**注意**  
如果您在显示的主题和主题筛选条件中将“json”替换为“cbor”，则您的设备将收到 CBOR 格式的消息，这种格式比 JSON 更紧凑。
Amazon IoT 基于 MQTT 的文件传输将数据块的大小限制为 128 KB。如果您对超过 128 KB 的数据块发出请求，请求将失败。
您可以对总大小大于 128 KB 的多个数据块发出请求（例如，如果您请求 5 个数据块，各 32 KB，则共有 160 KB 的数据）。在这种情况下，请求不会失败，但您的设备必须发出多个请求才能接收请求的所有数据。当您的设备提出额外请求时，该服务将发送额外的数据块。我们建议您仅在正确接收和处理之前的响应之后，才继续执行新的请求。
无论请求的数据总大小如何，您都应该对设备进行编程，以便在未收到数据块或未正确接收数据块时启动重试。

使用 JSON 格式的典型 `GetStream` 请求类似于以下示例。

```
{
    "c": "1bb8aaa1-5c18-4d21-80c2-0b44fee10380",
    "s": 1,
    "f": 0,
    "l": 4096,
    "o": 2,
    "n": 100,
    "b": "..."
}
```
+ [可选]“`c`”是客户端令牌字段。

  客户端令牌不能超过 64 字节。超过 64 字节的客户端令牌将导致错误响应和 `InvalidRequest` 错误消息。
+ [可选]“`s`”是流版本字段（整数）。

  基于 MQTT 的文件传输应用基于此请求的版本和云中的最新流版本的一致性检查。如果从设备发送的 `GetStream` 请求中的流版本与云中的最新流版本不匹配，则服务会发送错误响应和 `VersionMismatch` 错误消息。通常，设备会在初始数据集或对 `DescribeStream` 的响应中接收预期（最新）的流版本。
+ “`f`”是流文件 ID（范围为 0 到 255 的整数）。

  使用或 SDK 创建或更新直播时，需要提供流文件 ID。 Amazon CLI 如果设备请求了 ID 不存在的流文件，则该服务将发送错误响应和 `ResourceNotFound` 错误消息。
+ “`l`”是以字节为单位的数据块大小（范围为 256 到 131072 的整数）。

  请参阅 [为请求生成位 GetStream 图](#mqtt-based-file-delivery-build-a-bitmap) 以了解如何使用位图字段指定流文件的哪些部分将在 `GetStream` 响应中返回。如果设备指定的数据块大小超出范围，则服务会发送错误响应和 `BlockSizeOutOfBounds` 错误消息。
+ [可选]“`o`”是流文件中数据块的偏移量（范围为 0 到 98304 的整数）。

  请参阅 [为请求生成位 GetStream 图](#mqtt-based-file-delivery-build-a-bitmap) 以了解如何使用位图字段指定流文件的哪些部分将在 `GetStream` 响应中返回。最大值 98304 是基于 24 MB 流文件大小限制和 256 字节的最小数据块大小计算得出的。如果未指定，默认值为 0。
+ [可选]“`n`”是请求的数据块数量（范围为 0 到 98304 的整数）。

  “n”字段指定（1）请求的数据块数量，或（2）使用位图字段（“b”）时，位图请求将返回的数据块数量的限制。第二种用法是可选的。如果未定义，则默认为 131072/ *DataBlockSize*。
+ [可选]“`b`”是一个位图，表示正在请求的数据块。

  使用位图，您的设备可以请求非连续的数据块，这使得处理错误后重试的操作更加方便。请参阅 [为请求生成位 GetStream 图](#mqtt-based-file-delivery-build-a-bitmap) 了解如何使用位图字段指定流文件的哪一部分将在 `GetStream` 响应中返回。对于此字段，将位图转换为以十六进制表示法表示位图值的字符串。位图必须小于 12288 个字节。

**重要**  
应指定“`n`”或“`b`”。如果两者都未指定，当文件小于 131072 字节（128 KB）时，`GetStream` 请求可能无效。

### GetStream 响应


JSON 格式的 `GetStream` 响应对于每个请求的数据块而言均如此示例所示。

```
{
    "c": "1bb8aaa1-5c18-4d21-80c2-0b44fee10380",
    "f": 0,
    "l": 4096,
    "i": 2,
    "p": "..."
}
```
+ “`c`”是客户端令牌字段。如果它在 `GetStream` 请求中给出，则将会返回。使用客户端令牌将响应与其请求相关联。
+ “`f`”是当前数据块有效载荷所属的流文件的 ID。
+ “`l`”是数据块负载的大小（以字节为单位）。
+ “`i`”是有效载荷中包含的数据块的 ID。数据块从 0 开始编号。
+ “`p`”包含数据块有效载荷。此字段是一个字符串，表示 [Base64](https://en.wikipedia.org/wiki/Base64) 编码中数据块的值。

### 为请求生成位 GetStream 图


您可以使用 `GetStream` 请求中的位图字段 (`b`) 从流文件中获取非连续数据块。这有助于 RAM 容量有限的设备处理网络交付问题。设备只能请求那些未接收或未正确接收的数据块。位图确定将返回流文件的哪些数据块。对于位图中设置为 1 的每个位，将返回相应的流文件数据块。

下面的示例将演示如何在 `GetStream` 请中指定位图机器支持的字段。例如，您希望以 256 字节的数据块（块大小）接收流文件。将每个 256 字节的数据块视为有一个指定其在文件中位置的数字，从 0 开始。因此，数据块 0 是文件中第一个 256 字节的数据块，数据块 1 是第二个，依此类推。您想要从文件中请求数据块 20、21、24 和 43。

**数据块偏移**  
由于第一个数据块是第 20 号，因此指定偏移量（字段 `o`）为 20，以节省位图中的空间。

**数据块数量**  
为了确保您的设备收到的数据块数量不会超过其在有限内存资源中所能处理的数量，您可以指定基于 MQTT 的文件传输发送的每条消息中应返回的最大数据块数量。请注意，如果位图本身指定的数据块数量小于此数值，或者如果它会使基于 MQTT 的文件传输发送的响应消息总大小大于每个 `GetStream` 请求 128 KB 的服务限制，则将忽略此值。

**数据块位图**  
位图本身是一个以十六进制表示法表示的无签名字节数组，并以数字字符串的形式包含在 `GetStream` 请求中。但要构造这个字符串，我们首先要将位图视为一个长序列的位（二进制数）。如果此序列中的位设置为 1，则流文件中的相应数据块将被发送回设备。在我们的示例中，我们希望接收数据块 20、21、24 和 43，因此我们必须在位图中设置位 20、21、24 和 43。我们可以使用数据块偏移来节省空间，因此在从每个数据块编号中减去偏移后，我们希望设置位 0、1、4 和 23，如下例所示。  

```
 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 
```
一次取一个字节（8 位），这个字节通常被写作：“0b00010011”、“0b00000000”和“0b10000000”。位 0 显示在第一个字节末尾的二进制表示中，位 23 显示在最后一个字节的开头。除非您早已了解惯例，否则这种表示方式可能会令人困惑不已。第一个字节包含位 7-0（按该顺序），第二个字节包含位 15-8，第三个字节包含位 23-16，依此类推。在十六进制表示法中，这将转换为“0x130080”。  
您可以将标准二进制转换为十六进制表示法。一次取四个二进制数字，并将它们转换为十六进制等效数字。例如，“0001”变为“1”，“0011”变成 “3”，依此类推。

![\[用于在 GetStream 请求中构造字符串的区块位图细分。\]](http://docs.amazonaws.cn/iot/latest/developerguide/images/blockBitmap.png)

将这一切结合起来，我们的 `GetStream` 请求在 JSON 格式中看起来与下方类似。  

```
{
    "c" : "1",       
    "s" : 1,         
    "l" : 256,       
    "f" : 1,         
    "o" : 20,        
    "n" : 32,       
    "b" : "130080"
}
```
+ “`c`”是客户端令牌字段。
+ “`s`”是预期的流版本。
+ “`l`”是数据块负载的大小（以字节为单位）。
+ “`f`”是源文件索引的 ID。
+ “`o`”是数据块偏移量。
+ “`n`”是数据块的数量。
+ “`b`”是从偏移量开始缺失的 blockId 位图。此值必须采用 base64 编码。

## 处理 Amazon IoT 基于 MQTT 的文件交付产生的错误


针对两者`DescribeStream`发送到设备的错误响应，`GetStream` APIs其中包含客户端令牌、错误代码和错误消息。典型的错误响应类似于以下示例。

```
{
    "o": "BlockSizeOutOfBounds", 
    "m": "The block size is out of bounds", 
    "c": "1bb8aaa1-5c18-4d21-80c2-0b44fee10380" 
}
```
+ 如果出现错误，则“`o`”为指示错误原因的代码。有关更多详细信息，请参阅本部分后面的错误代码。
+ “`m`”是包含错误详细信息的错误消息。
+ “`c`”是客户端令牌字段。如果它在 `DescribeStream` 请求中提供，则可能会返回。您可以使用客户端令牌将响应与其请求相关联。

  客户端令牌字段并不总是包含在错误响应中。当请求中给出的客户端令牌无效或格式不正确时，它不会在错误响应中返回。

**注意**  
为了向后兼容，错误响应中的字段可能采用非缩写形式。例如，错误代码可能由“code”或“o”字段表示，客户端令牌字段可以由“clientToken”或“c”字段表示。建议您使用上面显示的缩写形式。

**InvalidTopic**  
流消息的 MQTT 主题无效。

**InvalidJson**  
流请求不是有效的 JSON 文档。

**InvalidCbor**  
流请求不是有效的 CBOR 文档。

**InvalidRequest**  
请求整体被标识为格式错误。有关更多信息，请参阅错误消息。

**Unauthorized**  
请求未获授权访问存储介质（如 Amazon S3）中的流数据文件。有关更多信息，请参阅错误消息。

**BlockSizeOutOfBounds**  
数据块大小超出了界限。请参阅 [Amazon IoT Core Service Quotas](https://docs.amazonaws.cn//general/latest/gr/iot-core.html#limits_iot) 中的“**基于 MQTT 的文件传输**”部分。

**OffsetOutOfBounds**  
偏移超出界限。请参阅 [Amazon IoT Core Service Quotas](https://docs.amazonaws.cn//general/latest/gr/iot-core.html#limits_iot) 中的“**基于 MQTT 的文件传输**”部分。

**BlockCountLimitExceeded**  
请求数据块的数量超出了界限。请参阅 [Amazon IoT Core Service Quotas](https://docs.amazonaws.cn//general/latest/gr/iot-core.html#limits_iot) 中的“**基于 MQTT 的文件传输**”部分。

**BlockBitmapLimitExceeded**  
请求位图的大小超出了界限。请参阅 [Amazon IoT Core Service Quotas](https://docs.amazonaws.cn//general/latest/gr/iot-core.html#limits_iot) 中的“**基于 MQTT 的文件传输**”部分。

**ResourceNotFound**  
找不到请求的流、文件、文件版本或数据块。有关更多详细信息，请参阅错误消息。

**VersionMismatch**  
请求中的流版本与基于 MQTT 的文件传输特征中的流版本不匹配。这表示自设备最初接收流版本以来，流数据已被修改。

**ETag不匹配**  
流 ETag 中的 S3 与最新的 S3 对象版本不匹配。 ETag 

**InternalError**  
基于 MQTT 的文件传输中发生内部错误。