linux下使用C语言操作MYSQL存取图片


运行环境

Ubuntu20.04 虚拟机
Mysql 8.0 API libmysqlclient-dev
gcc 9.4.0


数据库中可以存储一些小型的资源文件,例如图片和声音文件。较大的资源文件应该存储在服务器中,在数据库中写文件索引。
MYSQL 中有个数据对象是 BLOB,即 Binary Large Object,顾名思义也就是二进制大型数据对象,用来记录二进制的数据,它有 4 种类型,分别是:tinyblob(255B)、blob(65KB)、mediumblob(16MB)、longblob(4GB)。

一、准备工作

在数据库中创建一张准备存储文件的表:

CREATE DATABASE C_DB;
USE C_DB;
CREATE TABLE C_IMG(
ID INT PRIMARY KEY AUTO_INCREMENT,#主键且自增
Images blob
);

在这里插入图片描述


二、建立与mysql的连接

1、在C文件中引入头文件

在C文件中引入头文件#inlude<mysql.h>
通过MYSQL mysql;创建一个mysql结构体对象
MYSQL结构体定义在mysql.h

#include<mysql.h>
#include<stdio.h>
#include<string.h>
int main()
{
	 MYSQL mysql;
}

2、初始化mysql与数据库的通道

mysql_init函数初始化失败会返回NULL
mysql_error是mysql API提供的标准错误

	 if(mysql_init(&mysql) == NULL)
    {
        printf("%s",mysql_error(&mysql));
        return -1;
    }

3、与mysql建立真实连接

先在文件最前面定义一些宏,方便后面使用

#define C_DB_SERVER_IP   "192.168.186.128" //要连接的数据库IP
#define C_DB_SERVER_PORT 3306			   //要连接的端口号
#define C_DB_USERNAME    "admin"		   //mysql用户名
#define C_DB_PASSWORD    "admin"		   //mysql密码
#define C_DB_DEFAULT_DB  "C_IMG"		   //连接后默认使用的数据库
	//参数依次为 mysql通道,
	//ip,用户名,
	//密码,数据库名,
	//端口号,socket(设置为NULL的时候表示不适用socket),客户端标识符
   	//mysql_real_connect函数若返回0表示则表示连接失败
    if(!mysql_real_connect(&mysql,
	C_DB_SERVER_IP, C_DB_USERNAME, 
    C_DB_PASSWORD, C_DB_DEFAULT_DB, 
    C_DB_SERVER_PORT,NULL, 0))
    {
        printf("mysql_real_connect : %s\n",mysql_error(&mysql));
        return -2;
    }

此时已经通过C代码与MYSQL数据库进行了连接,下一步便可以开始进行数据库的操作


三、硬盘中文件的读写

1硬盘中读取文件

//硬盘读取文件
int read_image(char *filename, char *buffer)
{
    //检测文件和文件二进制容器buffer是否存在
    if(filename == NULL || buffer == NULL) return -1;
    //定义文件指针 指向文件最开始 rb是以 读二进制 的方式打开
    FILE *fp = fopen(filename,"rb");
    if(fp == NULL)
    {
        printf("fopen failed\n");
        return -2;
    }
    //检测文件大小
    fseek(fp, 0, SEEK_END);     //把文件指针置到文件末尾
    int fileLen = ftell(fp);    //获取文件偏移量
    fseek(fp, 0 ,SEEK_SET);     //把文件指针置到文件开头

    //获取文件的二进制长度 
    //buffer为接收数据的地址 size为一个单元的大小 count为单元个数 stream为文件流。
    //返回实际读取的单元个数。如果小于count,则可能文件结束或读取出错;
    int fileSize = fread(buffer, 1, fileLen, fp);    
    //因为是每次读取一个单位,所以fileLen应该等于fileSize
    if(fileSize != fileLen)
    {
        printf("fread failed : %d\n", fileSize);
        return -3;
    }
	//如果读取到的大小超过了FILE_MAX_SIZE 也就是超过了数据库中blob的65KB则返回错误
    if(fileSize >= FILE_MAX_SIZE)
    {
        printf("file size is too big\n");
        return -4;
    }
    
    fclose(fp);
    return fileSize;
}

