Linux C/C++ 开发(学习笔记八):Mysql数据库图片存储

本文涉及的产品
RDS Agent(兼容OpenClaw),2核4GB
RDS AI 助手,专业版
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: Linux C/C++ 开发(学习笔记八):Mysql数据库图片存储

1.准备好一张图片文件,将图片read,存放到buffer

2.将图片数据(buffer)写入mysql

3.从mysql中读取图片数据(buffer)

4.将图片数据(buffer)写入磁盘

一、读入图片

//读取图片
//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");
    if(fp==NULL){
        printf("fopen faild\n");
        return -2;
    }
    //file size
    fseek(fp,0,SEEK_END);//把文件指针置到末尾
    int length=ftell(fp);//返回当前文件流的指针位置,也就是偏移量,文件大小file size 
    fseek(fp,0,SEEK_SET);//把文件指针置到开头
    int size=fread(buffer,1,length,fp);//每次读1个字节,读length次。读取的参数放到buffer里
    if(size!=length){
        printf("fread faild\n");
        return -3;
    }
    fclose(fp);
    return size;
}

二、图片数据写入磁盘

//图片写入到磁盘(参数1:保存的文件名 参数2:要写的数据   参数3:数据长度)
int write_image(char* filename,char* buffer,int length){
    if(filename==NULL||buffer==NULL||length<=0) return -1;
    FILE *fp=fopen(filename,"wb+");//w表示写入 b表示二进制 +表示,如果文件不存在则会创建,存在则会直接写入
    if(fp==NULL){
        printf("fopen faild\n");
        return -2;
    }
    int size=fwrite(buffer,1,length,fp);//从buffer写到fp中,每次写1个字节,写length次
    if(size!=length){
        printf("fwirte failded:%d\n",size);
        return -3;
    }
    fclose(fp);
    return size;
}

三、表中增加图片数据一列

在表中加一列U_IMG,存放图片数据

ALTER TABLE TBL_USER ADD U_IMG BLOB;

变成下面这样

四、将图片数据存入Mysql服务器

MySQL C语言接口-预处理语句

mysql stmt语法_mysql中SQL执行过程详解与用于预处理语句的SQL语法

//插入图片的SQL语句,由于不知道传入的图片是什么,用?作为占位符
#define SQL_INSERT_IMG_USER "insert TBL_USER(U_NAME,U_GENDER,U_IMG) value('Dog','man',?);"  
int mysql_write(MYSQL* handle,char* buffer,int length){
    if(handle==NULL||buffer==NULL||length<=0) return -1;
    MYSQL_STMT *stmt=mysql_stmt_init(handle);//在mysql中创建一个存储空间
    int ret=mysql_stmt_prepare(stmt,SQL_INSERT_IMG_USER,strlen(SQL_INSERT_IMG_USER));
    if(ret){
        printf("mysql_stmt_prepare:%s\n",mysql_error(handle));
        return -2;
    }
    MYSQL_BIND param={0};
    param.buffer_type=MYSQL_TYPE_LONG_BLOB;
    param.buffer=NULL;
    param.is_null=0;
    param.length=NULL;
    ret=mysql_stmt_bind_param(stmt,&param);
    if(ret){
        printf("mysql_stmt_bind_param:%s\n",mysql_error(handle));
        return -3;
    }
    ret=mysql_stmt_send_long_data(stmt,0,buffer,length);//允许应用程序分块地将参数数据发送到服务器
    if(ret){
        printf("mysql_stmt_send_long_data:%s\n",mysql_error(handle));
        return -4;
    }
    ret=mysql_stmt_execute(stmt);//将当前绑定的参数标记符的值发送到服务器,服务器用新提供的数据替换标记符
    if(ret){
        printf("mysql_stmt_execute:%s\n",mysql_error(handle));
        return -5;
    }
    ret=mysql_stmt_close(stmt);
    if(ret){
        printf("mysql_stmt_close:%s\n",mysql_error(handle));
        return -6;
    }
    return ret;
}

五、将图像数据从Mysql服务器中取出

