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 | 內存釋放。 |