图床项目详解-3

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 图床项目详解

3.7 分享/ 删除文件/ 更新下载数

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

8c9e48e06c7b15e42045782cddfb2d48_a125901e504949ef96b0c877f73b2857.png

具体流程是:

◼ 先判断此文件是否已经分享,判断集合有没有这个文件,如果有,说明别人已经分享此文件,中断操作(redis操作)。

◼ 如果集合没有此元素,可能因为redis中没有记录,再从mysql中查询,如果mysql也没有,说明真没有(mysql操作)

◼ 如果mysql有记录,而redis没有记录,说明redis没有保存此文件,redis保存此文件信息后,再中断操作(redis操作)

◼ 如果此文件没有被分享,mysql保存一份持久化操作(mysql操作)

◼ redis集合中增加一个元素(redis操作)

◼ redis对应的hash也需要变化 (redis操作)


    //文件标示,md5+文件名
    sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());
    if (cache_conn) {
        ret2 = cache_conn->ZsetExit(FILE_PUBLIC_ZSET, fileid);
    } else {
        ret2 = 0;
    }
    LogInfo("fileid: {}, ZsetExit: {}", fileid, ret2);
    //===1、先判断此文件是否已经分享,判断集合有没有这个文件,如果有,说明别人已经分享此文件,中断操作(redis操作)
    if (ret2 == 1) //存在
    {
        LogWarn("别人已经分享此文件");
        share_state = ShareHad;
        goto END;
    } else if (ret2 == 0) //不存在
    {
        //===2、如果集合没有此元素,可能因为redis中没有记录,再从mysql中查询,如果mysql也没有,说明真没有(mysql操作)
        //===3、如果mysql有记录,而redis没有记录,说明redis没有保存此文件,redis保存此文件信息后,再中断操作(redis操作)
        //查看此文件别人是否已经分享了
        sprintf(sql_cmd,
                "select * from share_file_list where md5 = '%s' and file_name "
                "= '%s'",
                md5.c_str(), filename.c_str());
        //返回值:1有记录
        ret2 = CheckwhetherHaveRecord(
            db_conn,
            sql_cmd); //执行sql语句, 最后一个参数为NULL
                      //,如果有则说明没有及时保持到redis,这里需要保存到redis
        if (ret2 == 1) //说明有结果,别人已经分享此文件
        {
            // redis保存此文件信息
            cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, 0, fileid);
            cache_conn->Hset(FILE_NAME_HASH, fileid, filename);
            LogWarn("别人已经分享此文件");
            share_state = ShareHad;
            goto END;
        }
    } else //出错
    {
        ret = -1;
        goto END;
    }
    //===4、如果此文件没有被分享,mysql保存一份持久化操作(mysql操作)
    // sql语句, 更新共享标志字段
    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());
    if (!db_conn->ExecuteUpdate(sql_cmd, false)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -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保存列表
    /*
        -- user 文件所属用户
        -- md5 文件md5
        -- create_time 文件共享时间
        -- file_name 文件名字
        -- pv 文件下载量,默认值为1,下载一次加1
    */
    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)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    // 共享文件数量+1
    ret = CacheIncrCount(cache_conn, FILE_PUBLIC_COUNT);
    if (ret < 0) {
        LogError("CacheIncrCount failed");
        ret = -1;
        goto END;
    }
    //===5、redis集合中增加一个元素(redis操作)
    cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, 0,
                        fileid); // 如果失败是需要撤销mysql数据库的操作的
    //===6、redis对应的hash也需要变化 (redis操作)
    //     fileid ------>  filename
    LogInfo("Hset FILE_NAME_HASH {}-{}", fileid, filename);
    ret = cache_conn->Hset(FILE_NAME_HASH, fileid, filename);
    if (ret < 0) {
        LogWarn("Hset FILE_NAME_HASH failed");
    }
    share_state = ShareOk;
END:
    return (int)share_state;

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

530544a09c5391c3b85eaae04fd9555c_d510cca3cbe4405a99addef10b58d517.png

1)先判断此文件是否已经分享

◼ 判断集合有没有这个文件,如果有,说明别人已经分享此文件(redis 操作)

