当前位置: 首页 > news >正文

基于muduo库的图床云共享存储项目(五)

基于muduo库的图床云共享存储项目(五)

  • 处理文件
    • /api/dealfile?cmd=share 分享文件
      • 功能介绍
      • 代码实现
    • /api/dealfile?cmd=del 删除文件
      • 功能实现
      • 代码实现
    • /api/dealfile?cmd=pv 更新文件下载计数
      • 功能介绍
      • 代码实现
  • 获取共享文件或下载榜
    • /api/sharefiles?cmd=count 获取共享文件数量
      • 功能介绍
      • 代码实现
    • /api/sharefiles?cmd=normal 获取共享文件列表
      • 功能介绍
      • 代码实现
    • /api/sharefiles?cmd=pvdesc 获取共享文件下载排行榜
      • 功能介绍
      • 代码实现
  • 取消分享/转存/更新下载计数
    • api/dealsharefile?cmd=cancel 取消分享
      • 功能介绍
      • 代码实现
    • /api/dealsharefile?cmd=save 转存文件
      • 功能介绍
      • 代码实现
    • /api/dealsharefile?cmd=pv 更新共享文件下载计数
      • 功能介绍
      • 代码实现

本章节的重点就在于实现文件共享/删除功能,实现文件共享获取和共享排行榜。

处理文件

分享文件的功能主要的实现目标就是,不同的用户都可以对文件进行分享,分享完成以后每个用户都可以看见对应别的用户所分享的文件,可以对这些文件进行转存,即使对应的用户取消共享了,转存以后得文件依然是可以看见的,同时,也会更新共享文件的下载量以及一个下载榜单。

/api/dealfile?cmd=share 分享文件

对于分享文件,当前我们就不仅仅将其更新到 MySQL 中去了,还需要将其更新到 Redis 中去,因为我们需要更新对应的下载量以及下载榜单,所以当前环节就需要引入 Redis 的连接池了。

功能介绍

当前引入 Redis 以后,需要用到 Redis 的两个数据结构,hash 以及 zset 的结构,zest 结构因为本身就是一种有序且唯一的结构,可以用来实现排行榜,另外一个 hash 结构其实就是实现了对应的文件名文件MD5+文件名的映射关系,因为我们给用户展示的时候并不能将文件MD5+文件名这一大串数据全部返回,只需要返回对应的文件名即可。

在这里插入图片描述

请求URL

URLhttp://192.168.1.6/api/dealfile?cmd=share
请求方式POST
HTTP版本1.1
Content-Typeapplication/json

请求参数

参数名含义规则说明是否必须缺省值
token令牌同上必填
user用户名称不能超过32个字符必填
md5md5值md5加密后的值必填
filename文件名称不能超过128个字符必填

返回结果参数说明

名称含义规则说明
code结果值0: 成功
1: 失败
3: 别人已经分享此文件
4: token验证失败

在这里插入图片描述

代码实现

api_dealfile.h

#ifndef _API_DEALFILE_H_
#define _API_DEALFILE_H_
#include "api_common.h"
int ApiDealfile(string &url, string &post_data, string &str_json);#endif

api_dealfile.h

#include "api_dealfile.h"// 序列化
int encodeDealfileJson(int ret, string &str_json) {Json::Value root;root["code"] = ret;Json::FastWriter writer;str_json = writer.write(root);return 0;
}// 反序列化
int decodeDealfileJson(string &str_json, string &user_name, string &token,string &md5, string &filename) {bool res;Json::Value root;Json::Reader jsonReader;res = jsonReader.parse(str_json, root);if (!res) {LOG_ERROR << "parse reg json failed ";return -1;}if (root["user"].isNull()) {LOG_ERROR << "user null";return -1;}user_name = root["user"].asString();if (root["token"].isNull()) {LOG_ERROR << "token null";return -1;}token = root["token"].asString();if (root["md5"].isNull()) {LOG_ERROR << "md5 null";return -1;}md5 = root["md5"].asString();if (root["filename"].isNull()) {LOG_ERROR << "filename null";return -1;}filename = root["filename"].asString();return 0;
}// 分享文件
int handleShareFile(string &user, string &md5, string &filename)
{int share_state = 0;int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};char fileid[1024] = {0};    //md5+文件名// 获取对应的数据库连接CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("ranking_list");AUTO_REL_CACHECONN(cache_manager, cache_conn);// md5+文件名sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());// 先在redis的zset中去查询该文件// 如果有,证明别人已经分享,不需要在进行分享// 如果没有在进行分享if (cache_conn) {// ret = 1, 说明该文件已经存在ret = cache_conn->ZsetExit(FILE_PUBLIC_ZSET, fileid);LOG_INFO << "fileid: " << fileid << ", ZsetExit: " <<  ret;} else  {ret = 0;}if (ret == 1) {LOG_WARN << "别人已经分享此文件";share_state = 3;goto END;}// 如果redis中不存在,需要在mysql中去查看是否有人已经分享过此文件// 如果mysql中已经分享,redis中不存在,就需要将其添加到redis中去// 只有当mysql和redis中都不存在时,此时就进行分享sprintf(sql_cmd, "select * from share_file_list where md5 = '%s' and file_name = '%s'",md5.c_str(), filename.c_str()); //返回值:1有记录ret = CheckwhetherHaveRecord(db_conn, sql_cmd); //执行sql语句, 最后一个参数为NULLif (ret == 1) //说明有结果,别人已经分享此文件{// redis保存此文件信息cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, 0, fileid);cache_conn->Hset(FILE_NAME_HASH, fileid, filename);LOG_WARN << "别人已经分享此文件";share_state = 3;goto END;}// 走到这儿,说明没有人分享过该文件// 首先去user_file_list当中更新对应的分享状态sprintf(sql_cmd, "update user_file_list set shared_status = 1 where user = '%s' and ""md5 = '%s' and file_name = '%s'", user.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行 " << sql_cmd;if (!db_conn->ExecuteUpdate(sql_cmd, false)) {LOG_ERROR << sql_cmd << " 操作失败";share_state = 1;goto END;}time_t now;char create_time[TIME_STRING_LEN];//获取当前时间now = time(NULL);strftime(create_time, TIME_STRING_LEN - 1, "%Y-%m-%d %H:%M:%S", localtime(&now));//插入一条share_file_list记录sprintf(sql_cmd,  "insert into share_file_list (user, md5, create_time, file_name, pv) values ('%s', '%s', '%s', '%s', %d)",user.c_str(), md5.c_str(), create_time, filename.c_str(), 0);if (!db_conn->ExecuteCreate(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";share_state = 1;goto END;}//设置redis // redis保存此文件信息cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, 0, fileid);if (cache_conn->Hset(FILE_NAME_HASH, fileid, filename) < 0) {LOG_WARN << "Hset FILE_NAME_HASH failed";}share_state = 0;END:return share_state;
}int ApiDealfile(string &url, string &post_data, string &str_json)
{char cmd[20];string user_name;string token;string md5;      //文件md5码string filename; //文件名字int ret = 0;// url解析QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);LOG_INFO << "cmd = " <<  cmd;if (strcmp(cmd, "share") == 0) //分享文件{// 反序列化if(decodeDealfileJson(post_data, user_name, token, md5, filename) < 0) {encodeDealfileJson(1, str_json);return -1;}//token校验  //处理分享逻辑   序列化ret = handleShareFile(user_name , md5, filename);encodeDealfileJson(ret, str_json);}
}
  • handleShareFile 就是分享文件的主要逻辑,当前我们分享文件,首先会去 Redis 中进行查找,如果 Redis 中存在,就证明当前文件已经被分享过了,就不去进行分享了。
  • 当 Redis 中不存在的时候,也会去 MySQL 中进行查找,如果 MySQL 中当前已经被分享过了,那么就需要将其更新到 Redis 当中,然后将其置为已经被分享的状态,只有当 Redis 和 MySQL 中都没有了以后,此时才会真正的去进行分享文件的操作,更新 user_info_listshare_file_list 的信息,然后分享成功。

