C++ 二进制图片的读取和blob插入mysql_stmt_init—新年第一课

news2025/1/11 19:53:31

关于二进制图片的读取和BLOB插入一共包含五步

第一步:初始化

MYSQL_STMT* stmt = mysql_stmt_init(&mysql);

第二步:预处理sql语句

mysql_stmt_prepare(stmt,sql,sqllen);

第三步:绑定字段

mysql_stmt_bind_param(stmt,bind);

第四步:执行

mysql_stmt_excute(stmt);

第五步:关闭

mysql_stmt_close(stmt);

详细步骤如下:

1、连接数据库;这个就不详细讲解了

const char* host = "127.0.0.1";
	const char* user = "root";
	const char* password = "************";
	const char* database = "database_test";
	int port = 3306;

	MYSQL mysql;
	mysql_init(&mysql);
	mysql_library_init(0, NULL, NULL);
	//防止乱码
	mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "GBK");

	//支持多条sql语句 CLIENT_MULTI_STATEMENTS   clientflag  默认是不支持的
	//if (!mysql_real_connect(&mysql, host, user, password, database, port, 0, 0))
	if (!mysql_real_connect(&mysql, host, user, password, database, port, NULL, 0))
	{
		cout << "failed to connect" << host << mysql_error(&mysql) << endl;
	}

	else
	{
		cout << "successfully connected " << host <<endl;
	}

2、创建数据表t_data; 创建好存放二进制数据的表 t_data

注意***:这里的数据类型,必须是blob其他和以前一样

//创建号存放二进制数据的表 t_data
	string sql = "";
	sql="CREATE TABLe IF NOT EXISTS `t_data`(\
		`id` int AUTO_INCREMENT,\
		`name` varchar(1024),\
		`data` blob,\
		`size` int,\
		PRIMARY KEY(id)\
		)";
	
	int re = mysql_query(&mysql, sql.c_str());
	if (re != 0)
	{
		cout << "failed mysql_error" << mysql_error(&mysql) << endl;
	}

3、初始化stmt

MYSQL_STMT*stmt=mysql_stmt_init(&mysql);

4、预处理sql语句

// 注意这里的插入必须是???
sql="insert into t_data (name,data,size) values (?,?,?)";

//错误输出是mysql_stmt_error(stmt);和之前不一样哦;

//sql语句,插入数据
// 注意这里的插入必须是???

	sql = "insert into `t_data` (name,data,size) values (?,?,?)";
	if (mysql_stmt_prepare(stmt, sql.c_str(), sql.size()))
	{
		cerr << "mysql_stmt_prepare failed!" << mysql_stmt_error(stmt) << endl;
	}

5、绑定字段

MYSQL_BIND bind[3]={ 0 };

//第一个数据name
bind[0].buffer_type = MYSQL_TYPE_STRING; //name文件名
bind[0].buffer = (char*)filename.c_str();//原图的文件名
bind[0].buffer_length = filename.size();
//(char*) buffer要求的是void*,而c_str()是只读的,所以要转换char*。
//而bind是可以输入可以输出,所以不可以只读。默认const是只读的,但是在这儿是插入数据,不会修改数据,可以用char*

bind[1].buffer_type = MYSQL_TYPE_BLOB;// data 文件二进制内容
bind[1].buffer = data; //二进制文件
bind[1].buffer_length = filesize; //文件大小

//把文件大小存下来
//从mysql取出来,可以知道文件大小,但是不一定将它取出来,
//经常做显示文件列表,有时候不需要显示二进制。希望有个字段将文件大小存下来

bind[2].buffer_type = MYSQL_TYPE_LONG;//长整型,不用直到size,因为是固定大小
bind[2].buffer = &filesize;  //存放指针

5.1、如何filename 和data?

在上述代码的前面需要定义filename和data大小;