◼ 如果集合没有此元素,可能因为 redis 中没有记录,再从 mysql 中查询,如果 mysql 也没有,说明真没有(mysql 操作)

    //文件标识,文件md5+文件名
    sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());
    //===1、先判断此文件是否已经分享,判断集合有没有这个文件,如果有,说明别人已经分享此文件
    ret2 = cache_conn->ZsetExit(FILE_PUBLIC_ZSET, fileid);
    LogInfo("ret2: {}", ret2);
    if (ret2 == 1) //存在
    {
        is_shared = 1;        //共享标志
        redis_has_record = 1; // redis有记录
    } else if (ret2 == 0)     //不存在
    { //===2、如果集合没有此元素,可能因为redis中没有记录,再从mysql中查询,如果mysql也没有,说明真没有(mysql操作)
        is_shared = 0;
        // 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());
        LogInfo("执行: {}", sql_cmd);
        int shared_status = 0;
        ret2 = GetResultOneStatus(db_conn, sql_cmd, shared_status); //执行sql语句
        if (ret2 == 0) {
            LogInfo("GetResultOneCount share  = {}", shared_status);
            is_shared = shar    ed_status; // 要从mysql里面获取赋值
        }
    } else //出错
    {
        ret = -1;
        goto END;
    }

2)若此文件被分享,删除分享列表(share_file_list)的数据

◼ 如果 mysql 有记录,而 redis 没有记录,那么分享文件处理只需要处理 mysql (mysql 操作)

◼ 如果 redis 有记录,mysql 和 redis 都需要处理,删除相关记录


    //说明此文件被分享,删除分享列表(share_file_list)的数据
    if (is_shared == 1) {
        //===3、如果mysql有记录,删除相关分享记录 (mysql操作)
        // 删除在共享列表的数据, 如果自己分享了这个文件,那同时从分享列表删除掉
        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());
        LogInfo("执行: {}", sql_cmd);
        if (!db_conn->ExecuteDrop(sql_cmd)) {
            LogError("{} 操作失败", sql_cmd);
            ret = -1;
            goto END;
        }
        //共享文件的数量-1
        //查询共享文件数量
        if (CacheDecrCount(cache_conn, FILE_PUBLIC_COUNT) < 0) {
            LogError("CacheDecrCount 操作失败");
            ret = -1;
            goto END;
        }
        //===4、如果redis有记录,redis需要处理,删除相关记录
        if (1 == redis_has_record) {
            //有序集合删除指定成员
            cache_conn->ZsetZrem(FILE_PUBLIC_ZSET, fileid);
            //从hash移除相应记录
            cache_conn->Hdel(FILE_NAME_HASH, fileid);
        }
    }

3)删除用户文件列表的数据,并使用户文件数量-1

    //用户文件数量-1
    if (CacheDecrCount(cache_conn, FILE_USER_COUNT + user) < 0) {
        LogError("CacheDecrCount 操作失败");
        ret = -1;
        goto END;
    }
    //删除用户文件列表数据
    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());
    LogInfo("执行: {}", sql_cmd);
    if (!db_conn->ExecuteDrop(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }

4)文件信息表(file_info)的文件引用计数count,减去1。如果count=0,说明没有用户引用此文件,需要在storage删除此文件


    //查看该文件文件引用计数
    sprintf(sql_cmd, "select count from file_info where md5 = '%s'",
            md5.c_str());
    LogInfo("执行: {}", sql_cmd);
    count = 0;
    ret2 = GetResultOneCount(db_conn, sql_cmd, count); //执行sql语句
    LogInfo("ret2: {}, count: {}", ret2, count);
    if (ret2 != 0) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    if (count > 0) {
        count -= 1;
        sprintf(sql_cmd, "update file_info set count=%d where md5 = '%s'",
                count, md5.c_str());
        LogInfo("执行: {}", sql_cmd);
        if (!db_conn->ExecuteUpdate(sql_cmd)) {
            LogError("{} 操作失败", sql_cmd);
            ret = -1;
            goto END;
        }
    }
    if (count == 0) //说明没有用户引用此文件,需要在storage删除此文件
    {
        //查询文件的id
        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)) {
            LogWarn("{} 操作失败", sql_cmd);
        }
        //从storage服务器删除此文件,参数为为文件id
        ret2 = RemoveFileFromFastDfs(fileid.c_str());
        if (ret2 != 0) {
            LogInfo("RemoveFileFromFastDfs err: {}", ret2);
            ret = -1;
            goto END;
        }
    }
    ret = 0;

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

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

