使用 C 语言操作 MySQL 实现图片写入与读取

在实际项目中,常常需要将图片或文件以二进制方式存储至数据库中,并能正确读取还原为文件。本文以 C 语言配合 MySQL C API 为例,完整演示如何实现将一张 JPG 图片写入数据库并再读出生成新图片文件的过程。

项目背景

我们使用如下表结构:

-- 创建用户信息表
CREATE TABLE TBL_USER (
    U_ID INT PRIMARY KEY AUTO_INCREMENT,  
    -- 用户编号,整型,主键,自动递增,系统自动分配唯一ID
    U_NAME VARCHAR(50),                   
    -- 用户名称,字符串类型,最大50个字符,用于存储用户名
    U_GENGDER VARCHAR(10),                
    -- 用户性别,字符串类型,最大10个字符,例如 'man'、'woman' 等
    U_IMG LONGBLOB                        
    -- 用户图片,超大二进制对象类型,最大可存储4GB数据,适合存储图片、文件、音视频等二进制数据
);

其中 U_IMG 为图片字段。注意:字段类型需为 LONGBLOB 才能容纳较大图片(最大可达 4GB)。若你使用的是 BLOB(最大 64KB),遇到较大图片会报错甚至发生栈溢出(stack smashing)!

完整流程

┌───────────┐
│  1. 连接数据库   
└───────────┘
          ↓
┌────────────────────┐
│  2. 插入用户普通信息(不含图片) 
└────────────────────┘
          ↓
┌─────────────────────┐
│  3. 查询用户信息并打印(验证表结构) 
└─────────────────────┘
          ↓
┌──────────────────┐
│  4. 读取本地图片到内存 buffer 
└──────────────────┘
          ↓
┌───────────────────────────┐
│  5. 通过 mysql_stmt 预处理存入图片 
└───────────────────────────┘
          ↓
┌────────────────────────┐
│  6. 从数据库读取图片二进制数据到内存 
└────────────────────────┘
          ↓
┌──────────────────────┐
│  7. 将内存数据写入新图片文件,还原图片 
└──────────────────────┘

第一步:程序初始化并连接 MySQL 数据库

mysql_init(&mysql);   // 初始化句柄
mysql_real_connect(&mysql, IP, USER, PWD, DB, PORT, NULL, 0);  // 连接数据库
  • mysql_init 初始化连接

  • mysql_real_connect 建立与 MySQL 的 TCP 连接

第二步:插入普通用户数据

mysql_real_query(&mysql, SQL_INSERT_TBL_USER, strlen(SQL_INSERT_TBL_USER));

执行普通 SQL 语句,插入一条用户数据(不含图片)

第三步:查询表内容

king_mysql_select(&mysql);

自定义查询函数:

  • 调用 mysql_real_query 执行 SELECT * FROM TBL_USER

  • mysql_store_result 把结果缓存到客户端

  • mysql_fetch_row 遍历每一行,打印每个字段内容

第四步:读取本地图片到内存 buffer

char buffer[FILE_IMAGE_LENGTH] = {0};
int length = read_image("2.jpg", buffer);
if(length < 0) goto Exit;

作用:

  • 通过 fopen + fread 将本地 2.jpg 读入内存数组 buffer

  • 返回实际读取的图片字节数

关键点:

  • 确保 buffer 大小足够大(FILE_IMAGE_LENGTH 需大于图片实际大小)

  • 若图片过大,BLOB字段类型必须调整(详见注意事项)

第五步:通过 mysql_stmt 预处理存入图片

mysql_write(&mysql, buffer, length);

作用:

  • 使用 MYSQL_STMT 结构体准备预处理 SQL

  • 通过参数绑定与 mysql_stmt_send_long_data 安全传输图片二进制数据

关键点:

  • SQL 语句用 ? 占位符

  • MYSQL_BIND 结构体指定 MYSQL_TYPE_LONG_BLOB 类型

  • 使用 mysql_stmt_send_long_data 分块发送大数据,避免 SQL 注入

第六步:从数据库读取图片二进制数据到内存

length = mysql_read(&mysql, buffer, FILE_IMAGE_LENGTH);

作用:

  • 预处理 SELECT 语句获取图片字段 U_IMG

  • 循环使用 mysql_stmt_fetch_column 按块读取数据到 buffer

关键点:

  • 需要提前设置 result.length 存储总字节数

  • 注意 offset 偏移计算,确保数据完整拼接到内存

第七步:将内存数据写入新图片文件,还原图片

write_image("a.jpg", buffer, length);

