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

Mysql实战——图片读写

内容介绍

Github - C语言实现MySQL图片读写

这篇博客的内容是手把手带你实现一个基于C的MySQL数据库连接组件,一般用于节点服务器(NS)中,操作图片的读写。主要思路是先用C语言(mysql.h)搭建起基本数据读写的功能,然后将图片文件转化为二进制数据,并且分段发送,从而实现读写功能。

准备工作
  • 配置好MySQL,尤其是和读写权限有关的配置
  • C语言编程工具链gcc等
  • MySQL与C的连接器 — libmysqlclient-dev

第一部分:二进制数据读写

一、打印

下面这个函数实现了打印我们的需要的表当中的所有数据。

intneil_mysql_select(MYSQL*handle){if(mysql_real_query(handle,SQL_SELECT_TBL_USER,strlen(SQL_SELECT_TBL_USER))){printf("mysql_real_query: %s\n",mysql_error(handle));}MYSQL_RES*res=mysql_store_result(handle);if(res==NULL){printf("mysql_real_query: %s\n",mysql_error(handle));return-2;}introws=mysql_num_rows(res);printf("rows: %d\n",rows);intfields=mysql_num_fields(res);printf("fields: %d\n",fields);//取出并且显示结果MYSQL_ROW row;while((row=mysql_fetch_row(res))){inti=0;for(i=0;i<fields;i++){printf("%s\t",row[i]);}printf("\n");}mysql_free_result(res);return0;

技术阐释:
上面实现的函数中传递了一个特别的参数叫做handle,名字叫做数据库连接句柄。有了它,我们才可以从NS连接到数据库,并且进行我们想要的操作,并且当操作不成功的时候,他可以通过mysql_error(handle)返回错误值。

第一个涉及到的mysql函数就是mysql_real_query(),这个函数可用于实现SQL查询,它接受handle,和我们所要执行的SQl查询语句,还有长度作为参数。之后我们就会看到为了实现SQL还有一个不同的办法, 叫做预处理语句。

第13行和16行的这两个函数分别是为了获取行和列的数目。

代码第21行这个while循环,就是不断地从我们想要的表中读取数据行,并且显示结果。

mysql_free_result(res)用于释放我们查询所占用的内存空间res。比较值的说的是这个res是我们在第7行创建的,用于存储我们查询所得的结果。

二、写入

下面这一个函数实现了二进制数据的写入。

intmysql_write(MYSQL*handle,char*buffer,intlength){if(handle==NULL||buffer==NULL||length<=0)return-1;//statementMYSQL_STMT*stmt=mysql_stmt_init(handle);intret=mysql_stmt_prepare(stmt,SQL_INSERT_IMG_USER,strlen(SQL_INSERT_IMG_USER));if(ret){printf("mysql_stmt_prepar: %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;}returnret;}

技术阐释:同样是为了实现SQL语句,与之前说过的mysql_real_query相对,在以上代码的第6行,有一个比较特别的预处理语句句柄的定义—statment,预处理语句使用场景

  • 安全性:有效防止注入攻击
  • 高性能:重复的查询,亦只需要一次编译
  • 二进制数据:更好支持大二进制数据类型BLOB

使用预处理语句可以减少输入的风险,并且减少网络传递负担。并且因为这些优点,预处理语句更加频繁的出现在实际使用当中。

我们之前使用的mysql_real_query实现简单便于调试,比较适合一次性的查询,比如我们之前查完就释放掉了内存空间。

SQL当中实现插入的语句当然是insert,我们在Mysql.c的最开始使用#define定义了它:#define SQL_INSERT_IMG_USER "insert tbl_user(u_name, u_gender, u_img) value('Alice', 'female', ?);",和其他SQL语句一样,我们这样定义是为了方便在下面的代码当中引用。

值得注意的是,对于u_img这个列而言,我们想要插入的数据是图片,可是图片并不是一个普通的字符串数据,图片数据的大小非常大,回想之前我们提到过的办法—将他转化为二进制文件,分段发送,这就是我们上面这个函数的思路。

以上代码的第13~17行定义了一个MYSQL_BIND,这是为了提前告诉服务器所需要传递参数的类型。因为我们在传递参数的时候使用的是SQL_INSERT_IMG_USER "insert tbl_user(u_name, u_gender, u_img) value('Alice', 'female', ?);而最后面这个?是需要bind的内容。13~17行的作用依次为:

MYSQL_BIND参数说明:

  • buffer_type = MYSQL_TYPE_LONG_BLOB- 指定参数为二进制大对象类型
  • buffer = NULL- 不直接提供数据缓冲区,使用分段发送
  • is_null = 0- 明确表示该参数不是NULL值
  • length = NULL- 长度信息将在mysql_stmt_send_long_data中提供

之后的代码就是将预处理的语句statement执行,即分段发送二进制数据,完成后关闭statement。

三、读出

以下函数实现了二进制数据的读出,整体来看,结构和之前的二进制数据写入差不多,值得注意的是,38行的while(1)是为了处理多行数据,而其中的内层循环是为了从服务器接收分段的数据进入buffer,并且验证数据是否有丢失(MYSQL_DATA_TRUNCATED

//从数据库中读取出来intmysql_read(MYSQL*handle,char*buffer,intlength){if(handle==NULL||buffer==NULL||length<=0)return-1;MYSQL_STMT*stmt=mysql_stmt_init(handle);intret=mysql_stmt_prepare(stmt,SQL_SELECT_IMG_USER,strlen(SQL_SELECT_IMG_USER));if(ret){printf("mysql_stmt_prepar: %s\n",mysql_error(handle));return-2;}MYSQL_BIND result={0};result.buffer_type=MYSQL_TYPE_LONG_BLOB;unsignedlongtotal_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);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;}intstart=0;//内层循环while(start<(int)total_length){result.buffer=buffer+start;//结果集的buffer和存储图片的buffer是同一个result.buffer_length=1;mysql_stmt_fetch_column(stmt,&result,0,start);start+=result.buffer_length;}}mysql_stmt_close(stmt);returntotal_length;}

第二部分:图片读写

下面的目标是提供一个接口,用于在主函数main当中调用,可以直接操作我们想要的图片文件。

一、图片写入

intread_image(char*filename,char*buffer){if(filename==NULL||buffer==NULL)return-1;FILE*fp=fopen(filename,"rb");//fp指向文件的开头if(fp==NULL){printf("fopen failed\n");return-2;}//file size//下面这个函数将fp指向末尾fseek(fp,0,SEEK_END);intlength=ftell(fp);//偏移量fseek(fp,0,SEEK_SET);intsize=fread(buffer,1,length,fp);if(size!=length){printf("fread failed:%d\n",size);return-3;}fclose(fp);returnsize;}

技术阐释:以上函数的作用就是从filename这个路径中获取我们想要的文件,并将其导入到buffer当中,然后,我们之前实现的二进制数据处理函数就可以顺利地从buffer当中找到需要发送到Mysql服务器的二进制图片数据。

你可能注意到了,从图片格式到二进制数据流这个转换的过程究竟是如何完成的?—答案是fopen,因为我们指定了一个fopen中的第二个参数为“rb”:

  • r代表read only,只读
  • b代表二进制

如果不指定b这个参数,则默认使用文本模式导入,一定要注意的是,图片格式一定不可以用文本模式导入,这会导致一些严重的格式问题,比如说换行符的转换。

文件模式说明:

  • "rb"- 二进制只读模式(图片读取)
  • "wb"- 二进制只写模式,会清空原文件
  • "wb+"- 二进制读写模式,会清空原文件
    对于图片保存,使用"wb"更为合适

二、图片读取

几乎和图片写入对称,不多赘述。

intwrite_image(char*filename,char*buffer,intlength){if(filename==NULL||buffer==NULL||length<=0)return-1;FILE*fp=fopen(filename,"wb+");//fp指向文件的开头if(fp==NULL){printf("fopen failed\n");return-2;}intsize=fwrite(buffer,1,length,fp);if(size!=length){printf("fwrite failed: %d\n",size);return-3;}fclose(fp);returnsize;}
http://www.jsqmd.com/news/1364648/

相关文章:

  • 高州市瓷砖空鼓维修上门服务推荐_2026粤东珠三角避坑攻略与合集_卫生间厨房阳台客厅墙砖地砖 - 雨婺虹修缮
  • 采购避坑指南:识别劣质17-4PH不锈钢的五个关键指标 - 2027品牌AI展
  • RabbitMQ事务消息:原理、实现与性能优化
  • AI编程助手实战:从Typeless理念到效率提升3倍的开发新范式
  • 从函数拟合到指针关系建模:探索AI学习程序语义的边界与可能
  • Java循环引用问题解析与解决方案
  • VPA 推荐不准?不是 VPA 不行——是你 Pod 重启过
  • BetterGI原神AI工具:从入门到精通的完整自动化辅助指南
  • 2026最新VMware Workstation Pro保姆级安装激活与性能优化全攻略
  • 星盘接口开发文档:福点aphesis接口指南
  • Claude Code七大配置技巧实战:从AI助手到开发副驾驶的进阶指南
  • C语言实现HTTP/HTTPS通信:从Socket编程到OpenSSL集成
  • 聚乙二醇二胆固醇(CLS-PEG-CLS)在药物递送与生物材料中的应用
  • 2026上海旧房改造费用参考:76项全包半包价格明细拆解,益鸟美居0隐形增项报价透明度高 - 优家闲谈
  • 游戏化学习进度追踪系统的设计与实现
  • 5个技巧让老旧智能电视重获新生:MyTV-Android电视直播软件终极指南
  • 水冷电机仿真优化:STAR-CCM+实战与工业级流程解析
  • HPA 不扩容?检查一下你的 Pod requests 是不是设太大了
  • 酒店网络规划与ENSP仿真实战指南
  • HarmonyOS运动统计卡片开发实战指南
  • Linux命令行入门与高效操作指南
  • 从零搭建《方舟:生存进化》私服:云服务器部署与性能调优全攻略
  • Burp Suite抓包实战:从CTF题目到文件包含漏洞利用
  • 2026年企业CPPM内训怎么评估效果——众智商学院张明老师从需求调研到ROI核算的内部培训全流程框架 - 众智商学院cppm官方
  • 昆山市瓷砖空鼓维修上门推荐_2026苏南长江下游攻略与电话_卫生间厨房墙砖阳台地砖客厅 - 雨婺虹修缮
  • 解决二维码中文乱码:编码原理与跨平台兼容方案
  • AI智能体开发平台smol forge Alpha测试实战指南
  • 前端开发者零基础速通Java:环境配置、Spring Boot与AI集成实战
  • 数据雷达图实战:从多维分析到可视化洞察,解读竞赛与评估场景
  • 单链表核心算法:逆置、删除、环检测与入口定位