概述
C++文件服务器项目—数据库表的设计—6
- 前言
- 1. 数据库建表
- 1.1 用户信息表 user_info
- 1.2 文件信息表 file_info
- 1.3 用户文件列表表 user_file_list
- 1.4 用户文件数量表 user_file_count
- 1.5 共享文件列表 share_file_list
- 1.6 共享图片列表 share_picture_list
- 1.7 导入数据库
- 2. 后端接口设计
- 2.1 注册 - reg
- 2.2 登陆 - login
- 2.3 上传文件 - 秒传 - md5
- 2.4 上传文件 - 真上传 - upload
- 2.5 获取用户文件数量 - myfiles
- 2.6 获取用户文件信息 - myfiles
- 2.7 获取共享列表数量 - sharefiles
- 2.8 获取共享列表文件 - sharefiles
- 2.9 下载榜 - sharefiles
- 2.10 文件分享 - dealfile
- 2.11 文件删除 - dealfile
- 2.12 文件下载之后, 下载量pv字段的处理 - dealfile
- 2.13 取消分享文件 - dealsharefile
- 2.14 转存文件 - dealsharefile
- 2.15 共享文件下载pv标志处理 - dealsharefile
- 2.16 图片分享 - sharepic
- 2.17 请求浏览图片 - sharepic
- 2.18 我的图片分享 - sharepic
- 2.19 取消图片分享 - sharepic
- 3. 总结
前言
到本文为止,该项目的前置知识已经写的差不多了。本文规划了数据库表的设计 和 后端与前端接口的设计,接下来就可以编写fastcgi程序进行运行了。项目以及项目相关源码地址:gopherWxf git
本专栏知识点是通过零声教育的线上课学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接 C/C++后台高级服务器课程介绍 详细查看课程的服务。
1. 数据库建表
1.1 用户信息表 user_info
字段 | 解释 |
---|
id | 用户序号,自动递增,主键 |
user_name | 用户名字 |
nick_name | 用户昵称 |
password | 密码 |
phone | 手机号码 |
email | 邮箱 |
create_time | 注册时间 |
CREATE TABLE `user_info`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户序号,自动递增,主键',
`user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名称',
`nick_name` varchar(32) CHARACTER SET utf8mb4 NOT NULL DEFAULT '' COMMENT '用户昵称',
`password` varchar(32) NOT NULL DEFAULT '' COMMENT '密码',
`phone` varchar(16) NOT NULL DEFAULT '' COMMENT '手机号码',
`email` varchar(64) DEFAULT '' COMMENT '邮箱',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_nick_name` (`nick_name`),
UNIQUE KEY `uq_user_name` (`user_name`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8 COMMENT ='用户信息表';
1.2 文件信息表 file_info
字段 | 解释 |
---|
id | 文件序号,自动递增,主键 |
md5 | 文件md5, 识别文件的唯一表示(身份证号) |
file_id | 文件id-/group1/M00/00/00/xxx.png |
url | 文件url 192.168.1.1:80/group1/M00/00/00/xxx.png - 下载的时候使用 |
size | 文件大小, 以字节为单位 |
type | 文件类型: png, zip, mp4…… |
count | 文件引用计数, 默认为1 每增加一个用户拥有此文件,此计数器+1 |
CREATE TABLE `file_info`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '文件序号,自动递增,主键',
`md5` varchar(256) NOT NULL COMMENT '文件md5',
`file_id` varchar(256) NOT NULL COMMENT '文件id:/group1/M00/00/00/xxx.png',
`url` varchar(512) NOT NULL COMMENT '文件url 192.168.52.139:80/group1/M00/00/00/xxx.png',
`size` bigint(20) DEFAULT '0' COMMENT '文件大小, 以字节为单位',
`type` varchar(32) DEFAULT '' COMMENT '文件类型: png, zip, mp4……',
`count` int(11) DEFAULT '0' COMMENT '文件引用计数,默认为1。每增加一个用户拥有此文件,此计数器+1',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8 COMMENT ='文件信息表';
1.3 用户文件列表表 user_file_list
字段 | 解释 |
---|
id | 序号,自动递增,主键 |
user | 文件所属用户 |
md5 | 文件md5 |
create_time | 文件创建时间 |
file_name | 文件名字 |
shared_status | 共享状态, 0为没有共享, 1为共享 |
pv | 文件下载量,默认值为0,下载一次加1 |
CREATE TABLE `user_file_list`
(
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`user` varchar(32) NOT NULL COMMENT '文件所属用户',
`md5` varchar(256) NOT NULL COMMENT '文件md5',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '文件创建时间',
`file_name` varchar(128) DEFAULT NULL COMMENT '文件名字',
`shared_status` int(11) DEFAULT NULL COMMENT '共享状态, 0为没有共享, 1为共享',
`pv` int(11) DEFAULT NULL COMMENT '文件下载量,默认值为0,下载一次加1',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8 COMMENT ='用户文件列表';
1.4 用户文件数量表 user_file_count
字段 | 解释 |
---|
id | 序号,自动递增,主键 |
user | 文件所属用户 |
count | 拥有文件的数量 |
CREATE TABLE `user_file_count`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` varchar(128) NOT NULL COMMENT '文件所属用户',
`count` int(11) DEFAULT NULL COMMENT '拥有文件的数量',
PRIMARY KEY (`id`),
UNIQUE KEY `user_UNIQUE` (`user`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8 COMMENT ='用户文件数量表';
1.5 共享文件列表 share_file_list
字段 | 解释 |
---|
id | 序号,自动递增,主键 |
user | 文件所属用户 |
md5 | 文件md5 |
file_name | 文件名字 |
pv | 文件下载量,默认值为1,下载一次加1 |
create_time | 文件共享时间 |
CREATE TABLE `share_file_list`
(
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`user` varchar(32) NOT NULL COMMENT '文件所属用户',
`md5` varchar(256) NOT NULL COMMENT '文件md5',
`file_name` varchar(128) DEFAULT NULL COMMENT '文件名字',
`pv` int(11) DEFAULT '1' COMMENT '文件下载量,默认值为1,下载一次加1',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '文件共享时间',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8 COMMENT ='共享文件列表';
1.6 共享图片列表 share_picture_list
字段 | 解释 |
---|
id | 序号,自动递增,主键 |
user | 图片所属用户 |
filemd5 | 图片md5 |
file_name | 图片名字 |
urlmd5 | 图片urlmd5 |
pv | 图片下载量,默认值为1,下载一次加1 |
create_time | 图片共享时间 |
CREATE TABLE `share_picture_list`
(
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`user` varchar(32) NOT NULL COMMENT '文件所属用户',
`filemd5` varchar(256) NOT NULL COMMENT '文件md5',
`file_name` varchar(128) DEFAULT NULL COMMENT '文件名字',
`urlmd5` varchar(256) NOT NULL COMMENT '图床urlmd5',
`pv` int(11) DEFAULT '1' COMMENT '文件下载量,默认值为1,下载一次加1',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '文件创建时间',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8 COMMENT ='共享图片列表';
1.7 导入数据库
gopher.sql
在前言源码中
root@wxf:/temp
mysql> source /temp/gopher.sql
2. 后端接口设计
2.1 注册 - reg
注册是一个简单的 HTTP 接口,根据用户输入的注册信息,创建一个新的用户。
URL | http://ip:port/api/reg |
---|
请求方式 | POST |
HTTP | 版本 1.1 |
Content-Type | application/json |
参数名 | 含义 | 规则说明 | 是否必须 | 缺省值 |
---|
userName | 用户名称 | 不能超过 32 个字符 | 必填 | 无 |
nickName | 用户昵称 | 不能超过 32 个字符 | 必填 | 无 |
firstPwd | 密码 | md5 | 加密后的值 | 必填 |
phone | 手机号码 | 不能超过 16 个字符 | 可选 | 无 |
email | 邮箱 | 必须符合 email 规范 | 可选 | 无 |
名称 | 含义 | 规则说明 |
---|
code | 结果值 | 0:成功 1:失败 2:用户存在 |
- 客户端
http://192.168.1.1/api/reg
{
userName:xxxx,
nickName:xxx,
firstPwd:xxx,
phone:xxx,
email:xxx
}
- 服务器端-Nginx
location /api/reg
{
fastcgi_pass localhost:10000;
include fastcgi.conf;
}
- 编写fastcgi程序
int main()
{
while(FCGI_Accept() >= 0)
{
// 1. 根据content-length得到post数据块的长度
// 2. 根据长度将post数据块读到内存
// 3. 解析json对象, 得到用户名, 密码, 昵称, 邮箱, 手机号
// 4. 连接数据库 - mysql, oracle
// 5. 查询, 看有没有用户名, 昵称冲突 -> {"code":"2"}
// 6. 有冲突 - 注册失败, 通知客户端
// 7. 没有冲突 - 用户数据插入到数据库中
// 8. 成功-> 通知客户端 -> {"code":"0"}
// 9. 通知客户端回传的字符串的格式
printf("content-type: application/jsonrn");
printf("{"code":"0"}");
}
}
2.2 登陆 - login
登录,根据用户输入的登录信息,登录进入到后台系统。
URL | http://ip:port/api/login |
---|
请求方式 | POST |
HTTP | 版本 1.1 |
Content-Type | application/json |
参数名 | 含义 | 规则说明 | 是否必须 | 缺省值 |
---|
user | 用户名称 | 不能超过 32 个字符 | 必填 | 无 |
pwd | 密码 | md5 加密后的值 | 必填 | 无 |
名称 | 含义 | 规则说明 |
---|
code | 结果值 | 0: 成功 1: 失败 |
token | 令牌 | 每次登录后,生成的 token 不一样,后续其他接口请求时,需要带上 token。 |
- 客户端
http://192.168.1.1:80/api/login
{
user:xxxx,
pwd:xxx
}
- 服务器端
location /aip/login
{
fastcgi_pass localhost:10001;
include fastcgi.conf;
}
- 编写fastcgi程序
int main()
{
while(FCGI_Accept() >= 0)
{
// 1. 根据content-length得到post数据块的长度
// 2. 根据长度将post数据块读到内存
// 3. 解析json对象, 得到用户名, 密码
// 4. 连接数据库 - mysql, oracle
// 5. 查询, 根据用户名密码查询有没有这条记录
// 6. 有记录- 登陆成功-生成token-> {"code":"0","token":"xxxx"}
// 7. 没有记录- 登陆失败-> {"code":"1","token":"faild"}
printf("content-type: application/jsonrn");
printf("{"code":"0","token":"xxx"}");
}
}
token -> 客户端成功连接了服务器, 服务器针对于客户端的个人信息生成了一个唯一的身份标识
- 可以按照每个人的身份证号理解
- 服务器将这个token发送给客户端
- 客户端token的使用:
- 使用: 登录成功之后, 向服务器在发送任意请求都需要携带该token值
- 服务器端的使用和保存:
- 使用: 接收客户端发送的token, 和服务器端保存的token进行认证
- 认证成功: 合法客户端, 失败: 客户端非法
- 保存: 服务器需要保存所有客户端的token
- 数据库中
- 配置文件 -> 效率低
- redis中 -> 效率最高
token = (客户端信息+随机数)*des*md5*base64
2.3 上传文件 - 秒传 - md5
上传文件的时候:
- 先调用 md5 接口判断服务器是否有该文件,如果 md 调用成功,则说明服务器已经存在该文件,客户端不需要再去调用 upload 接口上传文件。
- 如果不成功则客户端继续调用 upload 接口上传文件。
- 尝试秒传 -> 文件并没上传
- 给服务器发送的不是文件内容, 是文件的哈希值
- 在服务器端收到哈希值, 查询数据库
- 查到了 -> 秒传成功
- 没查到 -> 秒传失败, 需要进行一个真正的上传操作
- 进行真正的上传
- 需要的时间长
- 上传有文件内容, 文件的哈希值
- 文件内容 -> 分布式文件系统
- 哈希值 -> 数据库
URL | http://ip:port/api/md5 |
---|
请求方式 | POST |
HTTP 版本 | 1.1 |
Content-Type | application/json |
参数名 | 含义 | 规则说明 | 是否必须 | 缺省值 |
---|
token | 令牌 | 同上 | 必填 | 无 |
md5 | md5 值 | 不能超过 32 个字符 | 必填 | 无 |
filename | 文件名称 | 不能超过 128 个字符 | 必填 | 无 |
user | 用户名称 | 不能超过 32 个字符 | 必填 | 无 |
名称 | 含义 | 规则说明 |
---|
code | 结果值 | 0: 秒传成功1: 秒传失败 2: 已有该文件 3: token 校验失败 |
- 客户端
http://127.0.0.1:80/api/md5
{
user:xxxx,
token:xxxx,
md5:xxx,
fileName: xxx
}
location /api/md5
{
fastcgi_pass localhost:10003;
include fastcgi.conf;
}
int main()
{
while(FCGI_Accept() >= 0)
{
// 1. 得到post数据的长度
char* length = getenv("content-length");
int len = atoi(length);
// 2. 根据len将数据读到内存中, json对象字符串
// 3. 解析json对象, user,md5, token, fileName
// 4. token认证 , 查询redis/数据库
// -- 成功: 继续后续操作, 失败, 返回, 给客户端一个结果
// 5. 打开数据库, 并查询md5是否存在
// -- 存在 {"code":"0"}
-- 上传成功之后,业务逻辑--->存数据库一些相关信息
// -- 不存在 {"code":"1"}
}
}
2.4 上传文件 - 真上传 - upload
上传文件
URL | http://ip:port/api/upload |
---|
请求方式 | POST |
HTTP 版本 | 1.1 |
Content-Type | application/octet-stream |
参数名 | 含义 | 规则说明 | 是否必须 | 缺省值 |
---|
token | 密码 | md5 | 加密后的值 | 必填 |
名称 | 含义 | 规则说明 |
---|
code | 结果值 | 0: 上传成功 1: 上传失败 |
http://127.0.0.1:80/api/upload
------WebKitFormBoundary88asdgewtgewxrn
Content-Disposition: form-data; user="wxf"; filename="xxx.jpg"; md5="xxxx"; size=10240
Content-Type: image/jpg
------WebKitFormBoundary88asdgewtgewx--
location /api/upload
{
fastcgi_pass localhost:10002;
include fastcgi.conf;
}
// fastcgi程序
int main()
{
while(FCGI_Accept() >= 0)
{
// 1. 读一次数据 - buf, 保证能够将分界线和两个头都装进去
char buf[4096];
// len == 实际读出的字节数
int len = fread(buf, 1, 4096, stdin);
// 2. 跳过第一行的分界线
len = len - 第一行的长度
// 3. 将第二行中的user, filename, md5, size的值读出 -> 内存
len = len - 第二行的长度;
// 4. 跳过第3行
len = len-第三行的长度
// 5. 跳过空行
len = len-空行的长度;
// 6. 现在得到的位置的就是传输的真正数据的正文开始
// 7. 循环的将剩余的内容读出, 有必要就写磁盘
// 8. 读完了, 将最后的分界线剔除
// 9. 以上8步处理完毕, 文件内容就被扣出来了
}
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "fcgi_stdio.h"
int recv_save_file(char *user, char *filename, char *md5, long *p_size) {
int ret = 0;
char *file_buf = NULL;
char *begin = NULL;
char *p, *q, *k;
char content_text[512] = {0};
char boundary[512] = {0};
file_buf = (char *) malloc(4096);
if (file_buf == NULL) {
return -1;
}
int len = fread(file_buf, 1, 4096, stdin);
if (len == 0) {
ret = -1;
free(file_buf);
return ret;
}
begin = file_buf;
p = begin;
p = strstr(begin, "rn");
if (p == NULL) {
ret = -1;
free(file_buf);
return ret;
}
strncpy(boundary, begin, p - begin);
boundary[p - begin] = '