LINUX 入门 5.2

发布于:2024-05-08 ⋅ 阅读:(28) ⋅ 点赞:(0)

LINUX 入门 5

day8 20240503 耗时:240min

day9 20240504 耗时:80min

课程链接地址

第5章 数据库mysql项目实战

4步——读图read到buffer里,写到sql, 读image,从buffer存入磁盘

  1. goto exit;

    C++ 不支持 goto 的标准化使用,虽然它确实在语言中存在。一般来说,使用 goto 被认为是不良的实践,因为它可以导致难以维护的代码和潜在的错误。

    然而,如果你确实需要在 C++ 中使用 goto,这通常是在非常特殊的情况下,例如跳出多层循环或用于错误处理。

  2. #define FILE_IMAGE_LENGTH (64*1024)

    在 C 和 C++ 中,#define 指令用于创建宏。括号用于将表达式括起来,这样做的目的是防止在宏的展开过程中出现不符合预期的运算优先级问题。

    在你给出的示例中,FILE_IMAGE_LENGTH 被定义为 (64*1024),这是一个表达式,表示 64 乘以 1024,结果为 65536。括号确保了在宏展开时,乘法操作首先进行,然后再赋值给 FILE_IMAGE_LENGTH

mysql workbench闪退

MySQL Workbench闪退的解决方法 - 知乎 (zhihu.com)

C:\Users\mephistopheles\AppData\Roaming\MySQL里workbench删掉 但是配置都要重新弄了

文件操作:

  1. FILE *fp = fopen(filename, "rb");

  2. fseek(fp, 0, SEEK_END); //fopen文件指针fp是默认指向文件开始,现在用fseek放fp到最后

    fseek(fp, 0, SEEK_SET);

  3. int length = ftell(fp);

    1. fseek(fp, 0, SEEK_END);:这行代码将文件指针 fp 移动到文件的末尾。fseek 函数用于移动文件指针到指定位置,第一个参数是文件指针,第二个参数是偏移量(以字节为单位),第三个参数指定起始位置(SEEK_END 表示从文件末尾开始计算偏移量)。
    2. int length = ftell(fp);ftell 函数用于获取当前文件指针的位置,即当前文件的大小。在上一步将文件指针移到文件末尾后,ftell 返回的值就是文件的大小。这个大小以字节为单位。
    3. fseek(fp, 0, SEEK_SET);:这行代码将文件指针 fp 移回文件的开始处。这样做是为了确保文件指针回到了文件的开头,以便后续的文件操作。
  4. int size = fread(buffer, 1, length, fp);

  5. fclose(fp);

7 图片存储read image

读图到buffer,并从buffer到磁盘

// 读图片并read
// filename : path + file name
// buffer : store image data
int read_image(char *filename, char *buffer){
    if(filename == NULL || buffer == NULL) return -1;

    FILE *fp = fopen(filename, "rb"); //二进制格式b
    if(fp ==NULL) {
        printf("fopen failed \n");
        return -2;
    }

    fseek(fp, 0, SEEK_END); //fopen文件指针fp是默认指向文件开始,现在用fseek放fp到最后
    int length = ftell(fp);  // filesize
    fseek(fp, 0, SEEK_SET);

    int size = fread(buffer, 1, length, fp);
    if(size != length) {
        printf("fread failed: %d \n", size);
        return -3;
    }

    fclose(fp);
    return size;
}

// 4写入磁盘
int read_image(char *filename, char *buffer, int length){
    if(filename == NULL || buffer == NULL || length <= 0) return -1;

    FILE *fp = fopen(filename, "wb+");//没有就创建
    if(fp ==NULL) {
        printf("fopen failed \n");
        return -2;
    }

    int size = fwrite(buffer,1, length, fp); //写到buffer里
    if(size != length) {
        printf("fwrite failed: %d\n",size);
        return -3;
    }
    fclose(fp);
    return size;
}

8 图片存储 mysql_write

先改表

ALTER TABLE TBL_USER ADD U_IMG BLOB;

在 TBL_USER 表中添加一个名为 U_IMG 的 BLOB 列。

BLO(Binary Large Object)是一种数据库中用于存储大量二进制数据的数据类型。它通常用于存储图像、音频、视频等多媒体数据,以及其他任何不适合使用常规文本数据类型存储的数据。 BLOB 数据类型允许存储各种类型的二进制数据,并且可以具有非常大的大小。

MYSQL *handle: 类似发射管道,地表到卫星之间的那一段

statement:类似卫星发射中心 储物间备用的

#define SQL_INSERT_IMG_USER "INSERT TBL_USER(U_NAME, U_GENDER, U_IMG) VALUES('King', 'man', ?);"   ?是不确定的占位符!!


