自学内容网 自学内容网

C/C++精品项目之图床共享云存储(6):图片的共享,浏览,获取,以及短链的生成

目录

一:图片的共享操作

二:图片的浏览

三:查看自己共享的所有文件

四:取消共享

五:短链服务

1:URL短链

2:URL 重定向

3:长链变短链

4:查找长链        


C/C++精品项目之图床共享云存储(1):基础组件-CSDN博客

C/C++精品项目之图床共享云存储(2):MySql连接池_mysql 连接池c++ ping-CSDN博客

C/C++精品项目之图床共享云存储(3):网络缓冲区类和main-CSDN博客

C/C++精品项目之图床共享云存储(4):注册,登录,token,排序文件-CSDN博客

C/C++精品项目之图床共享云存储(5):FastDFS存储原理,文件的秒传-CSDN博客

一:图片的共享操作

        我们将图片或文件进行上传之后,我们现阶段只能通过本用户查看本用户的文件,还不能共享,现在我们加入共享的操作,让我们共享一个urlmd5后,将这个urlmd5分享给别人之后,别人就可以查看了。

        首先就是我们将要共享图片的md5等,传入这个函数,我们连接数据库之后,我们随机生成一个32位的字符串,或者使用uuid,生成一个urlmd5,我们将文件的md5和生成的urlmd5,共同插入到共享文件的数据库中,代表我们这个文件共享了,其他用户可以在共享表中看到别人的共享文件了。

        对于为什么生成这个urlmd5,我们下面讲浏览文件的时候,就可以看到了。

