扫码传文件

优质
小牛编辑
124浏览
2023-12-01

背景

什么是扫码传文件

introdction

基本原理

ui

在设备端下载得到文件后,可自行根据文件格式,调起响应的应用/服务来打开用户文件,提供流畅的体验

SDK概述

本文档主要介绍百度网盘Panlink C++ SDK的安装和使用。使用SDK前,需要先了解panlink文件提取的基本原理。

运行环境

Panlink SDK使用Golang编写,支持交叉编译成各种运行环境的Runtime。

目录结构:

├─src/baidu.com/panlink
│  ├─example               // 示例代码
│  ├─golog                 // 日志库
│  ├─panlink
│       ├─panlink_test.go  // Panlink接口封装,单元测试
│       ├─panlink.go       // Panlink接口封装
│       └─sign.go          // 签名函数
│  ├─panlink_agent
│       ├─agent_test.go    // Panlink客户端主程序,单元测试
│       ├─agent.go         // Panlink客户端主程序
│       └─pullworker.go    // 消息 Pull Worker
│  ├─types
│       └─errorcode.go     // 错误码定义
│  └─output
│       ├─bin              // example编译产出
│       ├─include          // sdk 头文件
│       ├─lib              // sdk lib 目录,根据运行环境,选择相应的dll/so文件
│       └─log              // example 输出的日志目录
│  └─build.sh

编译方法:

// 可以直接使用output中的,安装golang运行环境
// 参考:https://golang.org/doc/install
sh build.sh

目前发布的SDK压缩包中,已经包含了针对各个平台编译的动态链接库文件,如没有对应平台的库文件,则可联系我们提供。

初始化

在使用SDK前,需要向网盘申请AppID、SK和DeviceType。

参数说明
AppID应用ID,第三方开发者的唯一标识
SK和AppID对应的鉴权密钥,应该尽量避免将SK分发到提供给用户的客户端上
DeviceType设备类型标识,一个AppID下可以有多个DeviceType,用来唯一标识一种接入设备类型,当有多种设备类型时,可以申请多个,DeviceType由网盘为开发者分配

目前网盘提供 https://pan.baidu.com 作为服务域名

PanlinkAgent

PanlinkAgent为第三方开发者实现了临时设备标识的申请,文件下发消息的长轮询,并通过回调函数的方式通知第三方开发者。

配置参数

参数的设置,请参考example程序:

typedef struct {

// 网盘服务器地址
const char* PanlinkServerHost;

// 开发者参数,AppID和SK
int AppID;
const char* SK;
const char* DeviceType;
// 本机的唯一设备ID,可以使用mac地址,或者设备SN码
const char* DeviceID;

// 请求UUID、消息错误时的重试间隔,单位为s,默认为2s
int RequestErrorWaitSecond;
// 在当前UUID过期前多久,请求新的UUID,默认为60
int RequestUUIDBufferSecond;
// 拉取UUID内的消息时,最多包含多少个消息,最大为20,默认为:1
int PullLimit;
// 最多拉取多少个最近的UUID的消息,默认为2
int MaxPullWorkerLimit;

// 连接相关信息,建议不要修改,可以使用默认参数
// 最多空闲连接数,默认:3
int MaxIdleConns;
// 空闲连接的最大空闲时间,默认:2s
int IdleConnTimeoutSecond;
// 连接超时时间,默认:5s
int ConnectTimeoutSecond;
// 请求超时时间,默认:5s
int RequestTimeoutSecond;
// tcp keepalive间隔,默认:30s
int KeepAliveSecond;
// 长轮询的请求超时时间,默认:100s
int LongPullRequestTimeoutSecond;

}PanlinkAgentConfig;

初始化方法:

// PanlinkAgent对象全局只能有一个,当多次初始化时,会导致前一个对象被覆盖,可能会引起内存泄露,对于需要停止更新和拉取UUID时,使用StopAgent()和StartAgent()来启停Agent对象。
int InitPanlinkAgent(PanlinkAgentConfig* p0);

UUID申请

启动PanlinkAgent的接口:

int StartAgent();

启动PanlinkAgent后,Agent向网盘服务器申请临时设备标识,申请到的临时设备标识会包含以下信息:

typedef struct  {
    // 错误码,错误码为0时表示结果正常
    int ErrNo;
    // 错误消息,当错误码不为0时,可打印错误消息,以便定位错误原因
    char* ErrMsg;
    // UUID,设备临时标识
    char* UUID;
    // 二维码url,必须使用该url来展示二维码,以便网盘客户端正确识别设备身份
    char* QRUrl;
    // Time To Live,UUID的剩余有效时长,单位秒
    int TTL;
    // 过期的时间戳,单位秒
    int ExpireAt;
}UUIDCallBackData;

UUID自动更新

在二维码将要过期时,Panlink Agent会向服务器更新UUID,此时旧的UUID和新申请到的UUID均可以用于文件提取,同时有效的UUID个数可通过PanlinkAgentConfig.MaxPullWorkerLimit来设置。通过PanlinkAgentConfig.RequestUUIDBufferSecond参数可用于配置在当前UUID过期前多久更新UUID。

UUID手动强制刷新

强制更新接口: int RefreshUUID();

强制更新的结果,会通过 UUIDCallBackFn 回调函数返回给开发者。

UUID二维码生成

调用以下函数来根据UUID生成二维码:

int GetQRUrl(UUIDCallBackData* uuid, int level, int size);

参数说明:

- UUIDCallBackData结构体中,`UUIDCallBackData.UUID`为用于生成二维码的UUID,`UUIDCallBackData.QRUrl`为用于接收二维码url的变量
- level的取值表示二维码抗遮挡等级:0 low、1 medium、2 high、3 highest
- size的取值表示二维码大小,参考取值为:256

返回值说明:

- 返回值为0时,表示生成成功
- 返回值不为0时,表示失败

文件下发消息的拉取

PanlinkAgent会根据UUID向网盘服务器以长轮询的方式查询是否有新的消息发送给对应的UUID。

收到文件下发消息后,会用以下结构体向开发者传递文件下载消息,一次最多会下发20个文件下载地址,具体可通过PanlinkAgentConfig.PullLimit参数来设置:

typedef struct  {
    // 文件名,Golang 字符串编码是 utf-8
    char* Filename;
    // 下载地址,从该地址可以得到原始文件
    char* Dlink;
}FileInfo;

typedef struct { // 错误码,错误码为0时表示结果正常 int ErrNo; // 错误消息,当错误码不为0时,可打印错误消息,以便定位错误原因 char* ErrMsg; // 文件个数 int ListCnt; // 文件列表数组,个数由 ListCnt 指定 FileInfo* list; }PullCallBackData;

回调函数

开发者需要实现两个回调函数,分别用户UUID二维码的更新和文件下载消息的接收。需要注意的是,回调接口传入的参数UUIDCallBackDataPullCallBackData的有效期仅在回调函数内有效,回调函数运行结束后,便会回收对应的内存空间,故对于需要保留的消息,需要拷贝到其他用户变量中。

typedef void (*UUIDCallBackFn) (UUIDCallBackData*);
typedef void (*PullCallBackFn) (PullCallBackData*);

回调函数的注册

调用以下两个函数分别用于UUID更新和文件下发消息的回调函数注册

void RegiestUUIDCallBack(UUIDCallBackFn p0);
void RegiestPullCallBack(PullCallBackFn p0);

回调函数参考实现

需要注意的是,在 UUIDCallBackData 中,UUIDCallBackData.QRUrl 不会自动返回,需要调用 GetQRUrl 函数来生成业务上需要的指定冗余度和大小的二维码。QRUrl的空间回收需要业务上自己实现。

void uuidCallBackFn(UUIDCallBackData* data) {
    cout << "uuidCallBackFn called " << endl;

int ret = GetQRUrl(data, 1, 256);
if (ret != 0) {
  char * err = LastError();
  if (err != NULL) {
    cout << "GetQRUrl err, code=" << ret << " err=" << err << endl;
    free(err);
  } else {
    cout << "GetQRUrl err, code=" << ret << " err=null" << endl;
  }
}

cout << "errno: " << data->ErrNo << endl;
cout << "errmsg: " << data->ErrMsg << endl;
cout << "uuid: " << data->UUID << endl;
cout << "qrurl: " << data->QRUrl << endl;
cout << "ttl: " << data->TTL << endl;
cout << "expireat: " << data->ExpireAt << endl;

if (data->QRUrl != NULL){
  free(data->QRUrl);
}

}

void pullCallBackFn(PullCallBackData* data) { cout << "pullCallBackFn called " << endl; cout << "errno: " << data->ErrNo << endl; cout << "errmsg: " << data->ErrMsg << endl; cout << "listCnt: " << data->ListCnt << endl; for (int i = 0; i < data->ListCnt; ++i) { cout << "list[" << i << "].Filename: " << data->list[i].Filename << endl; cout << "list[" << i << "].Dlink: " << data->list[i].Dlink << endl; } }

异常处理

接口调用时,返回值不为0,表示遇到异常,获取异常的描述信息可以通过LastError()接口,该接口返回的字符串需要业务方自行回收内存空间。

参考示例:

PanlinkAgentConfig config;
int ret = InitPanlinkAgent(&config);
  if (ret != 0){
    char * err = LastError();
    if (err != NULL) {
      cout << "init panlink agent err, code=" << ret << " err=" << err << endl;
      free(err);
    } else {
      cout << "init panlink agent err, code=" << ret << " err=null" << endl;
    }

return ret;

}

停止Agent

需要提供UUID更新和文件下发消息接收时,调用以下函数:

int StopAgent();

PanlinkClient

暂不直接提供PanlinkClient的调用,优先使用PanlinkAgent来实现。

调试

Panlink SDK内嵌了日志库,可以通过以下两个函数来设置日志级别和日志文件输出位置,以便于调试。在没有设置SetLogPath时,日志会输出到标准错误输出(Stderr)。当需要关闭日志输出时,可以将日志级别设置为:LEVEL_EMERGENCY

void SetLogLevel(int p0);
void SetLogPath(char* p0);

日志级别

需要调试时,可以将日志级别设为 LEVEL_DEBUG

const int LEVEL_EMERGENCY = 0;
const int LEVEL_ALERT = 1;
const int LEVEL_CRITICAL = 2;
const int LEVEL_ERROR = 3;
const int LEVEL_WARNING = 4;
const int LEVEL_NOTICE = 6;
const int LEVEL_INFO = 7;
const int LEVEL_DEBUG = 8;
const int LEVEL_VERBOSE = 9;

EXAMPLE

example/host.cpp