/api/dealfile?cmd=del 删除文件

删除文件就是去删除对应的文件列表的文件,这儿也包括对应的共享文件,所以当前接口是在这儿进行实现的。

功能实现

请求URL

URLhttp://192.168.1.6/api/dealfile?cmd=del
请求方式POST
HTTP版本1.1
Content-Typeapplication/json

请求参数

参数名含义规则说明是否必须缺省值
token令牌同上必填
user用户名称不能超过32个字符必填
md5md5值md5加密后的值必填
filename文件名称不能超过128个字符必填

返回结果参数说明

名称含义规则说明
code结果值0: 成功
1: 失败

在这里插入图片描述

代码实现

#include "api_dealfile.h"// 序列化
int encodeDealfileJson(int ret, string &str_json) {Json::Value root;root["code"] = ret;Json::FastWriter writer;str_json = writer.write(root);return 0;
}// 反序列化
int decodeDealfileJson(string &str_json, string &user_name, string &token,string &md5, string &filename) {bool res;Json::Value root;Json::Reader jsonReader;res = jsonReader.parse(str_json, root);if (!res) {LOG_ERROR << "parse reg json failed ";return -1;}if (root["user"].isNull()) {LOG_ERROR << "user null";return -1;}user_name = root["user"].asString();if (root["token"].isNull()) {LOG_ERROR << "token null";return -1;}token = root["token"].asString();if (root["md5"].isNull()) {LOG_ERROR << "md5 null";return -1;}md5 = root["md5"].asString();if (root["filename"].isNull()) {LOG_ERROR << "filename null";return -1;}filename = root["filename"].asString();return 0;
}// 分享文件
int handleShareFile(string &user, string &md5, string &filename)
{int share_state = 0;int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};char fileid[1024] = {0};    //md5+文件名// 获取对应的数据库连接CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("ranking_list");AUTO_REL_CACHECONN(cache_manager, cache_conn);// md5+文件名sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());// 先在redis的zset中去查询该文件// 如果有,证明别人已经分享,不需要在进行分享// 如果没有在进行分享if (cache_conn) {// ret = 1, 说明该文件已经存在ret = cache_conn->ZsetExit(FILE_PUBLIC_ZSET, fileid);LOG_INFO << "fileid: " << fileid << ", ZsetExit: " <<  ret;} else  {ret = 0;}if (ret == 1) {LOG_WARN << "别人已经分享此文件";share_state = 3;goto END;}// 如果redis中不存在,需要在mysql中去查看是否有人已经分享过此文件// 如果mysql中已经分享,redis中不存在,就需要将其添加到redis中去// 只有当mysql和redis中都不存在时,此时就进行分享sprintf(sql_cmd, "select * from share_file_list where md5 = '%s' and file_name = '%s'",md5.c_str(), filename.c_str()); //返回值:1有记录ret = CheckwhetherHaveRecord(db_conn, sql_cmd); //执行sql语句, 最后一个参数为NULLif (ret == 1) //说明有结果,别人已经分享此文件{// redis保存此文件信息cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, 0, fileid);cache_conn->Hset(FILE_NAME_HASH, fileid, filename);LOG_WARN << "别人已经分享此文件";share_state = 3;goto END;}// 走到这儿,说明没有人分享过该文件// 首先去user_file_list当中更新对应的分享状态sprintf(sql_cmd, "update user_file_list set shared_status = 1 where user = '%s' and ""md5 = '%s' and file_name = '%s'", user.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行 " << sql_cmd;if (!db_conn->ExecuteUpdate(sql_cmd, false)) {LOG_ERROR << sql_cmd << " 操作失败";share_state = 1;goto END;}time_t now;char create_time[TIME_STRING_LEN];//获取当前时间now = time(NULL);strftime(create_time, TIME_STRING_LEN - 1, "%Y-%m-%d %H:%M:%S", localtime(&now));//插入一条share_file_list记录sprintf(sql_cmd,  "insert into share_file_list (user, md5, create_time, file_name, pv) values ('%s', '%s', '%s', '%s', %d)",user.c_str(), md5.c_str(), create_time, filename.c_str(), 0);if (!db_conn->ExecuteCreate(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";share_state = 1;goto END;}//设置redis // redis保存此文件信息cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, 0, fileid);if (cache_conn->Hset(FILE_NAME_HASH, fileid, filename) < 0) {LOG_WARN << "Hset FILE_NAME_HASH failed";}share_state = 0;END:return share_state;
}//删除文件 这里是删除文件,不是取消分享
int handleDeleteFile(string &user, string &md5, string &filename) 
{int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};char fileid[1024] = {0};int count = 0;int shared_status = 0;        //共享状态int redis_has_record = 0; //标志redis是否有记录CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("ranking_list");AUTO_REL_CACHECONN(cache_manager, cache_conn);// 首先需要判断对应的文件是不是已经共享过了// 会去user_file_list当中进行查找// 执行sql语句sprintf(sql_cmd,"select shared_status from user_file_list where user = '%s' and md5 = '%s' and file_name = '%s'",user.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " <<  sql_cmd;ret = GetResultOneStatus(db_conn, sql_cmd, shared_status); //执行sql语句if (ret == 0) {LOG_INFO << "GetResultOneCount share  = " <<  shared_status;} else {LOG_ERROR << "GetResultOneStatus" << " 操作失败";ret = -1;goto END;}// 如果当前文件已经分享过了就需要从共享文件中删除if (1 == shared_status) {// 先从redis中进行删除//文件标识,文件md5+文件名sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());// 从有序集合中移除记录cache_conn->ZsetZrem(FILE_PUBLIC_ZSET, fileid);// 在从hash中移除记录cache_conn->Hdel(FILE_NAME_HASH, fileid);// 删除share_file_list中的共享文件sprintf(sql_cmd,"delete from share_file_list where user = '%s' and md5 = '%s' and file_name = '%s'",user.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " <<  sql_cmd;if (!db_conn->ExecuteDrop(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}}// user_file_list删除// 删除用户文件列表数据sprintf(sql_cmd,"delete from user_file_list where user = '%s' and md5 = '%s' and file_name = '%s'",user.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " <<  sql_cmd;if (!db_conn->ExecuteDrop(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}//文件信息表(file_info)的文件引用计数count,减去1//查看该文件文件引用计数sprintf(sql_cmd, "select count from file_info where md5 = '%s'", md5.c_str());LOG_INFO << "执行: " <<  sql_cmd;count = 0;ret = GetResultOneCount(db_conn, sql_cmd, count); //执行sql语句LOG_INFO << "ret: {}, count: " <<  ret, count;if (ret != 0) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}if (count > 0) {//如果对应这个文件引用计数 -1count -= 1;sprintf(sql_cmd, "update file_info set count=%d where md5 = '%s'", count, md5.c_str());LOG_INFO << "执行: " <<  sql_cmd;if (!db_conn->ExecuteUpdate(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}}if(count == 0) {// file_info 删除sprintf(sql_cmd, "select file_id from file_info where md5 = '%s'",  md5.c_str());string fileid;CResultSet *result_set = db_conn->ExecuteQuery(sql_cmd);if (result_set->Next()) {fileid = result_set->GetString("file_id");}//删除文件信息表中该文件的信息sprintf(sql_cmd, "delete from file_info where md5 = '%s'", md5.c_str());if (!db_conn->ExecuteDrop(sql_cmd)) {LOG_WARN << sql_cmd << " 操作失败";}//从storage服务器删除此文件,参数为为文件idret = RemoveFileFromFastDfs(fileid.c_str());if (ret != 0) {LOG_INFO << "RemoveFileFromFastDfs err: " <<  ret;ret = -1;goto END;}// fastdfs也考虑删除}ret = 0;END:/*删除文件:成功:{"code":"0"}失败:{"code":"1"}*/if (ret == 0) {return 0;} else {return 1;}
}int ApiDealfile(string &url, string &post_data, string &str_json)
{char cmd[20];string user_name;string token;string md5;      //文件md5码string filename; //文件名字int ret = 0;// url解析QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);LOG_INFO << "cmd = " <<  cmd;if (strcmp(cmd, "share") == 0) //分享文件{// 反序列化if(decodeDealfileJson(post_data, user_name, token, md5, filename) < 0) {encodeDealfileJson(1, str_json);return -1;}//token校验  //处理分享逻辑   序列化ret = handleShareFile(user_name , md5, filename);encodeDealfileJson(ret, str_json);} else if (strcmp(cmd, "del")) {// 反序列化if(decodeDealfileJson(post_data, user_name, token, md5, filename) < 0) {encodeDealfileJson(1, str_json);return -1;}//token校验 ret = handleDeleteFile(user_name, md5, filename);encodeDealfileJson(ret, str_json);} else if (strcmp(cmd, "pv") == 0) //文件下载标志处理{}else {LOG_ERROR << "un handle cmd " << cmd;}
}
  • 对于删除文件来说,我们首先就需要注意当前文件是不是已经共享过了,如果已经共享过了,就需要先将共享文件给删除掉,先去 Redis 中删除掉对应的记录,然后在删除掉 MySQL 中的记录,然后再去删除对应的文件。
  • 同时我们需要注意的是,删除掉文件其实并不一次就将他给删除掉了,只是从当前我的文件列表中删除,因为文件的MD5可能是一样的,就意味着会有多个人持有该文件,我们只是将对应的引用计数进行--操作,只有引用计数减为 0 的时候,才会去真正的将文件删除。
  • 同样,我们不止需要删除对应文件表中的数据,还需要删除掉 fastdfs 中的数据。