//4、打开并读取文件
	string filename = "mysql.jpg";

	//打开文件可以有多种方式,可以用c的file fopen,但是这个在vs2017里面说不安全,得设置不安全选项
	//fopen_s 是多线程安全版本的,到了linux又不行了
	//为了跨平台,用C++方式读取 fstream

	//需要知道文件大小和文件二进制  考虑到空间的分配和释放 动态空间
	//int filesize = 0;
	//ios::binary必须写,因为是二进制文件,如果是文本文件,无所谓不用加,但是还是加着
	fstream in(filename, ios::in | ios::binary);
	//文件是否存在
	if (!in.is_open())
	{
		cerr << "file" << filename << "open failed" << endl;
	}
	//fopen
	//句柄,文件指针移到结尾处
	//从结尾往前移动,移动0个位置,也就是结尾
	//判断位置
	in.seekg(0, ios::end);
	// 得到文件大小,
	//文件大小和文件二进制
	int filesize = in.tellg();
	//得到大小之后,将文件移到开头,要不然待会读文件就读不到了
	in.seekg(0, ios::beg);
	//分配空间大小
	char* data = new char[filesize];
	//查看是否读完,不保证一次读完,所以用循环读,可以直到读取了多少
	int readed = 0;//已经读了多少
	while (!in.eof())
	{
		//in.read(data, filesize); data读什么数据,filesize读多少
		//指针要进行移位
		in.read(data + readed, filesize - readed);
		//上次读了多少字节
		if (in.gcount()<=0)
		{
			break;
		}
		readed += in.gcount();
	}
	in.close();

6、绑定 mysql_stmt_bind_param(stmt, bind)


	//6、绑定
	if (mysql_stmt_bind_param(stmt, bind)!=0) //定义和文档不一致,=表示成功
	{
		//cerr错误输出,在重定向的时候,可以分割开来,也可以写到文件当中
		//cout 标准输出
		/*
		cout:写到标准输出的ostream对象;
		cerr:输出到标准错误的ostream对象,常用于程序错误信息;
		clog:也是输出标准错误流(这点儿和cerr是一样的),貌似平时很少用到这个啊;
		*/
		cerr << "mysql_stmt_bind_param failed" << mysql_stmt_error(stmt) << endl;
	}

7、执行stmt sql

//7、执行stmt sql
	if (mysql_stmt_execute(stmt) != 0)
	{
		//cerr错误输出
		cerr << "mysql_stmt_execute failed" << mysql_stmt_error(stmt) << endl;
	}

8、关闭stmt

mysql_stmt_close(stmt);

9、注意****

9.1、刚才申请了data空间是动态控件,必须要释放

delete []data;

9.2、在读取文件之后,一定要把文件关闭

in.close();

完整代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
#include<mysql.h>
#include<thread>
#include<map>
#include<sstream>
#include<fstream>
//计时
#include<chrono>
using namespace std;
using namespace chrono;