int mysql_write(MYSQL *handle, char *buffer, int length){
    if(filename == NULL || buffer == NULL || length <= 0) return -1;

    // handle类似管道
    // 创建statement储物间
    MYSQL_STMT *stmt = mysql_stmt_init(handle);

    // 0成功
    if(mysql_stmt_prepare(stmt, SQL_INSERT_IMG_USER , strlen(SQL_INSERT_IMG_USER))){
        printf("mysql_statement_prepare failed\n", mysql_error(handle));
        return -2;
    }; //可以在参考手册chm里查用法)
    

    MYSQL_BIND param = {0}; //与stmt一一对应,把数据绑定到缓冲区
    param.buffer_type = MYSQL_TYPE_LONG_BLOB;
    param.buffer = NULL;
    param.is_null = 0;
    param.length = NULL;

    ret = mysql_stmt_bind_param(stat, &param); 
    if(ret){
        printf("mysql_statement_bind_param\n", mysql_error(handle));
        return -3;
    }   
    
    ret = mysql_stmt_send_long_data(stmt, 0, buffer, length);
    if(ret){
        printf("mysql_stmt_send_long_data\n", mysql_error(handle));
        return -4;
    }

    ret = mysql_stmt_execute(stmt);
    if(ret){
        printf("mysql_stmt_execute\n", mysql_error(handle));
        return -5;
    }

    ret = mysql_stmt_close(stmt); //有init就有close
    if(ret){
        printf(" mysql_stmt_close\n", mysql_error(handle));
        return -6;
    }
    return ret;
}

9 图片存储mysql_read

int mysql_read(MYSQL *handle, char *buffer, int length){
    if(handle == NULL || buffer == NULL || length <= 0) return -1;

    MYSQL_STMT *stmt = mysql_stmt_init(handle);
    int ret = mysql_stmt_prepare(stmt, SQL_SELECT_IMG_USER , strlen(SQL_SELECT_IMG_USER));
    if(ret){
        printf("mysql_statement_prepare failed: %s\n", mysql_error(handle));
        return -2;
    }; 

    MYSQL_BIND result = {0}; //与stmt一一对应,把数据绑定到缓冲区
    result.buffer_type = MYSQL_TYPE_LONG_BLOB;
    unsigned long total_length = 0;
    result.length = &total_length;

    // 和write一样 下面不绑定参数,绑定结果
    ret = mysql_stmt_bind_result(stmt, &result);
    if(ret){
        printf("mysql_stmt_bind_result: %s\n", mysql_error(handle));
        return -3;
    }

    ret = mysql_stmt_execute(stmt);
    if(ret){
        printf("mysql_stmt_execute: %s\n", mysql_error(handle));
        return -4;
    }
    

    ret = mysql_stmt_store_result(stmt);
    if(ret){
        printf("mysql_stmt_store_result: %s\n", mysql_error(handle));
        return -5;
    }
    while(1){
        ret = mysql_stmt_fetch(stmt);
        if(ret !=0 && ret != MYSQL_DATA_TRUNCATED) break;
            
        int start = 0;
        while(start < (int) total_length){
            // 抓取图片长度
            result.buffer = buffer + start; //不断更新buffer自己大小,不要再开辟新的空间了
            result.buffer_length = 1; //类似每次取一个byte
            mysql_stmt_fetch_column(stmt, &result, 0 , start); //第0列,从偏移量start开始
            start += result.buffer_length;
        }
    }

    mysql_stmt_close(stmt);
    return total_length;

}

10 调试

#define FILE_IMAGE_LENGTH (64*1024)

// C R U D增删改查 create update read delete
int main(){
    // 1 初始化
    MYSQL mysql;
    if(mysql_init(&mysql) == NULL){
        printf("mysql_init: %s\n", mysql_error(&mysql));
        return -1;
    };

    // 2 node server和db server连接
    if (!mysql_real_connect(&mysql, KING_DB_SERVER_IP, KING_DB_USERNAME, KING_DB_PASSWORD,
        KING_DB_DEFAULTDB, KING_DB_SERVER_PORT, NULL, 0)){
        
        printf("mysql_real_connect: %s\n", mysql_error(&mysql));
        goto Exit;
    };

    // mysql->insert 0成功 
    // 编译器编译忽略,执行才直接
    printf("case : mysql--->insert\n");
#if 1
    if(mysql_real_query(&mysql, SQL_INSERT_TBL_USER, strlen(SQL_INSERT_TBL_USER))){
        printf("mysql_real_query: %s\n", mysql_error(&mysql));
        goto Exit;
    };
#endif
    king_mysql_select(&mysql); //添加以后查询

    // mysql->delete
    printf("case : mysql--->delete\n");
#if 1
    if(mysql_real_query(&mysql, SQL_DELETE_TBL_USER, strlen(SQL_DELETE_TBL_USER))){
        printf("mysql_real_query: %s\n", mysql_error(&mysql));
        goto Exit;
    };
#endif
    king_mysql_select(&mysql); //删除以后查询


    printf("case: mysql-->read image and write mysql\n");
    char buffer[FILE_IMAGE_LENGTH] = {0};
    int length = read_image("0voice.jpg", buffer);
    if(length<0) goto Exit;

    mysql_write(&mysql, buffer, length);


    printf("case: mysql-->read mysql and write image\n");
    memset(buffer,0, FILE_IMAGE_LENGTH); //让初始buffer干净没有数据
    length = mysql_read(&mysql, buffer, FILE_IMAGE_LENGTH);
    
    write_image("a.jpg", buffer, length);


Exit:
    mysql_close(&mysql);


    return 0;
}

gcc -o mysql mysql.c -I /usr/include/mysql/ -lmysqlclient
./mysql

有点难,算囫囵吞枣

作业题

当node server和db server有很多连接handle时,希望数据库的连接池,不要每次创建连接handle,而是用完扔回池里


网站公告

今日签到

点亮在社区的每一天
去签到