/api/dealfile?cmd=pv 更新文件下载计数

功能介绍

是用来更新指定文件的下载量,每次成功下载一个文件成功后,调用该接口更新对应文件的pv值。

请求URL

URLhttp://192.168.1.6/api/dealfile?cmd=pv
请求方式POST
HTTP版本1.1
Content-Typeapplication/json

请求参数

参数名含义规则说明是否必须缺省值
token令牌同上必填
user用户名称不能超过32个字符必填
md5md5值md5加密后的值必填
filename文件名称不能超过128个字符必填

返回结果参数说明

名称含义规则说明
code结果值0:成功
1:失败

在这里插入图片描述

代码实现

// 更新文件下载数量
static int handlePvFile(string &user, string &md5, string &filename) {int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};int pv = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);// sql语句//查看该文件的pv字段sprintf(sql_cmd,"select pv from user_file_list where user = '%s' and md5 = '%s' and file_name = '%s'",user.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " <<  sql_cmd;CResultSet *result_set = db_conn->ExecuteQuery(sql_cmd);if (result_set && result_set->Next()) {pv = result_set->GetInt("pv");} else {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}//更新该文件pv字段,+1sprintf(sql_cmd,  "update user_file_list set pv = %d where user = '%s' and md5 = ""'%s' and file_name = '%s'",pv + 1, user.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " <<  sql_cmd;if (!db_conn->ExecuteUpdate(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}    
END:/*下载文件pv字段处理成功:{"code":0}失败:{"code":1}*/if (ret == 0) {return (0);} else {return (1);}
}int ApiDealfile(string &url, string &post_data, string &str_json)
{char cmd[20];string user_name;string token;string md5;      //文件md5码string filename; //文件名字int ret = 0;// url解析QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);LOG_INFO << "cmd = " <<  cmd;if (strcmp(cmd, "share") == 0) //分享文件{// 反序列化if(decodeDealfileJson(post_data, user_name, token, md5, filename) < 0) {encodeDealfileJson(1, str_json);return -1;}//token校验  //处理分享逻辑   序列化ret = handleShareFile(user_name , md5, filename);encodeDealfileJson(ret, str_json);} else if (strcmp(cmd, "del")) {// 反序列化if(decodeDealfileJson(post_data, user_name, token, md5, filename) < 0) {encodeDealfileJson(1, str_json);return -1;}//token校验 ret = handleDeleteFile(user_name, md5, filename);encodeDealfileJson(ret, str_json);} else if (strcmp(cmd, "pv") == 0) //文件下载标志处理{// 反序列化if(decodeDealfileJson(post_data, user_name, token, md5, filename) < 0) {encodeDealfileJson(1, str_json);return -1;}ret = handlePvFile(user_name, md5, filename);encodeDealfileJson(ret, str_json);}else {LOG_ERROR << "un handle cmd " << cmd;}
}

获取共享文件或下载榜

/api/sharefiles?cmd=count 获取共享文件数量

功能介绍

当前功能就是为了获取到共享文件的数量。

请求URL

URLhttp://192.168.1.6/api/sharefiles?cmd=count
请求方式GET
HTTP版本1.1
Content-Typeapplication/x-www-form-urlencoded

返回结果参数说明

名称含义规则说明
code结果码0:正常
1:失败
total总数量

在这里插入图片描述

代码实现

api_sharefiles.h

#ifndef _API_SHAREFILES_H_
#define _API_SHAREFILES_H_
#include "api_common.h"
int ApiSharefiles(string &url, string &post_data, string &str_json);#endif

api_sharefiles.cc

#include "api_sharefiles.h"// 进行序列化数据
int encodeSharefilesJson(int ret, int total, string &str_json) {Json::Value root;root["code"] = ret;if (ret == 0) {root["total"] = total; // 正常返回的时候才写入total}Json::FastWriter writer;str_json = writer.write(root);return 0;
}// 获取共享文件的数量
int handleGetSharefilesCount(int &count) {int ret = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);// 执行sql语句string str_sql = "select count(*) from share_file_list";CResultSet *result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set && result_set->Next()) {// 存在在返回count = result_set->GetInt("count(*)");LOG_INFO << "count: " << count;ret = 0;delete result_set;} else if (!result_set) {// 操作失败LOG_ERROR << str_sql << " 操作失败";ret = 1;}return ret;
}int ApiSharefiles(string &url, string &post_data, string &str_json)
{char cmd[20];string user_name;string token;int start = 0; //文件起点int count = 0; //文件个数int total = 0;int ret = 0;LOG_INFO << "post_data: " <<  post_data;//解析命令 解析url获取自定义参数QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);LOG_INFO << "cmd = " <<  cmd;if (strcmp(cmd, "count") == 0) {ret = handleGetSharefilesCount(total);encodeSharefilesJson(ret, total, str_json);} else if(strcmp(cmd, "normal") == 0) {}
}