103ff76cb3d469cba331c885ac46e1a6_fd81405631ea4d4883a397b20d229040.png


    // 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());
    LogInfo("执行: {}", sql_cmd);
    CResultSet *result_set = db_conn->ExecuteQuery(sql_cmd);
    if (result_set && result_set->Next()) {
        pv = result_set->GetInt("pv");
    } else {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    //更新该文件pv字段,+1
    sprintf(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());
    LogInfo("执行: {}", sql_cmd);
    if (!db_conn->ExecuteUpdate(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }

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

1、/dealsharefile?cmd=cancel 取消分享


86cb383212665df759c38444065196f3_d0884e170cdf4fe7b716a7ace43ac937.png

   //文件标示,md5+文件名
    sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());
    // 1、共享标志设置为0
    sprintf(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());
    LogInfo("执行: {}", sql_cmd);
    if (!db_conn->ExecuteUpdate(sql_cmd, false)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    // 2、共享文件数量-1
    ret2 = CacheDecrCount(cache_conn, FILE_PUBLIC_COUNT);
    if (ret2 < 0) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    //3、删除在共享列表的数据
    sprintf(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());
    LogInfo("执行: {}, ret = {}", sql_cmd, ret);
    if (!db_conn->ExecuteDrop(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    //4、redis记录操作
    //4.1 有序集合删除指定成员
    ret = cache_conn->ZsetZrem(FILE_PUBLIC_ZSET, fileid);
    if (ret != 0) {
        LogInfo("执行: ZsetZrem 操作失败");
        goto END;
    }
    //4.2 从hash移除相应记录
    LogInfo("Hdel FILE_NAME_HASH  {}", fileid);
    ret = cache_conn->Hdel(FILE_NAME_HASH, fileid);
    if (ret < 0) {
        LogInfo("执行: hdel 操作失败: ret = {}", ret);
        goto END;
    }

2、/api/dealsharefile?cmd=e save 转存文件

◼ 先查询是个人文件列表是否已经存在该文件。

◼ 增加 file_info表的 count 计数,表示多一个人保存了该文件。

◼ 个人的 user_file_list 增加一条文件记录

◼ 更新个人的 user_file_count

当我们转存该文件后,即使分享者删除自己的文件,不会影响到我们自己转存的文件。

0cea88f13db703fb89def09d9ae84a56_6be37ff3ece24c8dbb60bdee7aa0515b.png


    //查看此用户,文件名和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());
    ret2 = CheckwhetherHaveRecord(db_conn, sql_cmd); // 有记录返回1,错误返回-1,无记录返回0
    if (ret2 == 1) { //如果有结果,说明此用户已有此文件
        LogError("user_name: {}, filename: {}, md5: {} 已存在", user_name, filename, md5);
        ret = -2; //返回-2错误码
        goto END;
    }
    if (ret2 < 0) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1; //返回-1错误码
        goto END;
    }
    //文件信息表,查找该文件的计数器
    sprintf(sql_cmd, "select count from file_info where md5 = '%s'", md5.c_str());
    count = 0;
    ret2 = GetResultOneCount(db_conn, sql_cmd, count); //执行sql语句
    if (ret2 != 0) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    // 1、修改file_info中的count字段,+1 (count 文件引用计数)
    sprintf(sql_cmd, "update file_info set count = %d where md5 = '%s'",
            count + 1, md5.c_str());
    if (!db_conn->ExecuteUpdate(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    // 2、user_file_list插入一条数据
    gettimeofday(&tv, NULL);
    ptm = localtime(
        &tv.tv_sec); //把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间
    // strftime()
    // 函数根据区域设置格式化本地时间/日期,函数的功能将时间格式化,或者说格式化一个时间字符串
    strftime(time_str, sizeof(time_str), "%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(), time_str, filename.c_str(), 0, 0);
    if (!db_conn->ExecuteCreate(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    // 3、查询用户文件数量,更新该字段数量+1
    if (CacheIncrCount(cache_conn, FILE_USER_COUNT + user_name) < 0) {
        LogError("CacheIncrCount 操作失败");
        ret = -1;
        goto END;
    }

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

7c74f4a72c02152583271b48825767b7_e783e38b9bb442ecbf4d7017b50d3062.png

更新 share_file_list 的 pv 值

更新 redis 里的 FILE_PUBLIC_ZSET,用作排行榜


    //文件标示,md5+文件名
    sprintf(fileid, "%s%s", md5.c_str(), filename.c_str());
    //===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());
    LogInfo("执行: {}", sql_cmd);
    CResultSet *result_set = db_conn->ExecuteQuery(sql_cmd);
    if (result_set && result_set->Next()) {
        pv = result_set->GetInt("pv");
    } else {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    //更新该文件pv字段,+1
    sprintf(sql_cmd,
            "update share_file_list set pv = %d where md5 = '%s' and file_name "
            "= '%s'",
            pv + 1, md5.c_str(), filename.c_str());
    LogInfo("执行: {}", sql_cmd);
    if (!db_conn->ExecuteUpdate(sql_cmd, false)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    //===2、判断元素是否在集合中(redis操作)
    ret2 = cache_conn->ZsetExit(FILE_PUBLIC_ZSET, fileid);
    if (ret2 == 1) //===3、如果存在,有序集合score+1
    {              
        ret = cache_conn->ZsetIncr(
            FILE_PUBLIC_ZSET,
            fileid); // zrange FILE_PUBLIC_ZSET  0 -1 withscores 查看
        if (ret != 0) {
            LogError("ZsetIncr 操作失败");
        }
    } else if (ret2 == 0) //===4、如果不存在,从mysql导入数据
    {                     
        //===5、redis集合中增加一个元素(redis操作)
        cache_conn->ZsetAdd(FILE_PUBLIC_ZSET, pv + 1, fileid);
        //===6、redis对应的hash也需要变化 (redis操作)
        //     fileid ------>  filename
        cache_conn->Hset(FILE_NAME_HASH, fileid, filename);
    } else //出错
    {
        ret = -1;
        goto END;
    }

3.9 图床分享图片

1、/api/sharepic?cmd=share 请求图片分享

前端:

1)访问链接:http://xxx.xxx.xxx.xxx/602fdf30db2aacf517badf456512123,该

访问链接由 web 服务器提供。

2)访问链接的 web 向 后台服务器请求图片下载地址

请求接口 http://xxx.xxx.xxx.xxx/api/sharepic?cmd=browse

请求格式
{
“urlmd5”: “602fdf30db2aacf517badf4565121234” // 来自请求链接
}
返回格式
{
“code”: 0,
“url”: “http://xxx.xxx.xxx.xxx/602fdf30db2aacf517badf4565121234”, // 图片的下载地址
“user”: “qingfu”, // 分享者用户名
“pv”: 1, // 浏览次数
“time”: “2021-12-12 11:23:0” // 分享时间
}

web 页面获取到 url 后下载图片显示,并显示分享者用户名和分享时间。

335611e86e3840fb5865d8dd1f826e49_973738177ed94528acb6717e74e94caf.png


    // 1. 生成urlmd5
    string urlmd5;
    urlmd5 = RandomString(32); // 这里我们先简单的,直接使用随机数代替 MD5的使用
    LogInfo("urlmd5: {}", urlmd5);
  // 2. 插入share_picture_list,即添加图片分享记录
    time_t now;
    //获取当前时间
    now = time(NULL);
    strftime(create_time, TIME_STRING_LEN - 1, "%Y-%m-%d %H:%M:%S",
             localtime(&now));
    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);
        ret = -1;
        goto END;
    }
    //3、文件信息表,获取该共享图片的数量
    sprintf(sql_cmd, "select count from file_info where md5 = '%s'", filemd5);
    count = 0;
    ret = GetResultOneCount(db_conn, sql_cmd, count); //执行sql语句
    if (ret != 0) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    // 4、修改file_info中的count字段,即该共享图片的数量+1 (count 文件引用计数)
    sprintf(sql_cmd, "update file_info set count = %d where md5 = '%s'",
            count + 1, filemd5);
    if (!db_conn->ExecuteUpdate(sql_cmd)) {
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        goto END;
    }
    // 5、 增加分享图片计数  SHARE_PIC_COUNTdarren
    if (CacheIncrCount(cache_conn, SHARE_PIC_COUNT + string(user)) < 0) {
        LogError(" CacheIncrCount 操作失败");
    }

2、/api/sharepic?cmd=browse 请求浏览图片

请求接口 http://xxx.xxx.xxx.xxx/api/sharepic?cmd=browse,主要用来返回具体的图片下载地址。

110a8f59b6d57343e609af852f152a96_9e4f88ba5dc145a5bfe40831c2c02754.png


   // 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;
    }
    // 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;
    }
    // 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;
    }


3、/api/sharepic?cmd=normal 我的图片分享


376ddfbc7ea6c4d8561411f0562785f8_dfd2f5b0d8594e4892f4eb3e201b0061.png

4、/api/sharepic?cmd=cance 取消图片分享

65103989e1b92bfc75088b9d865a8798_2b907733ae1b40ccb50f71e44644868b.png

需要注意的是,如果文件引用次数(分享次数)为0,说明没人引用了,需要从文件信息表和fastdfs的storage删除。


    // 获取文件md5
    LogInfo("urlmd5: {}", urlmd5);
    // 1. 查看是否有分享记录,先从分享图片列表查询到文件信息
    sprintf(sql_cmd, "select filemd5 from share_picture_list where urlmd5 = '%s'", urlmd5);
    LogDebug("执行: {}", sql_cmd);
    result_set = db_conn->ExecuteQuery(sql_cmd);
    if (result_set && result_set->Next()) {
        filemd5 = result_set->GetString("filemd5");
        delete result_set;
    } else {
        if (result_set)
            delete result_set;
        ret = -1;
        goto END;
    }
    //2、查询文件信息表(file_info)的文件引用计数count
    sprintf(sql_cmd,
            "select count, file_id from file_info where md5 = '%s' for update",
            filemd5.c_str()); //
    LogInfo("执行: {}", sql_cmd);
    result_set = db_conn->ExecuteQuery(sql_cmd);
    if (result_set && result_set->Next()) {
        fileid = result_set->GetString("file_id");
        count = result_set->GetInt("count");
        delete result_set;
    } else {
        if (result_set)
            delete result_set;
        LogError("{} 操作失败", sql_cmd);
        ret = -1;
        // db_conn->Rollback();
        goto END;
    }
    // 3. 更新文件信息表的文件引用数count - 1
    if (count > 0) {
        count -= 1;
        sprintf(sql_cmd, "update file_info set count=%d where md5 = '%s'",
                count, filemd5.c_str());
        LogInfo("执行: {}", sql_cmd);
        if (!db_conn->ExecuteUpdate(sql_cmd)) {
            LogError("{} 操作失败", sql_cmd);
            ret = -1;
            // db_conn->Rollback();
            goto END;
        }
    }
    //4、删除在共享图片列表的数据
    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);
        ret = -1;
        // db_conn->Rollback();
        goto END;
    }
    // 5、若count=0,说明没有用户引用此文件,需要在文件信息表和storage删除此文件
    if (count == 0)
    {
        //删除文件信息表中该文件的信息
        sprintf(sql_cmd, "delete from file_info where md5 = '%s'",
                filemd5.c_str());
        LogInfo("执行: {}", sql_cmd);
        if (!db_conn->ExecuteDrop(sql_cmd)) {
            LogWarn("{} 操作失败", sql_cmd);
            ret = -1;
            goto END;
        }
        LogWarn("RemoveFileFromFastDfs");
        //从storage服务器删除此文件,参数为为文件id
        ret2 = RemoveFileFromFastDfs(fileid.c_str());
        if (ret2 != 0) {
            LogError("RemoveFileFromFastDfs err: {}", ret2);
            ret = -1;
            goto END;
        }
    }
    // 6、共享图片数量-1
    if (CacheDecrCount(cache_conn, SHARE_PIC_COUNT + string(user)) < 0) {
        LogError("CacheDecrCount failed"); // 即使失败 也可以下次从mysql加载计数
    }
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
7月前
|
存储 JavaScript
【开源图床】使用Typora+PicGo+Gitee搭建个人博客图床
【开源图床】使用Typora+PicGo+Gitee搭建个人博客图床
97 2
|
7月前
|
存储 JavaScript 网络架构
【开源图床】使用Typora+PicGo+Github+CDN搭建个人博客图床
【开源图床】使用Typora+PicGo+Github+CDN搭建个人博客图床
296 3
|
域名解析 应用服务中间件 网络安全
|
6月前
|
Linux
Typore+PicGo+GitHub图床搭建
Typore+PicGo+GitHub图床搭建
36 1
|
开发者
picgo+GitHub搭建图床
picgo+GitHub搭建图床
126 0
|
7月前
|
存储 定位技术 Windows
GitHub与PicGo搭建免费稳定图床并实现Typora内复制自动上传
GitHub与PicGo搭建免费稳定图床并实现Typora内复制自动上传
100 1
|
NoSQL 关系型数据库 MySQL
图床项目详解-2
图床项目详解
108 0
|
NoSQL 关系型数据库 MySQL
图床项目详解-1
图床项目详解
147 0
|
前端开发 Java 程序员
搭建一个属于自己的图床
搭建一个属于自己的图床
搭建一个属于自己的图床
|
对象存储 开发者
如何管理属于自己的图床?
作为开发者的你,平时肯定会有很多自己的笔记,记录着许多工作问题、学习记录等等。近年来有很多支持在线编辑的平台,例如wolai、语雀等等,它们或多或少的都支持在线的markdown的编辑,也支持导入导出等丰富功能。但是对笔者来说,这些平台虽然功能繁多,但因个人习惯不同,笔者还是习惯了Typora的简约的风格,也便于持久化存放、不会因为网络等问题访问不了笔记等原因。在实际使用的过程中,会遇到在笔记中图片上传的问题,在发布到其他平台的时候由于都是本地图片,还需要上传一次,这个时候图床就派上用场了。
387 0
如何管理属于自己的图床?