顶点云(应用)用户代理

设计用户代理,实现 DealWithRequests() 中的几种简单逻辑指令,如文件列表获取、文件拷贝、Fork操作等。

命令转发

  • 顶点云(应用)服务器逻辑实现 中,在校验用户身份后,客户端发送的指令会被转发给 rc.DealWithRequests() 方法执行,其中 rc 是用户对象。
  • 该方法是用户结构处理消息的入口,在函数头部先将用户的云盘信息、头像链接等发送给客户端,之后进入循环,等待客户端的指令,并依据指令的标识符转发:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
func (u *cuser) DealWithRequests(db *sql.DB) {
// 发送用户资料
u.listen.SendBytes([]byte(u.GetNickname())) // 用户昵称
u.listen.SendBytes(auth.Int64ToBytes(u.GetUsed())) // 用户已使用的容量
u.listen.SendBytes(auth.Int64ToBytes(u.GetMaxm())) // 用户云盘最大容量
avatar_link := u.GetAvatar()
if avatar_link == "" { // 用户不存在默认头像链接,发送 none
avatar_link = "none"
}
if avatar_link[0] == ':' { // 用户存在自定义头像外链,发送用户 id 及后缀
parts := strings.Split(avatar_link, ".")
suffix := parts[len(parts)-1]
u.listen.SendBytes([]byte(
fmt.Sprintf(`%d.%s`, u.id, suffix)))
} else { // 用户不存在自定义头像外链,发送 avatar 头像链接
u.listen.SendBytes([]byte(u.GetAvatar()))
}
u.curpath = "/"
for {
// 监听用户发送的命令
recvB, err := u.listen.RecvBytes()
if err != nil {
return
}
command := string(recvB)
fmt.Println(command)
switch {
case len(command) >= 2 && strings.ToUpper(command[:2]) == "RM":
// 删除资源
u.rm(db, command)
case len(command) >= 2 && strings.ToUpper(command[:2]) == "CP":
// 复制资源
u.cp(db, command)
// .... 其它指令
default:
// 指令无法识别,返回错误信息
u.listen.SendBytes([]byte("Invalid Command"))
}
}
}
  • 下面以文件列表作为例子,简述如何处理基本的逻辑。

文件列表获取

  • 要实现的系统规模不大,因此没有针对 Golang 编写 ORM,也没有使用现成的库,直接构造了 SQL 语句查找,因此看起来代码较复杂、凌乱。
  • 用户要获取某个目录下的文件列表,需要将查询路径包含在指令中发送,因此将列表获取的指令定义为 ls<SEP>是否递归查询<SEP>查询路径<SEP>搜索参数ls 方法根据指令参数决定是否递归遍历目录、是否按关键词检索。关键词数量可变。
  • 检索当前目录下文件时,构建的 SQL 语句为 path = 目录,递归检索时,SQL 为 path like '目录%'
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    func (u *cuser) ls(db *sql.DB, command string) {
    // 指令格式:LS<SEP>是否递归查询<SEP>查询路径<SEP>搜索参数
    var queryString string
    var returnString string = fmt.Sprintf(`UID%sPATH%sFILE%sCREATED TIME%sSIZE
    %sSHARED%sMODE`, conf.SEPERATER, conf.SEPERATER, conf.SEPERATER,

    conf.SEPERATER, conf.SEPERATER, conf.SEPERATER)
    var uid, ownerid, cfileid, shared, downloaded, cuid, csize, cref int
    var private, isdir bool
    var path, perlink, filename, linkpass, created, cmd5, ccreated string
    var err error
    var ufilelist *sql.Rows
    var recurssive int

    // 验证指令格式是否合法
    args := generateArgs(command, 0)
    valid := true
    argAll := "%"
    if args == nil || len(args) < 3 ||
    strings.ToUpper(args[0]) != "LS" ||
    !isPathFormatValid(args[2]) {
    valid = false
    goto LS_VERIFY
    }
    recurssive, err = strconv.Atoi(args[1])
    if err != nil || recurssive != 0 && recurssive != 1 {
    valid = false
    goto LS_VERIFY
    }
    for i := 3; i < len(args); i++ {
    if args[i] != "" {
    argAll += (args[i] + "%")
    }
    }

    // 调整当前用户所在的目录
    u.curpath = args[2]

    // 根据参数决定是否递归搜索
    if recurssive == 0 {
    queryString = fmt.Sprintf(`select uid, ownerid, cfileid, path, perlink,
    created, shared, downloaded, filename, private, linkpass, isdir
    from ufile where ownerid=%d and path='%s' and filename like '%s'`,

    u.id, u.curpath, argAll)
    } else {
    queryString = fmt.Sprintf(`select uid, ownerid, cfileid, path, perlink,
    created, shared, downloaded, filename, private, linkpass, isdir
    from ufile where ownerid=%d and path like '%s%%' and filename
    like '%s'`, u.id, u.curpath, argAll)

    }
    ufilelist, err = db.Query(queryString)
    if err != nil {
    valid = false
    goto LS_VERIFY
    }

    // 将资源列表中的资源信息依次格式化为字符串
    for ufilelist.Next() {
    err = ufilelist.Scan(&uid, &ownerid, &cfileid, &path, &perlink, &created,
    &shared, &downloaded, &filename, &private, &linkpass, &isdir)
    if err != nil {
    valid = false
    break
    }

    // 若引用实体文件则获取实体文件大小
    if cfileid >= 0 {
    tcfile := db.QueryRow(fmt.Sprintf(`SELECT uid, md5, size, ref,
    created FROM cfile where uid='%d'`, cfileid))

    if tcfile == nil {
    continue
    }
    err = tcfile.Scan(&cuid, &cmd5, &csize, &cref, &ccreated)
    if err != nil {
    continue
    }
    } else {
    csize = 0
    }
    // 添加格式化信息
    returnString += fmt.Sprintf("\n%d%s%s%s%s%s%s%s%d%s%d%s", uid,
    conf.SEPERATER, path, conf.SEPERATER, filename, conf.SEPERATER,
    created, conf.SEPERATER, csize, conf.SEPERATER, shared, conf.SEPERATER)

    // 根据是否为目录添加类型后缀
    if isdir {
    returnString += "DIR"
    } else {
    returnString += "FILE"
    }
    }
    LS_VERIFY:
    if !valid {
    // 请求失败时返回错误信息
    u.listen.SendBytes([]byte("error happens when querying files"))
    return
    }
    // 请求成功返回格式化的信息
    u.listen.SendBytes([]byte(returnString))
    }

专栏目录:顶点云(应用)设计与实现
此专栏的上一篇文章:顶点云(应用)服务器逻辑实现
此专栏的下一篇文章:顶点云(应用)用户文件传输代理

原创作品,允许转载,转载时无需告知,但请务必以超链接形式标明文章原始出处(http://blog.forec.cn/2016/12/03/zenith-cloud-7/) 、作者信息(Forec)和本声明。

分享到