/api/sharefiles?cmd=normal 获取共享文件列表

功能介绍

当前实现的就是获取到共享文件的列表。

请求URL

URLhttp://192.168.1.6/api/sharefiles?cmd=normal
请求方式POST
HTTP版本1.1
Content-Typeapplication/json

请求参数

参数名含义规则说明是否必须缺省值
count数量必填
start开始位置必填

返回结果参数说明

名称含义规则说明
files文件结果集“code”:0:正常;1:失败
“count”: 2,// 分页返回数量,如果为0则不需要解析files
“total”: 2,总的文件数量
“user”: 用户名称
“md5”: md5值
“create_time”: 创建时间
“file_name”: 文件名
“share_status”: 共享状态,0为没有共享, 1为共享
“pv”: 文件下载量,下载一次加1
“url”: URL
“size”: 文件大小
“type”: 文件类型

在这里插入图片描述

代码实现

#include "api_sharefiles.h"// 进行序列化数据
int encodeSharefilesJson(int ret, int total, string &str_json) {Json::Value root;root["code"] = ret;if (ret == 0) {root["total"] = total; // 正常返回的时候才写入total}Json::FastWriter writer;str_json = writer.write(root);return 0;
}// 获取共享文件的数量
int handleGetSharefilesCount(int &count) {int ret = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);// 执行sql语句string str_sql = "select count(*) from share_file_list";CResultSet *result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set && result_set->Next()) {// 存在在返回count = result_set->GetInt("count(*)");LOG_INFO << "count: " << count;ret = 0;delete result_set;} else if (!result_set) {// 操作失败LOG_ERROR << str_sql << " 操作失败";ret = 1;}return ret;
}// 反序列化操作
int decodeShareFileslistJson(string &str_json, int &start, int &count) {bool res;Json::Value root;Json::Reader jsonReader;res = jsonReader.parse(str_json, root);if (!res) {LOG_ERROR << "parse reg json failed ";return -1;}if (root["start"].isNull()) {LOG_ERROR << "start null";return -1;}start = root["start"].asInt();if (root["count"].isNull()) {LOG_ERROR << "count null";return -1;}count = root["count"].asInt();return 0;
}// 获取共享文件列表,其实就是去share_file_list和file_info中去获取
void handleGetShareFilelist(int start, int count, string &str_json) {int ret = 0;string str_sql;int total = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);CResultSet *result_set = NULL;int file_index = 0;Json::Value root, files;//获取总的文件数量ret =  handleGetSharefilesCount(total);if(ret != 0) {ret = -1;goto END;}// sql语句str_sql = FormatString("select share_file_list.*, file_info.url, file_info.size, file_info.type from file_info, \share_file_list where file_info.md5 = share_file_list.md5 limit %d, %d",start, count);LOG_INFO << "执行: " <<  str_sql;result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set) { // 遍历所有的内容// 获取大小file_index = 0;root["total"] = total;while (result_set->Next()) {Json::Value file;file["user"] = result_set->GetString("user");file["md5"] = result_set->GetString("md5");file["file_name"] = result_set->GetString("file_name");file["share_status"] = result_set->GetInt("share_status");file["pv"] = result_set->GetInt("pv");file["create_time"] = result_set->GetString("create_time");file["url"] = result_set->GetString("url");file["size"] = result_set->GetInt("size");file["type"] = result_set->GetString("type");files[file_index++] = file;  }root["count"] = file_index;if(file_index > 0) {root["files"] = files;    } else {LOG_WARN << "no files";}ret = 0;delete result_set;} else {ret = -1;}END:if (ret == 0) {root["code"] = 0;} else {root["code"] = 1;}str_json = root.toStyledString();
}int ApiSharefiles(string &url, string &post_data, string &str_json)
{char cmd[20];string user_name;string token;int start = 0; //文件起点int count = 0; //文件个数int total = 0;int ret = 0;LOG_INFO << "post_data: " <<  post_data;//解析命令 解析url获取自定义参数QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);LOG_INFO << "cmd = " <<  cmd;if (strcmp(cmd, "count") == 0) {ret = handleGetSharefilesCount(total);encodeSharefilesJson(ret, total, str_json);} else if(strcmp(cmd, "normal") == 0) {if (decodeShareFileslistJson(post_data, start, count) < 0){ //通过json包获取信息encodeSharefilesJson(1, 0, str_json);return 0;}// 获取共享文件handleGetShareFilelist(start, count, str_json);}
}

其实当前获取共享文件列表的实现与获取文件列表的实现是相似的,只不过是当前使用的是share_file_listfile_info两张表而已,这部分内容可以去参考对应的文件列表的实现。

/api/sharefiles?cmd=pvdesc 获取共享文件下载排行榜

功能介绍

当前实现的就是共享文件的一个下载排行榜,就是对应的共享文件有多少个人去进行下载,然后会进行排序操作。

请求URL

URLhttp://192.168.1.6/api/sharefiles?cmd=pvdesc
请求方式POST
HTTP版本1.1
Content-Typeapplication/json

请求参数

参数名含义规则说明是否必须缺省值
count数量必填
start开始位置必填

返回结果参数说明

名称含义规则说明
files文件结果集“filename”: 文件名
“pv”: 文件下载量,下载一次加1
code返回码0:正常
1:失败
cout分页文件数量比如:1,如果为0则不需要解析files
total总文件数量比如:2

代码实现

