本文以C Link SDK中的Demo文件./demos/mqtt_upload_basic_demo.c为例,介绍如何调用Link SDK的API,帮助设备基于MQTT协议的连接,将文件上传至物联网平台,以减少硬件资源的开销,并降低开发成本。

背景信息

  • 文件上传功能的更多信息,请参见文件上传概述
  • 文件上传功能基于MQTT接入,开发过程中涉及MQTT接入的代码说明,请参见MQTT接入

步骤一:初始化文件上传功能

  1. 添加头文件。
    ……
    ……
    #include "aiot_mqtt_api.h"
    #include "aiot_mqtt_upload_api.h"
    #include "core_crc64.h"
    ……
  2. 配置底层依赖和日志输出。
    /* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
    extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
    
    /* 位于external/ali_ca_cert.c中的服务器证书 */
    extern const char *ali_ca_cert;
    
    /* 日志回调函数, SDK的日志会从这里输出 */
    static int32_t demo_state_logcb(int32_t code, char *message)
    {
        printf("%s", message);
        return 0;
    }
  3. 调用aiot_mqtt_upload_init,创建文件上传功能的客户端实例,并初始化默认参数。
        mqtt_handle = aiot_mqtt_upload_init();

步骤二:配置文件上传功能

调用aiot_mqtt_upload_setopt,配置以下功能。

  1. 关联MQTT连接的句柄。
    注意 配置文件上传功能参数前,请确保已配置设备认证信息等相关参数。具体操作,请参见MQTT配置连接参数

    文件上传功能的请求基于MQTT连接,通过该配置项,关联MQTT连接句柄。

        void *up_handle = aiot_mqtt_upload_init();
        aiot_mqtt_upload_setopt(up_handle, AIOT_MQTT_UPLOADOPT_MQTT_HANDLE, mqtt_handle);
  2. 可选:配置要上传文件的相关参数。
    • 示例代码:
          uint32_t rsp_timeout = 2000;
          aiot_mqtt_upload_setopt(up_handle, AIOT_MQTT_UPLOADOPT_RSP_TIMEOUT_MS, &rsp_timeout);
          uint32_t rety_count = 5;
          aiot_mqtt_upload_setopt(up_handle, AIOT_MQTT_UPLOADOPT_RETRY_COUNT, &rety_count);
          uint32_t block_size = 1024;
          aiot_mqtt_upload_setopt(up_handle, AIOT_MQTT_UPLOADOPT_DEFAULLT_BLOCK_SIZE, &block_size);
    • 相关参数:
      配置项 说明
      rsp_timeout 定义每包数据重发的超时时间,单位毫秒。

      如果不设置或设置为0,则超时时间为5秒。

      示例代码将超时时间设置为2000毫秒。

      AIOT_MQTT_UPLOADOPT_RSP_TIMEOUT_MS 调用aiot_mqtt_upload_setopt,设置上传文件时每包数据重发的超时时间。
      rety_count 定义超时重试的次数。

      默认重试次数为10次。

      示例代码设置的重试5次。

      AIOT_MQTT_UPLOADOPT_RETRY_COUNT 调用aiot_mqtt_upload_setopt,设置上传文件时超时重试的次数。
      block_size 定义每包数据发送的最大值,单位字节。

      取值范围为256~131072,默认为2048字节。

      示例代码设置的最大值为1024字节。

      AIOT_MQTT_UPLOADOPT_DEFAULLT_BLOCK_SIZE 调用aiot_mqtt_upload_setopt,设置上传文件时每包数据发送的最大值。

(可选)步骤三:计算完整文件的CRC64

上传文件时,协议内默认对分片数据进行CRC16计算。您可以根据业务需要,选择CRC64计算,SDK默认提供CRC64计算C版库。

以下示例代码为计算完整文件的CRC64。

    uint64_t crc = mqtt_upload_get_file_crc64(MQTT_UPLOAD_FILE_NAME);