2文件写入硬盘

//硬盘写入文件
int write_image(char *filename, char *buffer, int fileLen)
{
    if(filename == NULL || buffer == NULL)  return -1;
    //定义文件指针 指向文件最开始 wb+是以 写二进制 的方式打开 +号表示如果没有这个文件便创建
    FILE *fp = fopen(filename,"wb+");
    if(fp == NULL)
    {
        printf("fopen failed");
        return -2;
    }
    //由二进制的方式写入文件 
    //buffer为接收数据的地址 size为一个单元的大小 count为单元个数 stream为文件流。
    //返回实际读取的单元个数。如果小于count,则可能文件结束或读取出错;
    int length = fwrite(buffer, 1, fileLen, fp);
    if(fileLen != length)
    {
        printf("fwite failed: %d\n",length);
        return -3;
    }
    fclose(fp);
    return length;
}

四、数据库中文件的读写

1二进制文件写入数据库

关于mysql C API的详细内容请在mysql官网查询
https://www.mysqlzh.com/doc/196/116.html

//二进制文件写入数据库
int mysql_write(MYSQL *mysql, char *buffer, int fileLen, char *sql)
{
    if(mysql == NULL || buffer == NULL || fileLen <= 0) return -1;
    //初始化预处理语句句柄
    /*
    MYSQL_BIND该结构用于语句输入(发送给服务器的数据值)和输出(从服务器返回的结果值)。
    对于输入,它与mysql_stmt_bind_param()一起使用,用于将参数数据值绑定到缓冲区上,以供mysql_stmt_execute()使用。
    对于输出,它与mysql_stmt_bind_result()一起使用,用于绑定结果缓冲区,以便用于with mysql_stmt_fetch()以获取行。
    */
    MYSQL_STMT *stmt = mysql_stmt_init(mysql);
    //mysql_stmt_prepare 成功返回0
    int ret = mysql_stmt_prepare(stmt, sql, strlen(sql));
    if(ret)
    {
        printf("mysql_stmt_prepare : %s\n", mysql_error(mysql));
        return -2;
    }
    MYSQL_BIND param = {0};
    param.buffer_type = MYSQL_TYPE_BLOB;
    param.buffer = NULL;
    param.length = NULL;

    //mysql_stmt_bind_param()一起使用,用于将参数数据值绑定到缓冲区上,以供mysql_stmt_execute()使用
    ret = mysql_stmt_bind_param(stmt, &param);
    if(ret)
    {
        printf("mysql_stmt_bind_param : %s\n", mysql_error(mysql));
        return -3;
    }

    //允许应用程序分段地(分块)将参数数据发送到服务器。
    // 可以多次调用该函数,以便发送关于某一列的字符或二进制数据的不同部分,列必须是TEXT或BLOB数据类型之一。
    //第二个参数指明了与数据关联的参数。参数从0开始编号。
    //第三个参数是指向包含将要发送的数据的缓冲区的指针,第四个参数指明了缓冲区内的字节数。
    ret = mysql_stmt_send_long_data(stmt, 0, buffer, fileLen);
    if(ret)
    {
        printf("mysql_stmt_bind_param : %s\n", mysql_error(mysql));
        return -4;
    }

    //执行与语句句柄相关的预处理查询。
    ret = mysql_stmt_execute(stmt);
    if(ret)
    {
        printf("mysql_stmt_execute : %s\n", mysql_error(mysql));
        return -5;   
    }

    //预处理句柄关闭
    ret = mysql_stmt_close(stmt);
    if(ret)
    {
        printf("mysql_stmt_close : %s\n", mysql_error(mysql));
        return -6;
    }

    return ret;
}

2从数据库读取二进制文件