int main()
{
	const char* host = "127.0.0.1";
	const char* user = "root";
	const char* password = "*********"; //输入自己数据库的密码
	const char* database = "database_test";
	int port = 3306;

	MYSQL mysql;
	mysql_init(&mysql);
	mysql_library_init(0, NULL, NULL);
	//防止乱码
	mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "GBK");

	//支持多条sql语句 CLIENT_MULTI_STATEMENTS   clientflag  默认是不支持的
	//if (!mysql_real_connect(&mysql, host, user, password, database, port, 0, 0))
	if (!mysql_real_connect(&mysql, host, user, password, database, port, NULL, 0))
	{
		cout << "failed to connect" << host << mysql_error(&mysql) << endl;
	}

	else
	{
		cout << "successfully connected " << host <<endl;
	}
	
	//blob  blob存放二进制
	// int 最大值大概是2g
	

	//1、创建号存放二进制数据的表 t_data
	string sql = "";
	sql="CREATE TABLe IF NOT EXISTS `t_data`(\
		`id` int AUTO_INCREMENT,\
		`name` varchar(1024),\
		`data` blob,\
		`size` int,\
		PRIMARY KEY(id)\
		)";
	
	int re = mysql_query(&mysql, sql.c_str());
	if (re != 0)
	{
		cout << "failed mysql_error" << mysql_error(&mysql) << endl;
	}

	//2、初始化
	MYSQL_STMT* stmt = mysql_stmt_init(&mysql);


	//3、预处理sql语句
	sql = "insert into `t_data` (name,data,size) values (?,?,?)";
	if (mysql_stmt_prepare(stmt, sql.c_str(), sql.size()))
	{
		cerr << "mysql_stmt_prepare failed!" << mysql_stmt_error(stmt) << endl;
	}

	//4、打开并读取文件
	string filename = "mysql.jpg";

	//打开文件可以有多种方式,可以用c的file fopen,但是这个在vs2017里面说不安全,得设置不安全选项
	//fopen_s 是多线程安全版本的,到了linux又不行了
	//为了跨平台,用C++方式读取 fstream

	//需要知道文件大小和文件二进制  考虑到空间的分配和释放 动态空间
	//int filesize = 0;
	//ios::binary必须写,因为是二进制文件,如果是文本文件,无所谓不用加,但是还是加着
	fstream in(filename, ios::in | ios::binary);
	//文件是否存在
	if (!in.is_open())
	{
		cerr << "file" << filename << "open failed" << endl;
	}
	//fopen
	//句柄,文件指针移到结尾处
	//从结尾往前移动,移动0个位置,也就是结尾
	//判断位置
	in.seekg(0, ios::end);
	// 得到文件大小,
	//文件大小和文件二进制
	int filesize = in.tellg();
	//得到大小之后,将文件移到开头,要不然待会读文件就读不到了
	in.seekg(0, ios::beg);
	//分配空间大小
	char* data = new char[filesize];
	//查看是否读完,不保证一次读完,所以用循环读,可以直到读取了多少
	int readed = 0;//已经读了多少
	while (!in.eof())
	{
		//in.read(data, filesize); data读什么数据,filesize读多少
		//指针要进行移位
		in.read(data + readed, filesize - readed);
		//上次读了多少字节
		if (in.gcount()<=0)
		{
			break;
		}
		readed += in.gcount();
	}
	in.close();


	//5、绑定字段
	MYSQL_BIND bind[3] = { 0 };
	bind[0].buffer_type = MYSQL_TYPE_STRING; //name文件名
	//(char*) buffer要求的是void*,而c_str()是只读的,所以要转换char*。
	//而bind是可以输入可以输出,所以不可以只读。默认const是只读的,但是在这儿是插入数据,不会修改数据,可以用char*
	bind[0].buffer = (char*)filename.c_str();//原图的文件名
	bind[0].buffer_length = filename.size();

	bind[1].buffer_type = MYSQL_TYPE_BLOB;// data 文件二进制内容
	bind[1].buffer = data; //二进制文件
	bind[1].buffer_length = filesize; //文件大小

	//把文件大小存下来
	//从mysql取出来,可以知道文件大小,但是不一定将它取出来,
	//经常做显示文件列表,有时候不需要显示二进制。希望有个字段将文件大小存下来
	bind[2].buffer_type = MYSQL_TYPE_LONG;//长整型,不用直到size,因为是固定大小
	bind[2].buffer = &filesize;  //存放指针

	//6、绑定
	if (mysql_stmt_bind_param(stmt, bind)!=0) //定义和文档不一致,=表示成功
	{
		//cerr错误输出,在重定向的时候,可以分割开来,也可以写到文件当中
		//cout 标准输出
		/*
		cout:写到标准输出的ostream对象;
		cerr:输出到标准错误的ostream对象,常用于程序错误信息;
		clog:也是输出标准错误流(这点儿和cerr是一样的),貌似平时很少用到这个啊;
		*/
		cerr << "mysql_stmt_bind_param failed" << mysql_stmt_error(stmt) << endl;
	}

	//7、执行stmt sql
	if (mysql_stmt_execute(stmt) != 0)
	{
		//cerr错误输出
		cerr << "mysql_stmt_execute failed" << mysql_stmt_error(stmt) << endl;
	}
	delete [] data;
	mysql_stmt_close(stmt);

	mysql_close(&mysql);
	mysql_library_end();
	system("pause");
	return 0;
}

