[MySQL C API] 8. 데이터베이스에 이미지 저장하기(Inserting images into MySQL database)
[ 충남인력개발원 ] (2019)/┗TCP&IP 2019. 12. 1. 20:32
프로젝트 결과물
이미지는 바이너리(binary) 데이터이다.
MySQL은 바이너리 데이터를 저장할 때, BLOB 타입을 사용한다.
BLOP은 Binary Large Object의 약어이다.
BLOP 타입은 소팅이나 INDEX생성은 할 수 없다.
1. 테이블 추가
MySQL을 실행하고, 아래 명령어를 실행한다.
mysql> CREATE TABLE Images(Id INT PRIMARY KEY, Data MEDIUMBLOB);
MEDIUMBLOB 타입 데이터를 저장할 수 있는 Images 테이블을 생성한다.
BLOB 타입은 indexing을 할 수 없으므로,
INT형 Id 필드를 만들고 PRIMARY KEY로 선언한다.
MEDIUMBLOB 타입에서 이미지는 16MB까지 저장할 수 있다.
이외에도 TINYBLOB(255 Bytes), BLOB(64 KB), LONGBLOP(4GB) 타입이 있다.
2. 소스코드
그림 파일 하나를 Images 테이블에 저장하는 소스코드이다.
#include <my_global.h>
#include <mysql.h>
#include <string.h>
void finish_with_error(MYSQL *con)
{
fprintf(stderr, "%s \n", mysql_error(con));
mysql_close(con);
exit(1);
}
int main(int argc, char** argv)
{
// picture.jpg read binary(rb)모드로 파일을 연다.
// 실행파일과 같은 경로에 사진이 있어야 한다.
FILE *fp = fopen("picture.jpg", "rb");
if (fp == NULL) {
fprintf(stderr, "cannot open image file \n");
exit(1);
}
// 파일 포인터를 파일의 끝으로 옮긴다.
fseek(fp, 0, SEEK_END);
if (ferror(fp)) {
fprintf(stderr, "fseek() failed \n");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "cannot close file handler \n");
}
exit(1);
}
// 파일의 처음부터 파일 포인터가 가리키는 곳까지의 크기를 flen에 저장
int flen = ftell(fp);
if (flen == -1) {
perror("error occurred");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "cannot close file handler \n");
}
exit(1);
}
// 파일 포인터를 파일의 시작으로 옮김
fseek(fp, 0, SEEK_SET);
if (ferror(fp)) {
fprintf(stderr, "fseek() failed \n");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "cannot close file handler \n");
}
exit(1);
}
// 이미지 데이터를 저장할 배열 동적할당
char* data = (char *) malloc(sizeof(char) * (flen+1));
// 이미지 저장 함, 저장된 바이트 수가 size에 반환 됨
int size = fread(data, 1, flen, fp);
if (ferror(fp)) {
fprintf(stderr, "fread() failed \n");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "cannot close file handler \n");
}
exit(1);
}
// 파일 닫기
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "cannot close file handler \n");
}
// MYSQL 구조체 초기화
MYSQL *con = mysql_init(NULL);
if (con == NULL) {
fprintf(stderr, "mysql_init() failed \n");
exit(1);
}
// 서버 연결
if (mysql_real_connect(con, "localhost", "user01", "1q2w3e!", "testdb",
0, NULL, 0) == NULL)
{
finish_with_error(con);
}
char* chunk = (char *)malloc(sizeof(char) * (2*size + 1));
mysql_real_escape_string(con, chunk, data, size);
char *st = "INSERT INTO Images(Id, Data) VALUES(1, '%s')";
size_t st_len = strlen(st);
char *query = (char *)malloc(sizeof(char) * (st_len + 2*size + 1));
int len = snprintf(query, st_len + 2*size + 1, st, chunk);
if (mysql_real_query(con, query, len)) {
finish_with_error(con);
}
mysql_close(con);
exit(0);
}
char* chunk = (char *)malloc(sizeof(char) * (2*size + 1));
mysql_real_escape_string(con, chunk, data, size);
binary data에서 중간에 삽입된 종결문자를 없애는 과정이다.
종결문자에 \, ', ", Ctrl+z 등이 있지만 아래에서는 \만으로 설명한다.
mysql_real_escape_string()함수는 문자열의 종료를 의미하는 '\0' 앞에
\를 붙여 '\\0'으로 만든다.
또한 이 함수를 사용함으로써 SQL injection attack도 예방할 수 있다.
(SQL injection attack 참고 : https://opentutorials.org/module/411/3962)
\를 붙이는 작업 때문에 최악의 경우 원래의 데이터의 2배짜리 버퍼가 필요하다
따라서 data버퍼 size의 2배 +1 만큼의 길이를 chunk에 할당한다.
char *st = "INSERT INTO Images(Id, Data) VALUES(1, '%s')";
size_t st_len = strlen(st);
쿼리문을 구성하고, strlen 함수를 통해 이 쿼리문의 길이를 구해 st_len에 저장.
char *query = (char *)malloc(sizeof(char) * (st_len + 2*size + 1));
int len = snprintf(query, st_len + 2*size + 1, st, chunk);
최종적으로 MySQL 서버에 보낼 쿼리문을 구성하는 과정이다.
앞서 구했던 '쿼리문의 길이'와 'chunk의 길이'를 합한
'st_len + 2*size + 1'만큼을 동적할당 한다.
그리고 snprintf를 통해서 query버퍼에 st와 chunk를 가지고
이미지 저장을 요청하는 쿼리문을 완성한다.
if (mysql_real_query(con, query, len)) {
finish_with_error(con);
}
이전까지는 mysql_query() 함수를 통해 데이터를 넣었었다.
mysql_query() 함수는 binary 데이터가 포함된 쿼리를 처리하지 못한다.
여기서는 binary 데이터를 취급하기 때문에 mysql_real_query() 함수를 사용한다.
아래 링크를 참고하여 번역 및 수정함
다른 참조
http://jidolstar.tistory.com/681
http://www.mysqlkorea.com/sub.html?mcode=manual&scode=01&m_no=21872&cat1=22&cat2=596&cat3=606&lang=k
출처: https://www.molllab.com/15 [mollLab]
출처: https://www.molllab.com/15 [mollLab]
'[ 충남인력개발원 ] (2019) > ┗TCP&IP' 카테고리의 다른 글
Uduntu window 통신 (0) | 2020.01.13 |
---|---|
MYSQL의 모든것 (0) | 2020.01.08 |
프로젝트 분석 (0) | 2019.11.29 |
mysql 더 설치하기 ㅎㅎ... (0) | 2019.11.28 |
Mysql 리눅스 연동하기 (0) | 2019.11.28 |