#define SQL_SELECT_IMG_USER "SELECT U_IMG FROM TBL_USER WHERE U_NAME='Dog';"
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);//在mysql中创建一个存储空间
    int ret=mysql_stmt_prepare(stmt,SQL_SELECT_IMG_USER,strlen(SQL_SELECT_IMG_USER));
    if(ret){
        printf("mysql_stmt_prepare:%s\n",mysql_error(handle));
        return -2;
    }
    MYSQL_BIND result={0};
    result.buffer_type=MYSQL_TYPE_LONG_BLOB;
    unsigned long total_length=0;
    result.length=&total_length;
    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);//执行后,长度数据会存储到total_length中
    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;
            result.buffer_length=1;//每次读取得长度为1
            mysql_stmt_fetch_column(stmt,&result,0,start);
            start+=result.buffer_length;
        }
    }
    mysql_stmt_close(stmt);
    return total_length;
}

六、完整代码

#include<mysql.h>
#include<stdio.h>
#include<string.h>
#define KING_DB_SERVER_IP "192.168.192.128" //ip
#define KING_DB_SERVER_PORT 3306            //端口号
#define KING_DB_USERNAME "admin"            //用户名
#define KING_DB_PASSWORD "123456"           //密码
#define KING_DB_DEFAULTDB "KING_DB"         //数据库名
//插入图片的SQL语句,由于不知道传入的图片是什么,用?作为占位符
#define SQL_INSERT_IMG_USER "insert TBL_USER(U_NAME,U_GENDER,U_IMG) value('Dog','man',?);"  
#define SQL_SELECT_IMG_USER "SELECT U_IMG FROM TBL_USER WHERE U_NAME='Dog';"
#define FILE_IMAGE_LENGTH (64*1024)
//读取图片
//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");
    if(fp==NULL){
        printf("fopen faild\n");
        return -2;
    }
    //file size
    fseek(fp,0,SEEK_END);//把文件指针置到末尾
    int length=ftell(fp);//返回当前文件流的指针位置,也就是偏移量,文件大小file size 
    fseek(fp,0,SEEK_SET);//把文件指针置到开头
    int size=fread(buffer,1,length,fp);//每次读1个字节,读length次。读取的参数放到buffer里
    if(size!=length){
        printf("fread faild\n");
        return -3;
    }
    fclose(fp);
    return size;
}
//图片写入到磁盘(参数1:保存的文件名 参数2:要写的数据   参数3:数据长度)
int write_image(char* filename,char* buffer,int length){
    if(filename==NULL||buffer==NULL||length<=0) return -1;
    FILE *fp=fopen(filename,"wb+");//w表示写入 b表示二进制 +表示,如果文件不存在则会创建,存在则会直接写入
    if(fp==NULL){
        printf("fopen faild\n");
        return -2;
    }
    int size=fwrite(buffer,1,length,fp);//从buffer写到fp中,每次写1个字节,写length次
    if(size!=length){
        printf("fwirte failded:%d\n",size);
        return -3;
    }
    fclose(fp);
    return size;
}
int mysql_write(MYSQL* handle,char* buffer,int length){
    if(handle==NULL||buffer==NULL||length<=0) return -1;
    MYSQL_STMT *stmt=mysql_stmt_init(handle);//在mysql中创建一个存储空间
    int ret=mysql_stmt_prepare(stmt,SQL_INSERT_IMG_USER,strlen(SQL_INSERT_IMG_USER));
    if(ret){
        printf("mysql_stmt_prepare:%s\n",mysql_error(handle));
        return -2;
    }
    MYSQL_BIND param={0};
    param.buffer_type=MYSQL_TYPE_LONG_BLOB;
    param.buffer=NULL;
    param.is_null=0;
    param.length=NULL;
    ret=mysql_stmt_bind_param(stmt,&param);
    if(ret){
        printf("mysql_stmt_bind_param:%s\n",mysql_error(handle));
        return -3;
    }
    ret=mysql_stmt_send_long_data(stmt,0,buffer,length);//允许应用程序分块地将参数数据发送到服务器
    if(ret){
        printf("mysql_stmt_send_long_data:%s\n",mysql_error(handle));
        return -4;
    }
    ret=mysql_stmt_execute(stmt);//将当前绑定的参数标记符的值发送到服务器,服务器用新提供的数据替换标记符
    if(ret){
        printf("mysql_stmt_execute:%s\n",mysql_error(handle));
        return -5;
    }
    ret=mysql_stmt_close(stmt);
    if(ret){
        printf("mysql_stmt_close:%s\n",mysql_error(handle));
        return -6;
    }
    return ret;
}
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);//在mysql中创建一个存储空间
    int ret=mysql_stmt_prepare(stmt,SQL_SELECT_IMG_USER,strlen(SQL_SELECT_IMG_USER));
    if(ret){
        printf("mysql_stmt_prepare:%s\n",mysql_error(handle));
        return -2;
    }
    MYSQL_BIND result={0};
    result.buffer_type=MYSQL_TYPE_LONG_BLOB;
    unsigned long total_length=0;
    result.length=&total_length;
    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);//执行后,长度数据会存储到total_length中
    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;
            result.buffer_length=1;//每次读取得长度为1
            mysql_stmt_fetch_column(stmt,&result,0,start);
            start+=result.buffer_length;
        }
    }
    mysql_stmt_close(stmt);
    return total_length;
}
int main(){
    MYSQL mysql;
    if(!mysql_init(&mysql)){//mysql初始化并判断是否出错(返回0为失败)
        printf("mysql_init:%s\n",mysql_error(&mysql));//mysql_error() 函数返回上一个 MySQL 操作产生的文本错误信息
        return -1;
    }
    if(!mysql_real_connect(&mysql,KING_DB_SERVER_IP,KING_DB_USERNAME,KING_DB_PASSWORD, 
    KING_DB_DEFAULTDB,KING_DB_SERVER_PORT,NULL,0)){ //连接mysql数据库,并判断是否出错(返回0为失败)
        printf("mysql_real_connect:%s\n",mysql_error(&mysql));
        return -2;
    }
    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);
    length=mysql_read(&mysql,buffer,FILE_IMAGE_LENGTH);
    write_image("a.jpg",buffer,length);