//从数据库读取二进制文件
int mysql_read(MYSQL *mysql, char *buffer, int fileLen, char *sql)
{
    if(mysql == NULL || buffer == NULL || fileLen <= 0 || sql == NULL) return -1;
    MYSQL_STMT stmt = mysql_stmt_init(mysql);
    int ret = mysql_stmt_prepare(stmt, sql, strlen(sql));
    if(ret)
    {
        printf("mysql_stmt_prepare : %s\n", mysql_error(mysql));
        return -2;
    }

    unsigned long total_length = 0; //计数器

    /*
    MYSQL_BIND该结构用于语句输入(发送给服务器的数据值)和输出(从服务器返回的结果值)。
    对于输入,它与mysql_stmt_bind_param()一起使用,用于将参数数据值绑定到缓冲区上,以供mysql_stmt_execute()使用。
    对于输出,它与mysql_stmt_bind_result()一起使用,用于绑定结果缓冲区,以便用于with mysql_stmt_fetch()以获取行。
    */
    MYSQL_BIND result = {0};
    result.buffer_type = MYSQL_TYPE_BLOB;
    result.length = &total_length;

    //mysql_stmt_bind_result()用于将结果集中的列与数据缓冲和长度缓冲关联(绑定)起来
    //当调用mysql_stmt_fetch()以获取数据时,
    //MySQL客户端/服务器协议会将绑定列的数据置于指定的缓冲区内。
    mysql_stmt_bind_result(stmt, &result);
    if(ret)
    {
        printf("mysql_stmt_bind_result : %s",mysql_error(mysql));
        return -3;
    }

    //执行 mysql_stmt_execute后结果在管道里面
    mysql_stmt_execute(stmt);
    if(ret)
    {
        printf("mysql_stmt_execute : %s\n", mysql_error(mysql));
        return -4;
    }

    //对于成功生成结果集的所有语句(SELECT、SHOW、DESCRIBE、EXPLAIN),
    //仅当打算对客户端的全部结果集进行缓冲处理时,必须调用mysql_stmt_store_result(),以便后续的mysql_stmt_fetch()调用能返回缓冲数据。
    ret = mysql_stmt_store_result(stmt);
    if(ret)
    {
        printf("mysql_stmt_store_result : %s\n", mysql_error(mysql));
		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;
            /*mysql_stmt_fetch_column从当前结果集行获取1列。
            参数二提供了应将数据置于其中的缓冲。
            参数三指明了将获取哪个列。第1列编号为0。
            参数四是数据值内的偏移量,将从该处开始检索数据。
            可将其用于获取碎片形式的数据值。值开始部分的偏移量为0。
            */
			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 C_DB_SERVER_IP   "192.168.186.128" //要连接的数据库IP
#define C_DB_SERVER_PORT 3306			   //要连接的端口号
#define C_DB_USERNAME    "admin"		   //mysql用户名
#define C_DB_PASSWORD    "admin"		   //mysql密码
#define C_DB_DEFAULT_DB  "C_DB"		       //连接后默认使用的数据库

#define FILE_MAX_SIZE    65*1024           //文件最大大小 65KB

//硬盘读取文件
int read_image(char *filename, char *buffer)
{
    //检测文件和文件二进制容器buffer是否存在
    if(filename == NULL || buffer == NULL) return -1;
    //定义文件指针 指向文件最开始 rb是以 读二进制 的方式打开
    FILE *fp = fopen(filename,"rb");
    if(fp == NULL)
    {
        printf("fopen failed\n");
        return -2;
    }

    //检测文件大小
    fseek(fp, 0, SEEK_END);     //把文件指针置到文件末尾
    int fileLen = ftell(fp);    //获取文件偏移量
    fseek(fp, 0 ,SEEK_SET);     //把文件指针置到文件开头

    //获取文件的二进制长度 
    //buffer为接收数据的地址 size为一个单元的大小 count为单元个数 stream为文件流。
    //返回实际读取的单元个数。如果小于count,则可能文件结束或读取出错;
    int fileSize = fread(buffer, 1, fileLen, fp);    
    //因为是每次读取一个单位,所以fileLen应该等于fileSize
    if(fileSize != fileLen)
    {
        printf("fread failed : %d\n", fileSize);
        return -3;
    }
    //如果读取到的大小超过了FILE_MAX_SIZE 也就是超过了数据库中blob的65KB则返回错误
    if(fileSize >= FILE_MAX_SIZE)
    {
        printf("file size is too big\n");
        return -4;
    }
    
    fclose(fp);
    return fileSize;
}

//硬盘写入文件
int write_image(char *filename, char *buffer, int fileLen)
{
    if(filename == NULL || buffer == NULL)  return -1;
    //定义文件指针 指向文件最开始 wb+是以 写二进制 的方式打开 +号表示如果没有这个文件便创建
    FILE *fp = fopen(filename,"wb+");
    if(fp == NULL)
    {
        printf("fopen failed");
        return -2;
    }
    //由二进制的方式写入文件 
    //buffer为接收数据的地址 size为一个单元的大小 count为单元个数 stream为文件流。
    //返回实际读取的单元个数。如果小于count,则可能文件结束或读取出错;
    int length = fwrite(buffer, 1, fileLen, fp);
    if(fileLen != length)
    {
        printf("fwite failed: %d\n",length);
        return -3;
    }
    fclose(fp);
    return length;
}

//文件以二进制方式写入数据库
int mysql_write(MYSQL *mysql, char *buffer, int fileLen, char *sql)
{
    if(mysql == NULL || buffer == NULL || fileLen <= 0 || sql == NULL) return -1;
    //初始化预处理语句句柄
    /*
    MYSQL_BIND该结构用于语句输入(发送给服务器的数据值)和输出(从服务器返回的结果值)。
    对于输入,它与mysql_stmt_bind_param()一起使用,用于将参数数据值绑定到缓冲区上,以供mysql_stmt_execute()使用。
    对于输出,它与mysql_stmt_bind_result()一起使用,用于绑定结果缓冲区,以便用于with mysql_stmt_fetch()以获取行。
    */
    MYSQL_STMT *stmt = mysql_stmt_init(mysql);
    //mysql_stmt_prepare 成功返回0
    int ret = mysql_stmt_prepare(stmt, sql, strlen(sql));
    if(ret)
    {
        printf("mysql_stmt_prepare : %s\n", mysql_error(mysql));
        return -2;
    }
    MYSQL_BIND param = {0};
    param.buffer_type = MYSQL_TYPE_BLOB;
    param.buffer = NULL;
    param.length = NULL;

    //mysql_stmt_bind_param()一起使用,用于将参数数据值绑定到缓冲区上,以供mysql_stmt_execute()使用
    ret = mysql_stmt_bind_param(stmt, &param);
    if(ret)
    {
        printf("mysql_stmt_bind_param : %s\n", mysql_error(mysql));
        return -3;
    }

    //mysql_stmt_send_long_data允许应用程序分段地(分块)将参数数据发送到服务器。
    //可以多次调用该函数,以便发送关于某一列的字符或二进制数据的不同部分,列必须是TEXT或BLOB数据类型之一。
    //第二个参数指明了与数据关联的参数。参数从0开始编号。
    //第三个参数是指向包含将要发送的数据的缓冲区的指针,第四个参数指明了缓冲区内的字节数。
    ret = mysql_stmt_send_long_data(stmt, 0, buffer, fileLen);
    if(ret)
    {
        printf("mysql_stmt_bind_param : %s\n", mysql_error(mysql));
        return -4;
    }

    //执行与语句句柄相关的预处理查询。
    ret = mysql_stmt_execute(stmt);
    if(ret)
    {
        printf("mysql_stmt_execute : %s\n", mysql_error(mysql));
        return -5;   
    }

    //预处理句柄关闭
    ret = mysql_stmt_close(stmt);
    if(ret)
    {
        printf("mysql_stmt_close : %s\n", mysql_error(mysql));
        return -6;
    }

    return ret;
}

//从数据库读取二进制文件
int mysql_read(MYSQL *mysql, char *buffer, int fileLen, char *sql)
{
    if(mysql == NULL || buffer == NULL || fileLen <= 0 || sql == NULL) return -1;
    MYSQL_STMT *stmt = mysql_stmt_init(mysql);
    int ret = mysql_stmt_prepare(stmt, sql, strlen(sql));
    if(ret)
    {
        printf("mysql_stmt_prepare : %s\n", mysql_error(mysql));
        return -2;
    }

    unsigned long total_length = 0; //计数器

    /*
    MYSQL_BIND该结构用于语句输入(发送给服务器的数据值)和输出(从服务器返回的结果值)。
    对于输入,它与mysql_stmt_bind_param()一起使用,用于将参数数据值绑定到缓冲区上,以供mysql_stmt_execute()使用。
    对于输出,它与mysql_stmt_bind_result()一起使用,用于绑定结果缓冲区,以便用于with mysql_stmt_fetch()以获取行。
    */
    MYSQL_BIND result = {0};
    result.buffer_type = MYSQL_TYPE_BLOB;
    result.length = &total_length;

    //mysql_stmt_bind_result()用于将结果集中的列与数据缓冲和长度缓冲关联(绑定)起来
    //当调用mysql_stmt_fetch()以获取数据时,
    //MySQL客户端/服务器协议会将绑定列的数据置于指定的缓冲区内。
    mysql_stmt_bind_result(stmt, &result);
    if(ret)
    {
        printf("mysql_stmt_bind_result : %s",mysql_error(mysql));
        return -3;
    }

    //执行 mysql_stmt_execute后结果在管道里面
    mysql_stmt_execute(stmt);
    if(ret)
    {
        printf("mysql_stmt_execute : %s\n", mysql_error(mysql));
        return -4;
    }

    //对于成功生成结果集的所有语句(SELECT、SHOW、DESCRIBE、EXPLAIN),
    //仅当打算对客户端的全部结果集进行缓冲处理时,必须调用mysql_stmt_store_result(),以便后续的mysql_stmt_fetch()调用能返回缓冲数据。
    ret = mysql_stmt_store_result(stmt);
    if(ret)
    {
        printf("mysql_stmt_store_result : %s\n", mysql_error(mysql));
		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;
            /*mysql_stmt_fetch_column从当前结果集行获取1列。
            参数二提供了应将数据置于其中的缓冲。
            参数三指明了将获取哪个列。第1列编号为0。
            参数四是数据值内的偏移量,将从该处开始检索数据。
            可将其用于获取碎片形式的数据值。值开始部分的偏移量为0。
            */
			mysql_stmt_fetch_column(stmt, &result, 0, start);
			start += result.buffer_length;
		}
	}

    mysql_stmt_close(stmt);

    return total_length;
}

int main(int argc,char *argv[])
{
	MYSQL mysql;
    if(mysql_init(&mysql) == NULL)
    {
        printf("%s",mysql_error(&mysql));
        return -1;
    }
    //参数依次为 mysql通道,
	//ip,用户名,
	//密码,数据库名,
	//端口号,socket(设置为NULL的时候表示不适用socket),客户端标识符
   	//mysql_real_connect函数若返回0表示则表示连接失败
    if(!mysql_real_connect(&mysql,C_DB_SERVER_IP,
    C_DB_USERNAME,C_DB_PASSWORD,
    C_DB_DEFAULT_DB,C_DB_SERVER_PORT,
    NULL,0))
    {
        printf("mysql_real_connect : %s\n",mysql_error(&mysql));
        return -2;
    }

    char buffer[FILE_MAX_SIZE] = {0};
    char sql[] = "INSERT C_IMG(ID, Images) VALUES(1,?)";
    // write_image("a.jpg", buffer, read_image(argv[1], buffer));

    int size = read_image(argv[1], buffer);
    
    mysql_write(&mysql, buffer, size, sql);
    
    char sql1[] = "SELECT Images FROM C_IMG WHERE ID='1'";
    memset(buffer, 0, FILE_MAX_SIZE);
    size = mysql_read(&mysql, buffer, size, sql1);

    write_image("out.jpg", buffer, size);
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值