#include "api_sharefiles.h"// 进行序列化数据
int encodeSharefilesJson(int ret, int total, string &str_json) {Json::Value root;root["code"] = ret;if (ret == 0) {root["total"] = total; // 正常返回的时候才写入total}Json::FastWriter writer;str_json = writer.write(root);return 0;
}// 获取共享文件的数量
int handleGetSharefilesCount(int &count) {int ret = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);// 执行sql语句string str_sql = "select count(*) from share_file_list";CResultSet *result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set && result_set->Next()) {// 存在在返回count = result_set->GetInt("count(*)");LOG_INFO << "count: " << count;ret = 0;delete result_set;} else if (!result_set) {// 操作失败LOG_ERROR << str_sql << " 操作失败";ret = 1;}return ret;
}// 反序列化操作
int decodeShareFileslistJson(string &str_json, int &start, int &count) {bool res;Json::Value root;Json::Reader jsonReader;res = jsonReader.parse(str_json, root);if (!res) {LOG_ERROR << "parse reg json failed ";return -1;}if (root["start"].isNull()) {LOG_ERROR << "start null";return -1;}start = root["start"].asInt();if (root["count"].isNull()) {LOG_ERROR << "count null";return -1;}count = root["count"].asInt();return 0;
}// 获取共享文件列表,其实就是去share_file_list和file_info中去获取
void handleGetShareFilelist(int start, int count, string &str_json) {int ret = 0;string str_sql;int total = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);CResultSet *result_set = NULL;int file_index = 0;Json::Value root, files;//获取总的文件数量ret =  handleGetSharefilesCount(total);if(ret != 0) {ret = -1;goto END;}// sql语句str_sql = FormatString("select share_file_list.*, file_info.url, file_info.size, file_info.type from file_info, \share_file_list where file_info.md5 = share_file_list.md5 limit %d, %d",start, count);LOG_INFO << "执行: " <<  str_sql;result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set) { // 遍历所有的内容// 获取大小file_index = 0;root["total"] = total;while (result_set->Next()) {Json::Value file;file["user"] = result_set->GetString("user");file["md5"] = result_set->GetString("md5");file["file_name"] = result_set->GetString("file_name");file["share_status"] = result_set->GetInt("share_status");file["pv"] = result_set->GetInt("pv");file["create_time"] = result_set->GetString("create_time");file["url"] = result_set->GetString("url");file["size"] = result_set->GetInt("size");file["type"] = result_set->GetString("type");files[file_index++] = file;  }root["count"] = file_index;if(file_index > 0) {root["files"] = files;    } else {LOG_WARN << "no files";}ret = 0;delete result_set;} else {ret = -1;}END:if (ret == 0) {root["code"] = 0;} else {root["code"] = 1;}str_json = root.toStyledString();
}// 获取对应的下载排行榜
void handleGetRankingFilelist(int start, int count, string &str_json) {/*首先我们需要检查mysql中的数量和redis中数量是否一致如果不一致,我们就需要将MySQL中的数量更新到redis中然后去redis中获取到对应的排行榜*/int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};int total = 0;char filename[512] = {0};int sql_num;int redis_num;int score;int end;RVALUES value = NULL;Json::Value root;Json::Value files;int file_count = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);CResultSet *pCResultSet = NULL;CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("ranking_list");AUTO_REL_CACHECONN(cache_manager, cache_conn);// 先去MySQL中获取对应的文件数量ret = handleGetSharefilesCount(total);if (ret != 0) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}sql_num = total;// redis中共享文件的数量redis_num = cache_conn->ZsetZcard(FILE_PUBLIC_ZSET); // Zcard 命令用于计算集合中元素的数量if (redis_num == -1) {LOG_ERROR << "ZsetZcard  操作失败";ret = -1;goto END;}LOG_INFO << "sql_num: " << sql_num << ", redis_num: " <<  redis_num;if (sql_num != redis_num) {// 两者数据不相等,先清空redis中的数据,然后吧MySQL中数据导入// 清空redis中数据cache_conn->Del(FILE_PUBLIC_ZSET); // 删除集合cache_conn->Del(FILE_NAME_HASH); // 删除hash, 理解 这里hash和集合的关系// 从mysql中导入数据到redis  所有共享文件都加载redis// 执行sql语句strcpy( sql_cmd, "select md5, file_name, pv from share_file_list order by pv desc");LOG_INFO << "执行: " <<  sql_cmd;pCResultSet = db_conn->ExecuteQuery(sql_cmd);if (!pCResultSet) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}// mysql_fetch_row从使用mysql_store_result得到的结果结构中提取一行,并把它放到一个行结构中// 当数据用完或发生错误时返回NULLwhile (pCResultSet->Next()){// 这里如果文件数量特别多,导致耗时严重,// 可以这么去改进当// mysql的记录和redis不一致的时候,开启一个后台线程去做同步char field[1024] = {0};string md5 = pCResultSet->GetString("md5"); // 文件的MD5string file_name = pCResultSet->GetString("file_name"); // 文件名int pv = pCResultSet->GetInt("pv");sprintf(field, "%s%s", md5.c_str(), file_name.c_str()); //文件标示,md5+文件名//增加有序集合成员cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, pv, field);//增加hash记录cache_conn->Hset(FILE_NAME_HASH, field, file_name);}delete pCResultSet;}// 从redis读取下载榜单value = (RVALUES)calloc(count, VALUES_ID_SIZE); //堆区请求空间if (value == NULL) {ret = -1;goto END;}end = start + count - 1; //加载资源的结束位置  start = 0; count = 10; end = 9;  [0, 9]//降序获取有序集合的元素   file_count获取实际返回的个数ret = cache_conn->ZsetZrevrange(FILE_PUBLIC_ZSET, start, end, value, file_count);if (ret != 0) {LOG_ERROR << "ZsetZrevrange 操作失败";ret = -1;goto END;}//遍历元素个数for (int i = 0; i < file_count; ++i) {ret = cache_conn->Hget(FILE_NAME_HASH, value[i], filename);if (ret != 0) {LOG_ERROR << "hget  操作失败";ret = -1;goto END;}Json::Value file;file["filename"] = filename; // zset 根据 member名字获取scoreint score = cache_conn->ZsetGetScore(FILE_PUBLIC_ZSET, value[i]);if (score == -1) {LOG_ERROR << "ZsetGetScore  操作失败";ret = -1;goto END;}file["pv"] = score;files[i] = file;}ret = 0;// json
END:if(ret == 0) {root["code"] = 0;root["total"] = sql_num;root["count"]  = file_count;root["files"] = files;} else {root["code"] = 1;}str_json = root.toStyledString();
}int ApiSharefiles(string &url, string &post_data, string &str_json)
{char cmd[20];string user_name;string token;int start = 0; //文件起点int count = 0; //文件个数int total = 0;int ret = 0;LOG_INFO << "post_data: " <<  post_data;//解析命令 解析url获取自定义参数QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);LOG_INFO << "cmd = " <<  cmd;if (strcmp(cmd, "count") == 0) {ret = handleGetSharefilesCount(total);encodeSharefilesJson(ret, total, str_json);} else if(strcmp(cmd, "normal") == 0) {if (decodeShareFileslistJson(post_data, start, count) < 0){ //通过json包获取信息encodeSharefilesJson(1, 0, str_json);return 0;}// 获取共享文件handleGetShareFilelist(start, count, str_json);} else if(strcmp(cmd, "=pvdesc") == 0) {if (decodeShareFileslistJson(post_data, start, count) < 0){ //通过json包获取信息encodeSharefilesJson(1, 0, str_json);return 0;}// 获取排行榜handleGetRankingFilelist(start, count, str_json);}
}
  • 获取共享文件的下载排行榜,首先我们需要检测MySQL和Redis中的数据是否一致,如果一致,我们才会去获取对应的共享文件数量,不一致的话我们首先就需要先将MySQL中的数据更新到Redis中去,然后在获取对应的共享文件数量。
  • 当前如果MySQL和Redis中的数据不一致,我们所采取的策略就是将Redis中的数据先进行清空,然后再将MySQL中的数据导入到Redis当中去,对应的zset以及hash中都需要进行添加。
  • 如果当前文件太多的话,我们可以优化对应的策略,就是不使用前台进程进行导入了,采用后台进程进行导入的方式,这样就不会阻塞前台进程的一系列操作了。
  • 从Redis读取对应的榜单,我们所做的操作就是获取到对应的zset中的数量,然后根据数量去对应的hash中获取到文件名,因为zset中的数据是有序的,我们采用倒序遍历的方式,依次获取下载次数由多到少的文件,这样就可以获取到我们对应的下载榜单了。

取消分享/转存/更新下载计数

api/dealsharefile?cmd=cancel 取消分享

功能介绍

当前所实现的就是取消对应的文件共享功能。

请求URL

URLhttp://192.168.1.6/api/dealsharefile?cmd=cancel
请求方式POST
HTTP版本1.1
Content-Typeapplication/json

请求参数

参数名含义规则说明是否必须缺省值
user用户名称不能超过32个字符必填
md5md5值md5加密后的值必填
filename文件名称不能超过128个字符必填

返回结果参数说明

名称含义规则说明
code结果值0: 成功
1: 失败

代码实现

api_deal_sharefiles.h

#ifndef _API_DEALSHAREFILE_H_
#define _API_DEALSHAREFILE_H_
#include "api_common.h"
int ApiDealsharefile(string &url, string &post_data, string &str_json);
#endif // ! _API_DEALSHAREFILE_H_

api_deal_sharefiles.cc

