SDK的文件上传功能使用HTTP2流式传输协议,将文件上传至阿里云物联网平台服务器。
- 支持多种上传模式,如以创建文件的方式上传,或以覆盖文件的方式上传。
- 支持指定上传长度,并在下次上传时续传,用户可在上传时根据网络带宽配置上传分配大小(
part_len
),以提高带宽利用效率。
本文以src/http2/http2_example_uploadfile.c
为例讲解如何使用文件上传功能。
1. 与云端建立连接
调用IOT_HTTP2_UploadFile_Connect
建立HTTP2连接,指定设备的证书信息和服务器地址或端口号。
http2_upload_conn_info_t conn_info;
void *handle;
memset(&conn_info, 0, sizeof(http2_upload_conn_info_t));
conn_info.product_key = HTTP2_PRODUCT_KEY;
conn_info.device_name = HTTP2_DEVICE_NAME;
conn_info.device_secret = HTTP2_DEVICE_SECRET;
conn_info.url = HTTP2_ONLINE_SERVER_URL;
conn_info.port = HTTP2_ONLINE_SERVER_PORT;
handle = IOT_HTTP2_UploadFile_Connect(&conn_info, NULL);
if(handle == NULL) {
return -1;
}
目前各个区域对应的域名和端口如下,其中*
符号应使用设备的ProductKey
替换,如ProductKey
为a1Ign******I
时对应的URL、PORT如下:
#define HTTP2_ONLINE_SERVER_URL "a1I******vI.iot-as-http2.cn-shanghai.aliyuncs.com"
#define HTTP2_ONLINE_SERVER_PORT 443
*.iot-as-http2.cn-shanghai.aliyuncs.com:443 // 上海正式
*.iot-as-http2.us-west-1.aliyuncs.com:443 // 美西正式
*.iot-as-http2.us-east-1.aliyuncs.com:443 // 美东正式
*.iot-as-http2.eu-central-1.aliyuncs.com:443 // 德国正式
*.iot-as-http2.ap-southeast-1.aliyuncs.com:443 // 新加坡正式
*.iot-as-http2.ap-northeast-1.aliyuncs.com:443 // 日本正式
2. 文件上传
使用IOT_HTTP2_UploadFile_Request
请求文件上传,示例程序以UPLOAD_FILE_OPT_BIT_OVERWRITE
的方式上传,每次上传都会覆盖云端文件。此接口为异步接口,可以插入多个上传请求到内部队列中。
http2_upload_params_t fs_params;
http2_upload_result_cb_t result_cb;
memset(&result_cb, 0, sizeof(http2_upload_result_cb_t));
result_cb.upload_completed_cb = upload_file_result;
result_cb.upload_id_received_cb = upload_id_received_handle;
memset(&fs_params, 0, sizeof(fs_params));
fs_params.file_path = argv[1]; /* 文件名称以命令行参数传入 */
fs_params.opt_bit_map = UPLOAD_FILE_OPT_BIT_OVERWRITE;
ret = IOT_HTTP2_UploadFile_Request(handle, &fs_params, &result_cb, NULL);
if(ret < 0) {
return -1;
}
示例程序中注册了2个回调函数,分别用于接收上传结果,和接收云端返回的上传标示符(upload_id
),在SDK调用了upload_file_result
后,文件上传操作结束,可进行下一步操作。
void upload_file_result(const char *file_path, int result, void *user_data)
{
upload_end++;
EXAMPLE_TRACE("=========== file_path = %s, result = %d, finish num = %d ===========", file_path, result, upload_end);
}
void upload_id_received_handle(const char *file_path, const char *upload_id, void *user_data)
{
EXAMPLE_TRACE("=========== file_path = %s, upload_id = %s ===========", file_path, upload_id);
if (upload_id != NULL) {
memcpy(g_upload_id, upload_id, strlen(upload_id));
}
}
- 设备端请求云端打开文件上传的通道。
[inf] on_frame_send_callback(143): [INFO] C ---------> S (HEADERS) stream_id [1] [inf] on_frame_send_callback(145): > :method: POST [inf] on_frame_send_callback(145): > :path: /stream/open/c/iot/sys/thing/file/upload [inf] on_frame_send_callback(145): > :scheme: https [inf] on_frame_send_callback(145): > x-auth-name: devicename [inf] on_frame_send_callback(145): > x-auth-param-client-id: *******D7vI.H2_FS01 [inf] on_frame_send_callback(145): > x-auth-param-signmethod: hmacsha1 [inf] on_frame_send_callback(145): > x-auth-param-product-key: a1IgnO***** [inf] on_frame_send_callback(145): > x-auth-param-device-name: H2_FS01 [inf] on_frame_send_callback(145): > x-auth-param-sign: 8d6b80749ed63823d********** [inf] on_frame_send_callback(145): > x-sdk-version: 301 [inf] on_frame_send_callback(145): > x-sdk-version-name: 3.0.1 [inf] on_frame_send_callback(145): > x-sdk-platform: c [inf] on_frame_send_callback(145): > content-length: 0 [inf] on_frame_send_callback(145): > x-file-name: upload1M [inf] on_frame_send_callback(145): > x-file-overwrite: 1 [inf] on_begin_headers_callback(393): [INFO] C <--------- S (HEADERS) stream_id [1] [inf] on_header_callback(363): < :status: 200 [inf] on_header_callback(363): < x-request-id: 11039195007******* [inf] on_header_callback(363): < x-next-append-position: 0 [inf] on_header_callback(363): < x-data-stream-id: DS1103919500******** [inf] on_header_callback(363): < x-file-upload-id: ULDS11039195******** [inf] on_header_callback(363): < x-response-status: 200
- 通道打开成功,接收到云端返回的文件上传标示符,并调用用户回调函数。
upload_id_received_handle|037 :: =========== file_path = upload1M, upload_id = ULDS11039195008******** ===========
- 通道打开成功后,设备端通过HTTP2请求上传文件。
[inf] on_frame_send_callback(143): [INFO] C ---------> S (HEADERS) stream_id [3] [inf] on_frame_send_callback(145): > :method: POST [inf] on_frame_send_callback(145): > :path: /stream/send/c/iot/sys/thing/file/upload [inf] on_frame_send_callback(145): > :scheme: https [inf] on_frame_send_callback(145): > content-length: 1048576 [inf] on_frame_send_callback(145): > x-data-stream-id: DS1103919500******** [inf] on_frame_send_callback(145): > x-sdk-version: 301 [inf] on_frame_send_callback(145): > x-sdk-version-name: 3.0.1 [inf] on_frame_send_callback(145): > x-sdk-platform: c [inf] on_frame_send_callback(145): > x-file-upload-id: ULDS1103919500******** [dbg] http2_stream_node_search(168): stream node not exist, stream_id = 3 [inf] send_callback(63): send_callback data len 10249, session->remote_window_size=16777215! [inf] send_callback(72): send_callback data ends len = 10249! [dbg] http2_stream_node_search(168): stream node not exist, stream_id = 3 [inf] iotx_http2_client_send(563): nghttp2_session_send 0 [dbg] _http2_fs_part_send_sync(250): send len = 10240 [inf] send_callback(63): send_callback data len 10249, session->remote_window_size=16766975! [inf] send_callback(72): send_callback data ends len = 10249! [inf] iotx_http2_client_send(563): nghttp2_session_send 0 [dbg] _http2_fs_part_send_sync(250): send len = 20480 [inf] send_callback(63): send_callback data len 10249, session->remote_window_size=16756735! [inf] send_callback(72): send_callback data ends len = 10249! [inf] iotx_http2_client_send(563): nghttp2_session_send 0 [dbg] _http2_fs_part_send_sync(250): send len = 30720 [inf] send_callback(63): send_callback data len 10249, session->remote_window_size=16746495! [inf] send_callback(72): send_callback data ends len = 10249! [inf] iotx_http2_client_send(563): nghttp2_session_send 0 [dbg] _http2_fs_part_send_sync(250): send len = 40960
- 文件上传结束,等待云端上传结构应答,应答中的
x-next-append-position
是已上传文件的大小。[inf] on_frame_recv_callback(196): on_frame_recv_callback, type = 8 [inf] on_frame_recv_callback(197): on_frame_recv_callback, stream_id = 3 [inf] on_frame_recv_callback(205): stream user data is not exist [inf] on_begin_headers_callback(393): [INFO] C <--------- S (HEADERS) stream_id [3] [inf] on_header_callback(363): < :status: 200 [inf] on_header_callback(363): < x-request-id: 11039195011******** [inf] on_header_callback(363): < x-next-append-position: 1048576 [inf] on_header_callback(363): < x-data-stream-id: DS110391950088******** [inf] on_header_callback(363): < x-response-status: 200 [inf] on_frame_recv_callback(196): [dbg] _http2_fs_part_send_sync(250): on_frame_recv_callback, type = 1 [inf] on_frame_recv_callback(197): on_frame_recv_callback, stream_id = 3 [inf] on_frame_recv_callback(205): send len = 1048576 [inf] _http2_fs_node_handle(350): file offset = 1048576 now
- SDK关闭文件上传通道。
[inf] on_frame_send_callback(143): [INFO] C ---------> S (HEADERS) stream_id [5] [inf] on_frame_send_callback(145): > :method: POST [inf] on_frame_send_callback(145): > :path: /stream/close/c/iot/sys/thing/file/upload [inf] on_frame_send_callback(145): > :scheme: https [inf] on_frame_send_callback(145): > x-data-stream-id: DS1103919500******** [inf] on_frame_send_callback(145): > x-sdk-version: 301 [inf] on_frame_send_callback(145): > x-sdk-version-name: 3.0.1 [inf] on_frame_send_callback(145): > x-sdk-platform: c [dbg] http2_stream_node_search(168): stream node not exist, stream_id = 5 [inf] iotx_http2_client_send(563): nghttp2_session_send 0 [inf] on_begin_headers_callback(393): [INFO] C <--------- S (HEADERS) stream_id [5] [inf] on_header_callback(363): < :status: 200 [inf] on_header_callback(363): < x-request-id: 11039195021******** [inf] on_header_callback(363): < x-data-stream-id: DS1103919500********* [inf] on_header_callback(363): < x-file-crc64ecma: 694777069228********* [inf] on_header_callback(363): < x-response-status: 200 [inf] on_header_callback(363): < x-file-store-id: 101184
3. 断开连接
所有文件上传结束后使用IOT_HTTP2_UploadFile_Disconnect
断开云端连接。
ret = IOT_HTTP2_UploadFile_Disconnect(handle);
功能API接口
src/http2/http2_upload_api.h
列出了HTTP2文件上传的所有API和相关数据类型定义。
src/http2/http2_wrapper.h
列出了HTTP2所需的底层接口
HTTP2建立连接
接口原型
void *IOT_HTTP2_UploadFile_Connect(http2_upload_conn_info_t *conn_info, http2_status_cb_t *cb);
接口说明
创建HTTP2连接,并注册相关状态回调函数,此接口为同步接口,当建连成功后会返回HTTP2连接句柄,否则返回NULL。
参数说明
参数 | 数据类型 | 方向 | 说明 |
---|---|---|---|
conn_info | http2_upload_conn_info_t * | 输入 | 设备连接信息。 |
cb | http2_status_cb_t * | 输入 | 设备状态回调函数结构体指针。 |
返回值说明
值 | 说明 |
---|---|
非NULL | 建连成功。 |
NULL | 建连失败。 |
参数附加说明
typedef struct {
char *product_key;
char *device_name;
char *device_secret;
char *url;
int port;
} http2_upload_conn_info_t;
- product_key:产品标示符。
- device_name:设备名称。
- device_secret:识别密钥。
- url:云端服务器地址。
- port:云端服务器端口。
typedef struct {
http2_disconnect_cb_t on_disconnect_cb;
http2_reconnect_cb_t on_reconnect_cb;
} http2_status_cb_t;
- on_disconnect_cb:HTTP2断连回调函数。
- on_reconnect_cb:HTTP2重连回调函数。
文件上传请求
接口原型
int IOT_HTTP2_UploadFile_Request(void *http2_handle, http2_upload_params_t *params, http2_upload_result_cb_t *cb, void *user_data);
接口说明
按照指定参数配置上传文件,并注册相关结果回调函数,此接口为异步接口,上传结果由回调函数返回。
参数说明
参数 | 数据类型 | 方向 | 说明 |
---|---|---|---|
http2_handle | void * | 输入 | 调用IOT_HTTP2_UploadFile_Connect 建连成功后返回的句柄。
|
params | http2_upload_params_t * | 输入 | 上传参数结构体指针。 |
cb | http2_upload_result_cb_t | 输入 | 上传结构回调函数结构体指针。 |
user_data | void * | 输入 | 用户数据。 |
返回值说明
值 | 说明 |
---|---|
0 | 函数调用成功。 |
< 0 | 函数调用失败。 |
typedef struct {
const char *file_path; /* file path, filename must be ASCII string and strlen < 2014 */
uint32_t part_len; /* maximum content len of one http2 request, must be in range of 100KB ~ 100MB */
const char *upload_id; /* a specific id used to indicate one upload session, only required when UPLOAD_FILE_OPT_BIT_RESUME option set */
uint32_t upload_len; /* used to indicate the upload length, only required when UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN option set */
uint32_t opt_bit_map; /* option bit map, support UPLOAD_FILE_OPT_BIT_OVERWRITE, UPLOAD_FILE_OPT_BIT_RESUME and UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN */
} http2_upload_params_t;
- file_path:文件路径。
说明 文件名必须为ASCII编码,且不能使用数字开头。
- part_len:文件上传分片大小,即HTTP2请求
content_len
的最大长度, 必须在100KB ~ 100MB范围内, 否则会使用http2_config.h
中的默认长度。 - upload_id:上传标示符,首次上传时返回。在需要使用断点续传方式上传时需添加
opt_bit_map
参数UPLOAD_FILE_OPT_BIT_RESUME
, 并指定对应上传标示符。 - upload_len:指定本次请求的长度,仅在
opt_bit_map
参数中有配置UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN
时才能起作用。 - opt_bit_map:位定义的选项表,可以使用按位或的方式配置此选项。
如
opt_bit_map = UPLOAD_FILE_OPT_BIT_OVERWRITE | UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN
表示使用覆盖方式上传指定的文件长度。
#define UPLOAD_FILE_OPT_BIT_OVERWRITE (0x00000001)
#define UPLOAD_FILE_OPT_BIT_RESUME (0x00000002)
#define UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN (0x00000004)
- UPLOAD_FILE_OPT_BIT_OVERWRITE:使用覆盖的方式上传文件。如果云端文件已存在,而未使用覆盖方式,则文件上传会失败。未使用此选项,将使用创建文件的方式上传文件。
- UPLOAD_FILE_OPT_BIT_RESUME:使用断点续传的方式上传文件。使用此选项需填写上传标示符参数
upload_id
。 - UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN:使用指定长度的方式上传。使用此选项需要填写上传长度参数(
upload_len
),否则将上传整个文件。
typedef struct {
http2_upload_id_received_cb_t upload_id_received_cb;
http2_upload_completed_cb_t upload_completed_cb;
} http2_upload_result_cb_t;
- upload_id_received_cb:接收到云端服务器返回的上传标示符时,将调用此回调函数。
- upload_completed_cb:文件上传结束时,将调用此回调函数,result参数指示了上传结果。
HTTP2断开连接
接口原型
int IOT_HTTP2_UploadFile_Disconnect(void *handle);
接口说明
断开参数handle指定的HTTP2连接。
参数说明
参数 | 数据类型 | 方向 | 说明 |
---|---|---|---|
http2_handle | void * | 输入 | 调用IOT_HTTP2_UploadFile_Connect 建连成功后返回的句柄。
|
返回值说明
值 | 说明 |
---|---|
0 | 函数调用成功。 |
< 0 | 函数调用失败。 |
需要对接的HAL接口
文件src/http2/http2_wrapper.h
中包含了对接HTTP2文件上传需要适配的部分HAL接口。
函数名 | 说明 |
---|---|
HAL_SSL_Destroy | 销毁一个TLS连接,用于MQTT功能、HTTPS功能。 |
HAL_SSL_Establish | 建立一个TLS连接,用于MQTT功能、HTTPS功能。 |
HAL_SSL_Read | 从一个TLS连接中读数据,用于MQTT功能、HTTPS功能。 |
HAL_SSL_Write | 向一个TLS连接中写数据,用于MQTT功能、HTTPS功能。 |
HAL_MutexCreate | 创建一个互斥量对象。 |
HAL_MutexDestroy | 销毁一个互斥量对象。 |
HAL_MutexLock | 锁住一个互斥量。 |
HAL_MutexUnlock | 解锁一个互斥量。 |
HAL_SemaphoreCreate | 创建信号量。 |
HAL_SemaphoreDestroy | 销毁信号量。 |
HAL_SemaphorePost | post信号量。 |
HAL_SemaphoreWait | 等待信号量。 |
HAL_ThreadCreate | 创建线程。 |
HAL_ThreadDelete | 销毁线程。 |
HAL_ThreadDetach | 分离线程。 |
HAL_Fopen | 打开文件。 |
HAL_Fread | 读取文件数据。 |
HAL_Fwrite | 向文件写入数据。 |
HAL_Fseek | 设置文件指针stream的位置。 |
HAL_Fclose | 关闭文件。 |
HAL_Ftell | 得到文件位置指针当前位置相对于文件首的偏移字节数。 |
HAL_Printf | 打印函数。 |
HAL_SleepMs | 睡眠函数。 |
HAL_Malloc | 内存分配。 |
HAL_Free | 内存释放。 |