Exit:
    mysql_close(&mysql);
    return 0;
}


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
存储 SQL 关系型数据库
Mysql学习笔记(二):数据库命令行代码总结
这篇文章是关于MySQL数据库命令行操作的总结,包括登录、退出、查看时间与版本、数据库和数据表的基本操作(如创建、删除、查看)、数据的增删改查等。它还涉及了如何通过SQL语句进行条件查询、模糊查询、范围查询和限制查询,以及如何进行表结构的修改。这些内容对于初学者来说非常实用,是学习MySQL数据库管理的基础。
434 6
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
5202 77
|
Shell Linux
Linux shell编程学习笔记30:打造彩色的选项菜单
Linux shell编程学习笔记30:打造彩色的选项菜单
|
Ubuntu 搜索推荐 Linux
详解Ubuntu的strings与grep命令:Linux开发的实用工具。
这就是Ubuntu中的strings和grep命令,透明且强大。我希望你喜欢这个神奇的世界,并能在你的Linux开发旅程上,通过它们找到你的方向。记住,你的电脑是你的舞台,在上面你可以做任何你想做的事,只要你敢于尝试。
554 32
|
SQL Ubuntu 关系型数据库
Mysql学习笔记(一):数据库详细介绍以及Navicat简单使用
本文为MySQL学习笔记,介绍了数据库的基本概念,包括行、列、主键等,并解释了C/S和B/S架构以及SQL语言的分类。接着,指导如何在Windows和Ubuntu系统上安装MySQL,并提供了启动、停止和重启服务的命令。文章还涵盖了Navicat的使用,包括安装、登录和新建表格等步骤。最后,介绍了MySQL中的数据类型和字段约束,如主键、外键、非空和唯一等。
346 3
Mysql学习笔记(一):数据库详细介绍以及Navicat简单使用
|
SQL 关系型数据库 MySQL
php学习笔记-连接操作mysq数据库(基础)-day08
本文介绍了PHP中连接操作MySQL数据库的常用函数,包括连接服务器、设置字符集、关闭连接、选择数据库、结果集释放、获取影响行数以及遍历结果集等操作。通过书籍查询的实例演示了如何使用这些函数进行数据库操作,并提供了一个PHP操纵MySQL数据库的模板。
php学习笔记-连接操作mysq数据库(基础)-day08
|
并行计算 Ubuntu Linux
Ubuntu学习笔记(三):Linux下操作指令大全
Ubuntu学习笔记,介绍了Linux操作系统中常用的命令和操作,如文件管理、系统信息查看、软件安装等。
512 3
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
918 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
Shell Linux
Linux shell编程学习笔记82:w命令——一览无余
Linux shell编程学习笔记82:w命令——一览无余
|
存储 Linux 开发工具
如何进行Linux内核开发【ChatGPT】
如何进行Linux内核开发【ChatGPT】