#include "api_deal_sharefile.h"// 数据反序列化
int decodeDealsharefileJson(string &str_json, string &user_name, string &md5,string &filename) {bool res;Json::Value root;Json::Reader jsonReader;res = jsonReader.parse(str_json, root);if (!res) {LOG_ERROR << "parse reg json failed";return -1;}if (root["user"].isNull()) {LOG_ERROR << "user null";return -1;}user_name = root["user"].asString();if (root["md5"].isNull()) {LOG_ERROR << "md5 null";return -1;}md5 = root["md5"].asString();if (root["filename"].isNull()) {LOG_ERROR << "filename null";return -1;}filename = root["filename"].asString();return 0;
}// 数据序列化
int encodeDealsharefileJson(int ret, string &str_json) {Json::Value root;root["code"] = ret;Json::FastWriter writer;str_json = writer.write(root);return 0;
}// 取消共享文件
// 就是要删除对应Redis跟MySQL当中的记录
int handleCancelShareFile(string &user_name, string &md5, string &filename) {int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};char fileid[512] = {0}; //md5 + filenameCDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("ranking_list");AUTO_REL_CACHECONN(cache_manager, cache_conn);//文件标示,md5+文件名sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());// 设置分享状态 user_file_list// 共享标志设置为0sprintf(sql_cmd, "update user_file_list set shared_status = 0 where user = '%s' and md5 = '%s' and file_name = '%s'",user_name.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " << sql_cmd;;if (!db_conn->ExecuteUpdate(sql_cmd, false)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}// 删除 share_file_list 记录  deletesprintf(sql_cmd, "delete from share_file_list where user = '%s' and md5 = '%s' and file_name = '%s'",user_name.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " << sql_cmd ;if (!db_conn->ExecuteDrop(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}// 从redis删除对应的keyret = cache_conn->ZsetZrem(FILE_PUBLIC_ZSET, fileid);if (ret != 0) {LOG_INFO << "执行: ZsetZrem 操作失败";goto END;}//从hash移除相应记录LOG_INFO << "Hdel FILE_NAME_HASH  " << fileid;ret = cache_conn->Hdel(FILE_NAME_HASH, fileid);if (ret < 0) {LOG_INFO << "执行: hdel 操作失败: ret = " << ret;goto END;}END:/*取消分享:成功:{"code": 0}失败:{"code": 1}*/if (ret == 0) {return (0);} else {return (1);}
}
int ApiDealsharefile(string &url, string &post_data, string &str_json)
{// 取消分享 删除 share_file_list记录   从redis删除对应的key// 转存 user_file_list添加记录, file_info count + 1// 更新下载  share_file_list pv +1,   redis zset score  +1char cmd[20];string user_name;string md5;      //文件md5码string filename; //文件名字int ret = 0;//解析命令QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);ret = decodeDealsharefileJson(post_data, user_name, md5, filename);LOG_INFO << "cmd: " << cmd << ", user_name:" << user_name << ", md5: "<< md5 << ", filename: " << filename;if (ret != 0) {encodeDealsharefileJson(1, str_json);return 0;}if (strcmp(cmd, "cancel") == 0) //取消分享文件{ret = handleCancelShareFile(user_name, md5, filename);} else if (strcmp(cmd, "save") == 0) //转存文件{} else if (strcmp(cmd, "pv") == 0) //文件下载标志处理{}if (ret < 0) {encodeDealsharefileJson(1, str_json);}else {encodeDealsharefileJson(0, str_json);}return 0;
}
  • 对于取消共享文件的逻辑,我们是需要将MySQL和Redis中的记录都进行删除的,只有这样,才算是真正的将对应的文件取消掉。
  • 首先我们需要将对应的file_info表中的文件分享状态设置为0,此时就表示该文件已经不是被分享的文件了,接着就是将share_file_list中的这条文件记录去删除掉,然后再在Redis的zset和hash中将对应的记录也清除掉,这样就完成了对应桥文件分享功能。

/api/dealsharefile?cmd=save 转存文件

功能介绍

当前功能实现的就是我们可以将对应的分享的文件进行转存,转存到自己的用户下。

请求URL

URLhttp://192.168.1.6/api/dealsharefile?cmd=save
请求方式POST
HTTP版本1.1
Content-Typeapplication/json

请求参数

参数名含义规则说明是否必须缺省值
user用户名称不能超过32个字符必填
md5md5值md5加密后的值必填
filename文件名称不能超过128个字符必填

返回结果参数说明

名称含义规则说明
code结果值0: 成功
1: 失败
5: 文件已存在

代码实现