步骤四:请求上传文件

  1. 调用aiot_mqtt_upload_open_stream,向物联网平台发起上传文件请求。
    注意
    • 同名文件上传过程中,请勿调用aiot_mqtt_upload_open_stream发起多次请求。上传失败后,您可以重复请求续传文件。
    • 不同名文件上传过程中,可以调用aiot_mqtt_upload_open_stream发起多次请求,且支持多文件上传。
    • 示例代码:
      • 定义要上传的文件大小和文件名:
        #define MQTT_UPLOAD_FILE_SIZE       (2 * 1024)
        #define MQTT_UPLOAD_FILE_NAME       ("mqttuploadfile001.txt")
        
        #define MQTT_UPLOAD_FILE_SIZE2       (1 * 1024 + 127)
        #define MQTT_UPLOAD_FILE_NAME2       ("mqttuploadfile002.txt")
      • 发起请求上传文件:
         uint32_t test_userdata = 100;
            aiot_mqtt_upload_open_stream(up_handle, MQTT_UPLOAD_FILE_NAME, MQTT_UPLOAD_FILE_SIZE,
                AIOT_MQTT_UPLOAD_FILE_MODE_OVERWRITE, &crc, mqtt_upload_read_data_handler, &test_userdata);
        
            mqtt_upload_create_upload_file(MQTT_UPLOAD_FILE_NAME2, MQTT_UPLOAD_FILE_SIZE2);
            aiot_mqtt_upload_open_stream(up_handle, MQTT_UPLOAD_FILE_NAME2, MQTT_UPLOAD_FILE_SIZE2,
                AIOT_MQTT_UPLOAD_FILE_MODE_OVERWRITE, NULL, mqtt_upload_read_data_handler, NULL);
    • 示例代码说明:
      • 示例代码调用该接口同时上传两个文件mqttuploadfile001.txtmqttuploadfile002.txt
        注意
        • 上传文件的名称限制如下:
          • 支持数字、英文字母、下划线(_)和英文句点(.)。
          • 首字符仅支持数字和英文字母。
          • 长度不超过100字节。
        • 单个文件不超过16 MB。
      • mqttuploadfile001.txt有CRC64校验,有userdata传参。
      • mqttuploadfile002.txt无CRC64校验,无userdata传参。
      • 示例代码使用的上传文件的处理策略为overwrite。处理策略的详细说明如下:
        处理策略(conflictStrategy) 说明
        覆盖模式(overwrite) 默认模式,删除已存在的同名文件,仅保留最新上传的文件。
        文件追加模式(append) 发起上传文件请求后,物联网平台返回已存在的同名文件信息,设备端可根据业务需要,选择以下操作:
        • 如果已存在的同名文件为未完成上传的文件,则设备继续执行上传任务。
          说明 物联网平台仅在24小时内保留未完成上传的文件。
        • 如果已存在的同名文件为已完成上传的文件,则设备选择覆盖模式或重命名文件后,重新上传文件。
        拒绝模式(reject) 物联网平台返回文件已存在的错误码,并拒绝再次上传该文件。
  2. 物联网平台接收文件上传的请求后,向设备返回应答消息。设备接收消息后,触发回调函数。
    在回调函数中,SDK分片读取文件内容或您的内存数据,获取到data数据后,对单包data数据进行CRC16计算。
    注意 分片传输过程中,单包数据不大于AIOT_MQTT_UPLOADOPT_DEFAULLT_BLOCK_SIZE设置的值。同时,除最后一包数据外,单包数据不小于256字节。

    示例代码如下,回调函数为mqtt_upload_read_data_handler

    int32_t mqtt_upload_read_data_handler(const aiot_mqtt_upload_recv_t *packet, uint8_t *data, uint32_t size, void *userdata) {
        int32_t read_len = 0;
        if (userdata != NULL) {
            uint32_t *test_userdata = (uint32_t *)userdata;
            printf("test_userdata:%d\r\n", *test_userdata);
        }
        if (packet == NULL) {
            return 0;
        }
    
        if (packet->desc.code == UPLOAD_FILE_OK) {
            if (data != NULL && size != 0) {
                uint32_t read_size = size;
                FILE *fp;
                char* file_name = packet->desc.file_name;
    
                fp = fopen(file_name, "r");
                uint32_t offset = packet->desc.file_offset;
                fseek(fp, offset, SEEK_SET);
                printf("Open %s read at: %d\r\n", file_name, offset);
    
                read_len = fread(data, sizeof(uint8_t), read_size, fp);
                printf("Read_len: %d\r\n", read_len);
                fclose(fp);
            }
        } else {
            printf("Error code:%d, message:%s\r\n", packet->desc.code, packet->desc.message);
        }
    
        return read_len;
    }
  3. 可选:上传文件过程中,如果需要取消上传,您可以调用aiot_mqtt_upload_cancel_stream
    取消上传接口的详细说明,请参见aiot_mqtt_upload_cancel_stream

步骤五:处理文件上传状态

文件上传过程中,调用aiot_mqtt_upload_process,循环检测文件上传的状态。示例代码如下:

    while(1) {
        aiot_mqtt_upload_result_t result = aiot_mqtt_upload_process(up_handle);
        if (result.code == STATE_MQTT_UPLOAD_FINISHED) {
            /* 上传成功 */
            printf("MQTT Upload file(%s) success\r\n", result.file_name);
            uploaded_file++;
            if (uploaded_file == 2) {
                break;
            }
        } else if (result.code == STATE_MQTT_UPLOAD_FAILED ||
                   result.code == STATE_MQTT_UPLOAD_FAILED_TIMEOUT ||
                   result.code == STATE_MQTT_UPLOAD_CANCEL_FAILED ) {
            /* 上传失败 */
            printf("MQTT Upload file(%s) failed,res:-0x%.4X\r\n", result.file_name, -result.code);
            /* 销毁MQTT UPLOAD实例 */
        } else if (result.code == STATE_MQTT_UPLOAD_CANCEL_SUCCESS) {
            printf("MQTT Upload file(%s) cancel success,res:-0x%.4X\r\n", result.file_name, -result.code);
        } else if (result.code == STATE_MQTT_UPLOAD_FAILED_WHOLE_CHECK) {
            printf("MQTT Upload file(%s) whole file md5 failed,res:-0x%.4X\r\n", result.file_name, -result.code);
        }

        sleep(1);
    }

步骤六:退出文件上传程序

调用aiot_mqtt_upload_deinit,销毁文件上传功能的客户端实例,释放资源。

aiot_mqtt_upload_deinit(&up_handle);

后续步骤

  • 例程文件配置完成后,需进行编译,生成可执行文件./output/mqtt-upload-basic-demo

    更多信息,请参见编译与运行

  • 关于运行结果的详细说明,请参见运行日志