关于二进制图片的读取和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;
}
插入成功