#include "api_deal_sharefile.h"// 数据反序列化
int decodeDealsharefileJson(string &str_json, string &user_name, string &md5,string &filename) {bool res;Json::Value root;Json::Reader jsonReader;res = jsonReader.parse(str_json, root);if (!res) {LOG_ERROR << "parse reg json failed";return -1;}if (root["user"].isNull()) {LOG_ERROR << "user null";return -1;}user_name = root["user"].asString();if (root["md5"].isNull()) {LOG_ERROR << "md5 null";return -1;}md5 = root["md5"].asString();if (root["filename"].isNull()) {LOG_ERROR << "filename null";return -1;}filename = root["filename"].asString();return 0;
}// 数据序列化
int encodeDealsharefileJson(int ret, string &str_json) {Json::Value root;root["code"] = ret;Json::FastWriter writer;str_json = writer.write(root);return 0;
}// 取消共享文件
// 就是要删除对应Redis跟MySQL当中的记录
int handleCancelShareFile(string &user_name, string &md5, string &filename) {int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};char fileid[512] = {0}; //md5 + filenameCDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("ranking_list");AUTO_REL_CACHECONN(cache_manager, cache_conn);//文件标示,md5+文件名sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());// 设置分享状态 user_file_list// 共享标志设置为0sprintf(sql_cmd, "update user_file_list set shared_status = 0 where user = '%s' and md5 = '%s' and file_name = '%s'",user_name.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " << sql_cmd;;if (!db_conn->ExecuteUpdate(sql_cmd, false)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}// 删除 share_file_list 记录  deletesprintf(sql_cmd, "delete from share_file_list where user = '%s' and md5 = '%s' and file_name = '%s'",user_name.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " << sql_cmd ;if (!db_conn->ExecuteDrop(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}// 从redis删除对应的keyret = cache_conn->ZsetZrem(FILE_PUBLIC_ZSET, fileid);if (ret != 0) {LOG_INFO << "执行: ZsetZrem 操作失败";goto END;}//从hash移除相应记录LOG_INFO << "Hdel FILE_NAME_HASH  " << fileid;ret = cache_conn->Hdel(FILE_NAME_HASH, fileid);if (ret < 0) {LOG_INFO << "执行: hdel 操作失败: ret = " << ret;goto END;}END:/*取消分享:成功:{"code": 0}失败:{"code": 1}*/if (ret == 0) {return (0);} else {return (1);}
}//转存文件
//返回值:0 成功,1 失败,5  文件已存在
int handleSaveFile(string &user_name, string &md5, string &filename)
{int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};//当前时间戳struct timeval tv;struct tm *ptm;char creat_time[128];int count;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);// 是否已经存在这个文件 user_file_list  md5 filename username// 查看此用户,文件名和md5是否存在,如果存在说明此文件存在sprintf(sql_cmd, "select * from user_file_list where user = '%s' and md5 = '%s' and  file_name = '%s'",user_name.c_str(), md5.c_str(), filename.c_str());// 有记录返回1,错误返回-1,无记录返回0ret = CheckwhetherHaveRecord(db_conn, sql_cmd);if (ret == 1) { //如果有结果,说明此用户已有此文件LOG_ERROR << "user_name: " << user_name << ", filename: " << filename << ", md5: " << md5 << " 已存在";ret = 5;   //已经存在的goto END;}//加锁{ScopedFileInfoLock lock(FileInfoLock::GetInstance(), 1000);if (lock.IsLocked()) {// 1、修改file_info中的count字段,+1 (count 文件引用计数)// 使用数据库提供的原子操作来增加或减少 count 字段。// 例如,使用 UPDATE file_info SET count = count + 1 WHERE file_id = ? 来增加引用计数。sprintf(sql_cmd, "update file_info set count = count+1 where md5 = '%s'", md5.c_str());if (!db_conn->ExecuteUpdate(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = 1;goto END;}} else {//超时LOG_ERROR << "FileInfoLock TryLockFor" << "超时";ret = 1;goto END;}}// 2、user_file_list插入一条数据//使用函数gettimeofday()函数来得到时间。它的精度可以达到微妙gettimeofday(&tv, NULL);ptm = localtime( &tv.tv_sec); //把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间// strftime()// 函数根据区域设置格式化本地时间/日期,函数的功能将时间格式化,或者说格式化一个时间字符串strftime(creat_time, sizeof(creat_time), "%Y-%m-%d %H:%M:%S", ptm);// sql语句/*-- =============================================== 用户文件列表-- user	文件所属用户-- md5 文件md5-- create_time 文件创建时间-- file_name 文件名字-- shared_status 共享状态, 0为没有共享, 1为共享-- pv 文件下载量,默认值为0,下载一次加1*/sprintf(sql_cmd,"insert into user_file_list(user, md5, create_time, file_name, shared_status, pv) values ('%s', '%s', '%s', '%s', %d, %d)",user_name.c_str(), md5.c_str(), creat_time, filename.c_str(), 0, 0);if (!db_conn->ExecuteCreate(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = 1;goto END;}ret = 0;END:return ret;}int ApiDealsharefile(string &url, string &post_data, string &str_json)
{// 取消分享 删除 share_file_list记录   从redis删除对应的key// 转存 user_file_list添加记录, file_info count + 1// 更新下载  share_file_list pv +1,   redis zset score  +1char cmd[20];string user_name;string md5;      //文件md5码string filename; //文件名字int ret = 0;//解析命令QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);ret = decodeDealsharefileJson(post_data, user_name, md5, filename);LOG_INFO << "cmd: " << cmd << ", user_name:" << user_name << ", md5: "<< md5 << ", filename: " << filename;if (ret != 0) {encodeDealsharefileJson(1, str_json);return 0;}if (strcmp(cmd, "cancel") == 0) //取消分享文件{ret = handleCancelShareFile(user_name, md5, filename);} else if (strcmp(cmd, "save") == 0) //转存文件{ret = handleSaveFile(user_name, md5, filename);} else if (strcmp(cmd, "pv") == 0) //文件下载标志处理{// ret = handlePvFile(md5, filename);}if (ret < 0) {encodeDealsharefileJson(1, str_json);}else {encodeDealsharefileJson(0, str_json);}return 0;
}
  • 对于转存文件来说,我们首先需要检查自己的个人文件列表里面有没有这个文件,如果这个文件存在,我们就不需要转存了,只有当这个文件不存在的时候我们才会去进行转存操作;
  • 转存文件,我们就需要在个人的文件列表中插入一条对应的文件记录,然后还需将对应的总文件列表中的引用计数进行++操作,表示此时多了一个用户进行关联。

/api/dealsharefile?cmd=pv 更新共享文件下载计数

功能介绍

当前功能就是更新对应的下载榜单的计数,共享文件下载成功后,发该url的请求,该文件下次数量+1。

请求URL

URLhttp://192.168.1.6/api/dealsharefile?cmd=pv
请求方式POST
HTTP版本1.1
Content-Typeapplication/json

请求参数

参数名含义规则说明是否必须缺省值
user用户名称不能超过32个字符必填
md5md5值md5加密后的值必填
filename文件名称不能超过128个字符必填

返回结果参数说明

名称含义规则说明
code结果值0: 成功
1: 失败

代码实现