插入成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1361806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用友U8+CRM 逻辑漏洞登录后台漏洞复现

0x01 产品简介 用友U8 CRM客户关系管理系统是一款专业的企业级CRM软件&#xff0c;旨在帮助企业高效管理客户关系、提升销售业绩和提供优质的客户服务。 0x02 漏洞概述 用友 U8 CRM客户关系管理系统 reservationcomplete.php文件存在逻辑漏洞&#xff0c;未授权的攻击者通过…

手把手带你门SpringCloud

目录​​​​​ 1、什么是 SpringCloud&#xff1f; SpringCloud常用组件&#xff1a; 简单介绍组件间作用 2&#xff0c;SpringCloud相关组件&#xff1a;Eureka 3&#xff0c;Spring Cloud核心组件&#xff1a;Feign 4&#xff0c;Spring Cloud核心组件&#xff1a;Zuu…

软件测试|Docker exec命令详细使用指南

简介 Docker exec命令是Docker提供的一个强大工具&#xff0c;用于在正在运行的容器中执行命令。本文将详细介绍Docker exec命令的用法和示例&#xff0c;帮助大家更好地理解和使用这个命令。 Docker是一种流行的容器化平台&#xff0c;允许我们在容器中运行应用程序。有时候…

locust 快速入门--程序调试

背景 对测试的api引入locust后&#xff0c;不在使用requests库进行http请求了&#xff0c;而是通过client属性发送请求&#xff0c;实质是使用HttpSession。 问题&#xff1a;如果对locust程序进行调试 解决方案&#xff1a; 因为locust使用协程&#xff0c;需要开启pych…

微服务-java spi 与 dubbo spi