//分享图片,就是将我们要分享的图片,生成一个urlmd5,然后存储到数据库中,他们想浏览这个图片,就可以通过使用这个 urlmd5 \
如果能找到这个urlmd5对应的md5值,那么就是分享出来了,我们也就可以看到了。这里只是将图片进行分享了,放到共享的图片列表中。
void handleSharePicture(const char *user, const char *filemd5,
                       const char *file_name, string &str_json) {
    // 数据库操作
    // 获取数据库连接
    CDBManager *db_manager = CDBManager::getInstance();
    CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");
    AUTO_REL_DBCONN(db_manager, db_conn);

    //操作数据库
    string urlmd5 = RandomString(32);  // uuid生成唯一值,再转成base64
    //分享时间
    //获取当前时间
    time_t now = time(NULL);
    char create_time[TIME_STRING_LEN];
    strftime(create_time, TIME_STRING_LEN - 1, "%Y-%m-%d %H:%M:%S", localtime(&now));

    LogInfo("urlmd5:{}", urlmd5);
    char sql_cmd[SQL_MAX_LEN] = {0};
    char key[5] = {0};
    sprintf(sql_cmd,
            "insert into share_picture_list (user, filemd5, file_name, urlmd5, "
            "`key`, pv, create_time) values ('%s', '%s', '%s', '%s', '%s', %d, "
            "'%s')",   user, filemd5, file_name, urlmd5.c_str(), key, 0, create_time);
    LogInfo("执行: {}", sql_cmd);
    if (!db_conn->ExecuteCreate(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        encodeSharePictureJson(HTTP_RESP_FAIL, urlmd5, str_json);
    } else {
        encodeSharePictureJson(HTTP_RESP_OK, urlmd5, str_json);
    }        
}

二:图片的浏览

        我们给文件放入到共享的数据库中,这个文件就会有一个文件的md5,和一个urlmd5,这俩在数据库中是对应的,当我们访问程序的浏览页面的时候,我们传入urlmd5,我们就会进入到数据库查找到源md5,有了源md5后,我们在查询info表,找到这个文件的全部信息,包括完整的下载链接。

//我们这个浏览共享图片的函数,就是从我们分享的一个urlmd5,通过这个在分享列表中进行查找,它对应的文件md5,然后再去file_info表中查找对应的url地址。 \
这样就可以观看具体的图片了。
void handleBrowsePicture(const char *urlmd5, string &str_json) {
    int ret = 0;
    char sql_cmd[SQL_MAX_LEN] = {0};
    string picture_url;
    string file_name;
    string user;
    string filemd5;
    string create_time;
    int pv = 0;

    //查询 share_picture_list, file_info的url
    CDBManager *db_manager = CDBManager::getInstance();
    CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");
    AUTO_REL_DBCONN(db_manager, db_conn);
    CResultSet *result_set = NULL;
     LogInfo("urlmd5: {}", urlmd5);
    // 1. 先从分享图片列表查询到文件信息
    sprintf(sql_cmd,
            "select user, filemd5, file_name, pv, create_time from "
            "share_picture_list where urlmd5 = '%s'", urlmd5);
    LogDebug("执行: {}", sql_cmd);
    result_set = db_conn->ExecuteQuery(sql_cmd);
    if (result_set && result_set->Next()) {
        user = result_set->GetString("user");
        filemd5 = result_set->GetString("filemd5");
        file_name = result_set->GetString("file_name");
        pv = result_set->GetInt("pv");
        create_time = result_set->GetString("create_time");
        delete result_set;
    } else {
        if (result_set)
            delete result_set;
        ret = -1;
        goto END;
    }     
    //查询file_info url
    // 2. 通过文件的MD5查找对应的url地址
    sprintf(sql_cmd, "select url from file_info where md5 ='%s'",
            filemd5.c_str());
    LogInfo("执行: {}", sql_cmd);
    result_set = db_conn->ExecuteQuery(sql_cmd);
    if (result_set && result_set->Next()) {
        picture_url = result_set->GetString("url");
        delete result_set;
    } else {
        if (result_set)
            delete result_set;
        ret = -1;
        goto END;
    }
    LogInfo("url: {}", picture_url);  

    // 3. 更新浏览次数, 可以考虑保存到redis,减少数据库查询的压力 修改、创建是比较花时间
    pv += 1; //浏览计数增加
    sprintf(sql_cmd,
            "update share_picture_list set pv = %d where urlmd5 = '%s'", pv,
            urlmd5);
    LogDebug("执行: {}", sql_cmd);
    if (!db_conn->ExecuteUpdate(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    ret = 0;
END:
     // 4. 返回urlmd5 和提取码key
    if (ret == 0) {
        encodeBrowselPictureJson(HTTP_RESP_OK, pv, picture_url, user,
                                 create_time, str_json);
    } else {
        encodeBrowselPictureJson(HTTP_RESP_FAIL, pv, picture_url, user,
                                 create_time, str_json);
    }
}

三:查看自己共享的所有文件

        既然是查看自己的全部文件,我们肯定是含有本用户的全部信息,我们将本用户和和共享列表中的用户进行匹配,将我们匹配成功的文件全部返回回来,当然我们匹配的时候,还要跟总文件表进行比较,万一我们总文件表中不存在,那就不对了。

//获取共享文件列表
//获取用户文件信息 127.0.0.1:80/sharepicture&cmd=normal
// 查看我的图片分享也就是将两个数据库的所有数据进行比较,这个分享的列表中与info中进行对比,相同的md5,以及当前用户下,这样就可以找到分享的图片了。
void handleGetSharePicturesList(const char *user, int start, int count,
                                string &str_json) { 
//查询share_picture_list
//查询url  file_info
    int ret = 0;
    char sql_cmd[SQL_MAX_LEN] = {0};
    CResultSet *result_set = NULL;
    CDBManager *db_manager = CDBManager::getInstance();
    CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");
    AUTO_REL_DBCONN(db_manager, db_conn);
    Json::Value root;
    
    int file_count = 0;
    //获取我的图片总分享数量
    int total = 0;
    string temp_user = user;
    ret = getSharePicturesCount(db_conn, temp_user, total);
    if (ret < 0) {
        LogError("getSharePicturesCount failed");
        ret = -1;
        goto END;
    }
    if (total == 0) {
        LogInfo("getSharePicturesCount count = 0");
        ret = 0;
        goto END;
    }
    //有记录 继续查询读取数据
    // sql语句  不需要查询 文件资源的http url
    sprintf(
        sql_cmd,
        "select share_picture_list.user, share_picture_list.filemd5, share_picture_list.file_name,share_picture_list.urlmd5, share_picture_list.pv, \
        share_picture_list.create_time, file_info.size from file_info, share_picture_list where share_picture_list.user = '%s' and  \
        file_info.md5 = share_picture_list.filemd5 limit %d, %d", user, start, count);

        LogInfo("执行: {}", sql_cmd);
    result_set = db_conn->ExecuteQuery(sql_cmd);
    if (result_set) {
        // 遍历所有的内容
        // 获取大小
        Json::Value files;
        while (result_set->Next()) {
            Json::Value file;
            file["user"] = result_set->GetString("user");
            file["filemd5"] = result_set->GetString("filemd5");
            file["file_name"] = result_set->GetString("file_name");
            file["urlmd5"] = result_set->GetString("urlmd5");
            file["pv"] = result_set->GetInt("pv");
            file["create_time"] = result_set->GetString("create_time");
            file["size"] = result_set->GetInt("size");
            files[file_count] = file;
            file_count++;
        }
        if (file_count > 0)
            root["files"] = files;

        ret = 0;
        delete result_set;
    } else {
        ret = -1;
    }     
END:
    //json最后的序列化
    if(ret != 0) {
       root["code"] = HTTP_RESP_FAIL;
    } else {
        root["code"] = 0;
        root["count"] = file_count; // 10
        root["total"] = total;  //20 
    }
    str_json = root.toStyledString();
    LogInfo("str_json: {}",str_json);
}

四:取消共享

        取消共享是最简单的,因为我们只需要将共享表中本用户的全部信息直接进行删除即可。

//取消分享文件,取消图片分享直接将分享中的图片进行删除即可。
void handleCancelSharePicture(const char *user, const char *urlmd5,
                              string &str_json) {
    char sql_cmd[SQL_MAX_LEN] = {0};
   
    CDBManager *db_manager = CDBManager::getInstance();
    CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");
    AUTO_REL_DBCONN(db_manager, db_conn);
    LogWarn("into");
 
    // 获取文件md5
    LogInfo("urlmd5: {}", urlmd5);
    //删除在共享图片列表的数据
    sprintf(sql_cmd, "delete from share_picture_list where user = '%s' and urlmd5 = '%s'",  user, urlmd5);
    LogInfo("执行: {}", sql_cmd);
    if (!db_conn->ExecutePassQuery(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        encodeCancelPictureJson(HTTP_RESP_FAIL, str_json);
    } else {
        encodeCancelPictureJson(HTTP_RESP_OK, str_json);
    }
}

五:短链服务

1:URL短链

也就是短网址,是将原本较长的网址转换成较短形式的服务。短链的意义主要体现在以下几个方面:

1. 便于分享和传播:在社交媒体、短信等平台上,短链占用的空间小,可以让用户在有限的字符限制内分享更多内容,同时也更美观。
2. 数据统计:短链服务通常提供数据统计功能,可以追踪每个链接的点击量,帮助网站或内容创作者了解链接的传播效果。

3. 降低成本:在短信营销中,长链接可能会使短信内容超过限制长度,导致需要发送多条短信。使用短链可以减少短信数量,从而节省成本。
4. 匿名访问:短链可以隐藏用户的访问来源,通过HTTP的Refer字段无法获取跳转前的来源,为用户提供一定程度的匿名性。
5. 访问控制:短链可以设置密码访问,限定访问次数、时段或黑白名单,为内容的访问提供更多控制。
6. 简化二维码:长链接生成的二维码可能过于复杂,影响识别成功率。短链可以简化二维码的图案,提高扫描识别率。
7. 降低权重传递:在搜索引擎优化中,长链接可能会传递权重给其他网站。使用短链可以避免这种情况,保持网站权重不外泄。
8. 适应限制:某些平台对链接长度有限制,如早期的微博字数限制,使用短链可以适应这些限制,避免内容被截断。

短链服务通过将长链接映射到短链接,使得用户在分享和传播链接时更加方便,同时也为网站运营者提供了更多的管理和分析工具。

2:URL 重定向

        URL 重定向,也称为 URL 转发,是一种当实际资源,如单个页面、表单或者整个 Web 应用被迁移到新的URL 下的时候,保持(原有)链接可用的技术。HTTP 协议提供了一种特殊形式的响应—— HTTP 重定向(HTTP redirects)来执行此类操作。
        简单理解,重定向是指一个Web资源接收到客户端请求后,这个Web资源通知客户端去访问另外一个Web资源,客服端一共会发送2次Http请求。大概的流程如下:

        简单来说,就是类似于浏览的urlmd5,我们将长链变成短链,然后将他们存储到一个单独的数据库中,当我们用短链来访问的时候,我们就会从这个数据库中查找长链,然后返回长链给客户端,我们客户端再根据这个长链访问具体的信息。下面我们就讲讲具体的细节。

3:长链变短链

        我们这个代码操作就在upload中,我们将文件上传之后,我们不是在数据库中存储了长链吗,我们这里还要再加一步,我们这里将这个长链转变成短链后也存储到数据库中,当然这里涉及到了grpc的远程调用以及短链的tobase62。

        我们在upload中添加一个grpc的类,我们通过这个类远程连接另一个服务器,这个服务器就是专门进行转短链以及查找长链的服务器。这个服务器中最重要的就是 ToBase62 。我们通过传入来的id,将这个id进行base62转换成字符串,我们将这个转换后的字符串保存起来,我们在内部进行拼接成完整的url。下面的grpc类,只是用来连接另一个服务器用的。

class ShortUrlClient {
 public:
  ShortUrlClient(std::shared_ptr<Channel> channel)
      : stub_(ShortUrl::NewStub(channel)) {}

  
  int GetShortUrl(const std::string& url, const bool is_public, std::string &short_key) {
    // Data we are sending to the server.
    Url request;
    request.set_url(url);
    request.set_ispublic(is_public);
 
    // Container for the data we expect from the server.
    Url reply;

    // Context for the client. It could be used to convey extra information to
    // the server and/or tweak certain RPC behaviors.
    ClientContext context;
    string meta_key = "authorization";  // 自定义,目前和shorurl-server是写的固定key
    context.AddMetadata(meta_key, s_shorturl_server_access_token);
    // The actual RPC.
    Status status = stub_->GetShortUrl(&context, request, &reply);

    // Act upon its status.
    if (status.ok()) {
        short_key = reply.url();   
        return 0;
    } else {
        std::cout << status.error_code() << ": " << status.error_message() << std::endl;
        return -1;
    }
    return 0;
  }

 private:
  std::unique_ptr<ShortUrl::Stub> stub_;
};

 这里的代码是用GO写的,他具体是进行base62的转换。

// 字母的顺序可以适当调整,增加安全性。这里是62字符
const chars = "klmOPQRnopqrs012wxyz34FGHIJK5tuABCDEv67TUVW89abhijLMNScdefgXYZ"

// 把自增id转成62进制字符串
func ToBase62(num int64) string {
result := ""
for num > 0 {
result = string(chars[num%62]) + result
num /= 62
}
return result
}

下面的代码是我们在upload中添加的代码,我们要在这里转成短链并存储起来。

int originUrl2ShortUrl(const string &origin_url, string &short_url)
{
    // sksSdkFdDngKie8n05nr9jey84prEhw5u43th0yi294780yjr3h7
    ShortUrlClient client(grpc::CreateChannel(s_shorturl_server_address, grpc::InsecureChannelCredentials()));

    int ret = client.GetShortUrl(origin_url, true, short_url);
    LogInfo("origin_url = {}", origin_url);
    if(ret == 0) {
        LogInfo("short_url = {}", short_url);
    }
    else {
        LogError("get short_url failed");
    }
    return ret;
}

4:查找长链        

        这样通过上面的协作后,我们成功将长链变成短链并存储在数据库中,我们再捋一遍,我们将文件上传之后,我们不仅将长链进行存储,也将短链进行存储了,那么我们怎么通过短链进行浏览呢?

        我们在nginx中,我们设置了跳转的功能:http://127.0.0.1:80/p/1  。 我们找到p之后,就跳转到我们另一个服务器中,也就是那个转短链,以及查长链的服务器,我们将短链发送过去之后,我们会进行查找长链的操作,然后返回长链,然后再通过长链进行具体的浏览。也就是上面所说到的url重定向。

# 短链代理,对应tuchuang/shorturl/shorturl-proxy服务
        location /p/{
                proxy_pass http://127.0.0.1:8082;
        }

说这么多,我们需要一个完整的图来描述一下,当然也给出我们 grpc 写的 protobuf 的文件格式。

syntax = "proto3";
option go_package = "shorturl/proto";
package shorturl_voice;
// 消息定义
message Url {
  string url = 1;
  bool isPublic = 2;
}
message ShortKey {
  string key = 1;
  bool isPublic =2;
}
// 服务定义
service ShortUrl {
  rpc GetShortUrl(Url)returns(Url){}
  rpc GetOriginalUrl(ShortKey)returns(Url){}
}

这一篇把这个图能理清楚就完全理解了。0voice · GitHub


原文地址:https://blog.csdn.net/2301_76446998/article/details/143874167

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!