#include "api_deal_sharefile.h"// 数据反序列化
int decodeDealsharefileJson(string &str_json, string &user_name, string &md5,string &filename) {bool res;Json::Value root;Json::Reader jsonReader;res = jsonReader.parse(str_json, root);if (!res) {LOG_ERROR << "parse reg json failed";return -1;}if (root["user"].isNull()) {LOG_ERROR << "user null";return -1;}user_name = root["user"].asString();if (root["md5"].isNull()) {LOG_ERROR << "md5 null";return -1;}md5 = root["md5"].asString();if (root["filename"].isNull()) {LOG_ERROR << "filename null";return -1;}filename = root["filename"].asString();return 0;
}// 数据序列化
int encodeDealsharefileJson(int ret, string &str_json) {Json::Value root;root["code"] = ret;Json::FastWriter writer;str_json = writer.write(root);return 0;
}// 取消共享文件
// 就是要删除对应Redis跟MySQL当中的记录
int handleCancelShareFile(string &user_name, string &md5, string &filename) {int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};char fileid[512] = {0}; //md5 + filenameCDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("ranking_list");AUTO_REL_CACHECONN(cache_manager, cache_conn);//文件标示,md5+文件名sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());// 设置分享状态 user_file_list// 共享标志设置为0sprintf(sql_cmd, "update user_file_list set shared_status = 0 where user = '%s' and md5 = '%s' and file_name = '%s'",user_name.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " << sql_cmd;;if (!db_conn->ExecuteUpdate(sql_cmd, false)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}// 删除 share_file_list 记录  deletesprintf(sql_cmd, "delete from share_file_list where user = '%s' and md5 = '%s' and file_name = '%s'",user_name.c_str(), md5.c_str(), filename.c_str());LOG_INFO << "执行: " << sql_cmd ;if (!db_conn->ExecuteDrop(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = -1;goto END;}// 从redis删除对应的keyret = cache_conn->ZsetZrem(FILE_PUBLIC_ZSET, fileid);if (ret != 0) {LOG_INFO << "执行: ZsetZrem 操作失败";goto END;}//从hash移除相应记录LOG_INFO << "Hdel FILE_NAME_HASH  " << fileid;ret = cache_conn->Hdel(FILE_NAME_HASH, fileid);if (ret < 0) {LOG_INFO << "执行: hdel 操作失败: ret = " << ret;goto END;}END:/*取消分享:成功:{"code": 0}失败:{"code": 1}*/if (ret == 0) {return (0);} else {return (1);}
}//转存文件
//返回值:0 成功,1 失败,5  文件已存在
int handleSaveFile(string &user_name, string &md5, string &filename)
{int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};//当前时间戳struct timeval tv;struct tm *ptm;char creat_time[128];int count;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);// 是否已经存在这个文件 user_file_list  md5 filename username// 查看此用户,文件名和md5是否存在,如果存在说明此文件存在sprintf(sql_cmd, "select * from user_file_list where user = '%s' and md5 = '%s' and  file_name = '%s'",user_name.c_str(), md5.c_str(), filename.c_str());// 有记录返回1,错误返回-1,无记录返回0ret = CheckwhetherHaveRecord(db_conn, sql_cmd);if (ret == 1) { //如果有结果,说明此用户已有此文件LOG_ERROR << "user_name: " << user_name << ", filename: " << filename << ", md5: " << md5 << " 已存在";ret = 5;   //已经存在的goto END;}sprintf(sql_cmd, "update file_info set count = %d where md5 = '%s'", count + 1, md5.c_str());if (!db_conn->ExecuteUpdate(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = 1;goto END;}// 2、user_file_list插入一条数据//使用函数gettimeofday()函数来得到时间。它的精度可以达到微妙gettimeofday(&tv, NULL);ptm = localtime( &tv.tv_sec); //把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间// strftime()// 函数根据区域设置格式化本地时间/日期,函数的功能将时间格式化,或者说格式化一个时间字符串strftime(creat_time, sizeof(creat_time), "%Y-%m-%d %H:%M:%S", ptm);// sql语句/*-- =============================================== 用户文件列表-- user	文件所属用户-- md5 文件md5-- create_time 文件创建时间-- file_name 文件名字-- shared_status 共享状态, 0为没有共享, 1为共享-- pv 文件下载量,默认值为0,下载一次加1*/sprintf(sql_cmd,"insert into user_file_list(user, md5, create_time, file_name, shared_status, pv) values ('%s', '%s', '%s', '%s', %d, %d)",user_name.c_str(), md5.c_str(), creat_time, filename.c_str(), 0, 0);if (!db_conn->ExecuteCreate(sql_cmd)) {LOG_ERROR << sql_cmd << " 操作失败";ret = 1;goto END;}ret = 0;END:return ret;
}//文件下载标志处理
int handlePvFile(string &md5, string &filename) {// share_file_list pv + 1;   // redis zset score + 1int ret = 0;char sql_cmd[SQL_MAX_LEN] = {0};char fileid[1024] = {0};int pv = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("ranking_list");AUTO_REL_CACHECONN(cache_manager, cache_conn);//文件标示,md5+文件名sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());// share_file_list pv + 1;// mysql的下载量+1(mysql操作)// sql语句,查看该共享文件的pv字段sprintf(sql_cmd, "select pv from share_file_list where md5 = '%s' and file_name = '%s'",md5.c_str(), filename.c_str());LOG_INFO << "执行: " << sql_cmd;;CResultSet *result_set = db_conn->ExecuteQuery(sql_cmd);if (result_set && result_set->Next()) {pv = result_set->GetInt("pv");} else {LOG_ERROR << sql_cmd << " 操作失败";ret = 1;goto END;}// 更新该文件pv字段,+1sprintf(sql_cmd, "update share_file_list set pv = %d where md5 = '%s' and file_name = '%s'",pv + 1, md5.c_str(), filename.c_str());LOG_INFO << "执行: " << sql_cmd;;if (!db_conn->ExecuteUpdate(sql_cmd, false)) {LOG_ERROR << sql_cmd << " 操作失败";ret = 1;goto END;}// zset 的member// 判断元素是否在集合中(redis操作)ret = cache_conn->ZsetExit(FILE_PUBLIC_ZSET, fileid);if (ret == 0) //不存在{   // 如果不存在,从mysql导入数据// redis集合中增加一个元素(redis操作)cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, pv + 1, fileid);// redis对应的hash也需要变化 (redis操作)// fileid ------>  filenamecache_conn->Hset(FILE_NAME_HASH, fileid, filename);ret = 0;} else if (ret == 1) {ret = cache_conn->ZsetIncr(FILE_PUBLIC_ZSET,  fileid); if (ret != 0) {ret = 1;LOG_ERROR << "ZsetIncr 操作失败";}}else //出错{ret = 1;goto END;}
END:return ret;
}int ApiDealsharefile(string &url, string &post_data, string &str_json)
{// 取消分享 删除 share_file_list记录   从redis删除对应的key// 转存 user_file_list添加记录, file_info count + 1// 更新下载  share_file_list pv +1,   redis zset score  +1char cmd[20];string user_name;string md5;      //文件md5码string filename; //文件名字int ret = 0;//解析命令QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);ret = decodeDealsharefileJson(post_data, user_name, md5, filename);LOG_INFO << "cmd: " << cmd << ", user_name:" << user_name << ", md5: "<< md5 << ", filename: " << filename;if (ret != 0) {encodeDealsharefileJson(1, str_json);return 0;}if (strcmp(cmd, "cancel") == 0) //取消分享文件{ret = handleCancelShareFile(user_name, md5, filename);} else if (strcmp(cmd, "save") == 0) //转存文件{ret = handleSaveFile(user_name, md5, filename);} else if (strcmp(cmd, "pv") == 0) //文件下载标志处理{ret = handlePvFile(md5, filename);}if (ret < 0) {encodeDealsharefileJson(1, str_json);}else {encodeDealsharefileJson(0, str_json);}return 0;
}
  • 对于更新下载的 pv 计数,我们既需要更新对应的MySQL当中的数据,也需要更新对应的Redis中的数据,首先我们需要去MySQL的user_file_list中获取对应的pv计数,然后对pv计数进行+1的操作,之后对user_file_list中的pv计数进行更新。
  • 对于Redis中的数据,首先就需要判断对应的Redis中是否存在该热点数据,如果不存在,需要先从MySQL中将对应的数据进行导入,然后更新pv计数+1,如果有,直接进行+1即可。

以上就是图床云共享存储项目服务端代码的全部实现,后续会进行测试以及优化操作。

http://www.xdnf.cn/news/1463941.html

相关文章:

  • webshell及冰蝎双击无法打开?
  • 如何将视频从 iPhone 转移到 Mac
  • 开学信息收集不再愁,这个工具太省心
  • JavaEE---7.文件操作和IO
  • The Algorithmic Foundations of Differential Privacy - 3(2)
  • Windows Server2012 R2 安装.NET Framework 3.5
  • 安科瑞基站智慧运维云平台:安全管控与节能降耗双效赋能
  • python库 Py2app 的详细使用(将 Python 脚本变为 MacOS 独立软件包)
  • MacOS 15.6 编译SDL3 Android平台多架构so库
  • 【NVIDIA AIQ】自定义函数实践
  • windows安装flash-attn记录
  • 在 Java Web 项目中优雅地实现验证码拦截与校验
  • 新闻丨重庆两江新区党工委副书记、管委会主任许宏球一行莅临华院计算考察指导
  • Java 内存模型与垃圾回收机制详解
  • 迅为RK3568开发板OpenHarmonyv3.2-Beta4版本测试-命令终端
  • AI在目前会议直播系统中应用
  • CSS 选择器的优先级/层叠性
  • watchEffect 与 watch的区别
  • 双轴倾角传感器厂家与物联网角度传感器应用全解析
  • MySQL】从零开始了解数据库开发 --- 表的操作
  • 盘点完今年CoRL最火的VLA论文,发现最强的机器人,竟是用“假数据”喂大的
  • 前端视觉交互设计全解析:从悬停高亮到多维交互体系(含代码 + 图表)
  • “我店”模式:热潮中的商机还是泡沫陷阱?深度解析当前入局可行性
  • 阿里云vs腾讯云按量付费服务器
  • 腾讯云大模型训练平台
  • BigDecimal的使用
  • 【AndroidStudio】官网下载免安装版,AndroidStudio压缩版的配置和使用
  • 华为网路设备学习-32(BGP协议 七)路由反射器与联邦
  • 中小企业数字化转型卡在哪?选对AI工具+用好企业微信,人力成本直降70%
  • SQLalachemy 错误 - Lost connection to MySQL server during query