Java SPI 通过一个案例来看SPI public interface DemoSPI {void echo(); } public class FirstImpl implements DemoSPI{Overridepublic void echo() {System.out.println("first echo");} } public class SecondImpl implements DemoSPI{Overridepublic void ech…

万界星空科技云MES,助力客户快速构建数字工厂

一、MES发展趋势 1、定制化趋势 工业2.0、3.0的技术已较为成熟&#xff0c;部分制造业水平较为发达的国家已经率先进入以网络化、智能化为代表的工业4.0发展阶段,MES作为制造业规划层随着物联网等持续发展&#xff0c;为适应定制化时代&#xff0c;整体技术模块化、服务化将重…

解决Gitee每次push都需要输入用户名和密码

其实很简单&#xff0c;只需要使用命令 git config --global credential.helper store 在你下次push时只需要再输入一次用户名和密码&#xff0c;电脑就会保存下来&#xff0c;之后就无需进行输入了。

TypeScript 从入门到进阶之基础篇(三) 元组类型篇

系列文章目录 TypeScript 从入门到进阶系列 TypeScript 从入门到进阶之基础篇(一) ts基础类型篇TypeScript 从入门到进阶之基础篇(二) ts进阶类型篇 文章目录 系列文章目录TypeScript 从入门到进阶系列前言一、在TypeScript中使用元组二、TypeScript 中元组的使用场景1、让函…

从Spring Cloud Alibaba开始聊架构

作为SpringCloudAlibaba微服务架构实战派上下册和RocketMQ消息中间件实战派上下册的作者胡弦。 另外我的新书RocketMQ消息中间件实战派上下册&#xff0c;在京东已经上架啦&#xff0c;目前都是5折&#xff0c;非常的实惠。 https://item.jd.com/14337086.htmlhttps://item.jd…

【详解】求解迷宫所有路径(递归实现)----直接打穿迷宫

目录 递归的模型&#xff1a; 栈帧&#xff1a; 递归调用深度&#xff1a; ​编辑 用递归算法求解迷宫问题&#xff1a; 小结&#xff1a; 结语&#xff1a; 递归的小小总结&#xff0c;朋友们可以看看&#xff0c;有助于理解后面的递归程序。 递归的模型&#xff1a; …

钡铼技术2023年年度报告来了

不积跬步&#xff0c;无以至千里&#xff1b; 不积小流&#xff0c;无以成江海。 钡铼的2023年 平凡却又意义深远。 在工业自动化及物联网技术发展的道路上&#xff0c;钡铼技术每一个进步都源于不懈的努力和持续的积累。钡铼技术在过去的一年中&#xff0c;稳扎稳打&#xf…

QCharView使用

QChart是 QGraphicsWidget的子类。 QCharView是QGraphicsView的子类 QCharView概念:title、系列、图标Chart、视图 说明: 需要添加Qt组件charts 在使用QChart或者QChartView之前需要添加宏定义QT_CHARTS_USE_NAMESPACE &#xff08;其实是使用了命名空间&#xff09;&#xff…

前端uniapp的tab选项卡for循环切换、开通VIP实战案例【带源码/最新】

目录 效果图图1图2 源码最后 这个案例是uniapp&#xff0c;同样也适用Vue项目&#xff0c;语法一样for循环&#xff0c;点击切换 效果图 图1 图2 源码 直接代码复制查看效果 <template><view class"my-helper-service-pass"><view class"tab…

服务号怎么改为订阅号

服务号和订阅号有什么区别&#xff1f;服务号转为订阅号有哪些作用&#xff1f;很多小伙伴想把服务号改为订阅号&#xff0c;但是不知道改了之后具体有什么作用&#xff0c;今天跟大家具体讲解一下。首先我们知道服务号一个月只能发四次文章&#xff0c;但是订阅号每天都可以发…

Windows系统如何使用VNC远程连接Deepin桌面【内网穿透】

文章目录 1. 安装x11vnc2. 本地远程连接测试3. Deepin安装Cpolar4. 配置公网远程地址5. 公网远程连接Deepin桌面6. 固定连接公网地址7. 固定公网地址连接测试 x11vnc是一种在Linux系统中实现远程桌面控制的工具&#xff0c;它的原理是通过X Window系统的协议来实现远程桌面的展…

L1-078:吉老师的回归

题目描述 曾经在天梯赛大杀四方的吉老师决定回归天梯赛赛场啦&#xff01; 为了简化题目&#xff0c;我们不妨假设天梯赛的每道题目可以用一个不超过 500 的、只包括可打印符号的字符串描述出来&#xff0c;如&#xff1a;Problem A: Print "Hello world!"。 众所周知…

jdbc源码研究

JDBC介绍 JDBC&#xff08;Java Data Base Connectivity,java数据库连接&#xff09;是一种用于执行SQL语句的Java API&#xff0c;可以为多种关系数据库提供统一访问&#xff0c;它由一组用Java语言编写的类和接口组成。 开发者不必为每家数据通信协议的不同而疲于奔命&#…

竞赛保研 基于深度学习的人脸专注度检测计算系统 - opencv python cnn

文章目录 1 前言2 相关技术2.1CNN简介2.2 人脸识别算法2.3专注检测原理2.4 OpenCV 3 功能介绍3.1人脸录入功能3.2 人脸识别3.3 人脸专注度检测3.4 识别记录 4 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的人脸专注度…

联合体类型和枚举类型

联合体 联合体类型的声明 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以不同的类型。 联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫&#xff1a;共⽤体。 所以给联合体其中⼀个成员赋值&#xff0c;其他成员的值也跟着…

Android WiFi基础概览

Android WiFi 基础概览 1、WiFi协议2、Android WLAN 架构2.1 应用框架2.2 Wi-Fi 服务2.3 Wi-Fi HAL 3、相关编译 android13-release 1、WiFi协议 Wi-Fi&#xff08;无线通信技术&#xff09;_百度百科 2.4GHz 频段支持以下标准&#xff08;802.11b/g/n/ax&#xff09;&#xff…