作用:

  • 调用 fopen + fwrite 将内存数据写入 a.jpg

  • 实现数据库图片数据还原为本地文件

关键点:

  • 确保内存数据完整,文件长度正确

  • 写入成功后可以直接打开 a.jpg 查看效果

完整代码

#include 
#include 
#include 

#define KING_DB_SERVER_IP   "10.0.0.129"      // 数据库IP
#define KING_DB_SERVER_PORT 3306              // 数据库端口
#define KING_DB_USERNAME    "admin"           // 数据库用户名
#define KING_DB_PASSWORD    "123456"          // 数据库密码
#define KING_DB_DEFAULTDB   "KING_DB"         // 默认库名

// 插入普通用户信息
#define SQL_INSERT_TBL_USER     "INSERT TBL_USER(U_NAME,U_GENGDER) VALUES('charon','man');"
// 查询所有用户信息
#define SQL_SELECT_TBL_USER     "SELECT * FROM TBL_USER;"
// 调用存储过程删除用户
#define SQL_DELETE_TBL_USER     "CALL PROC_DELETE_USER('charon')"
// 插入含图片的用户信息(U_IMG字段用 ? 占位)
#define SQL_INSERT_IMG_USER     "INSERT TBL_USER(U_NAME,U_GENGDER,U_IMG) VALUES('charon','man',?)"
// 查询图片字段
#define SQL_SELECT_IMG_USER     "SELECT U_IMG FROM TBL_USER WHERE U_NAME = 'charon';"

#define FILE_IMAGE_LENGTH       (64*1024*10)  // 设定图片缓冲区大小,视实际图片大小调整

/**
 * 查询并打印所有用户信息
 */
int king_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));
        return -1;
    }

    MYSQL_RES *res = mysql_store_result(handle);
    if(res == NULL){
        printf("mysql_store_result : %s\n", mysql_error(handle));
        return -2;
    }

    int rows = mysql_num_rows(res);
    printf("rows: %d\n", rows);

    int fields = mysql_num_fields(res);
    printf("fields: %d\n", fields);

    MYSQL_ROW row;
    while((row = mysql_fetch_row(res))){
        for(int i = 0; i < fields; i++){
            printf("%s\t", row[i]);
        }
        printf("\n");
    }

    mysql_free_result(res);
    return 0;
}

/**
 * 读取本地图片文件到内存
 */
int read_image(char *filename, char *buffer){
    if(filename == NULL || buffer == NULL) return -1;
    FILE *fp = fopen(filename, "rb");
    if(fp == NULL){
        printf("open file %s failed\n", filename);
        return -2;
    }

    fseek(fp, 0, SEEK_END);
    int length = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    int size = fread(buffer, 1, length, fp);
    if(size != length){
        printf("read file %d failed\n", size);
        return -3;
    }

    fclose(fp);
    return size;
}

/**
 * 将内存中的数据写入本地图片文件
 */
int write_image(char *filename, char *buffer, int length){
    if(filename == NULL || buffer == NULL || length <= 0) return -1;
    FILE *fp = fopen(filename, "wb+");
    if(fp == NULL){
        printf("open file %s failed\n", filename);
        return -2;
    }
    int size = fwrite(buffer, 1, length, fp);
    if(size != length){
        printf("write file %d failed\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);
    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 = buffer;   // 数据指针
    param.is_null = 0;
    param.length = NULL;     // 长度通过 send_long_data 传递

    ret = mysql_stmt_bind_param(stmt, ¶m);
    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);
    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);
    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;  // 逐字节读取
            mysql_stmt_fetch_column(stmt, &result, 0, start);
            start += result.buffer_length;
        }
    }

    mysql_stmt_close(stmt);
    return total_length;
}

/**
 * 主函数:整体流程控制
 */
int main() {
    MYSQL mysql;
    if(NULL == mysql_init(&mysql)){
        printf("mysql_init : %s\n", 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)){
        printf("mysql_real_connect : %s\n", mysql_error(&mysql));
        goto Exit;
    }

    printf("case : mysql --> select\n");
    if(mysql_real_query(&mysql, SQL_INSERT_TBL_USER, strlen(SQL_INSERT_TBL_USER))){
        printf("mysql_real_query : %s\n", mysql_error(&mysql));
        goto Exit;
    }
    king_mysql_select(&mysql);

    printf("case : mysql --> read image and write mysql\n");
    char buffer[FILE_IMAGE_LENGTH] = {0};
    int length = read_image("2.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;
}

https://github.com/0voice

你可能感兴趣的:(mysql,数据库)