일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- web
- 코딜리티
- 아두이노 pro mini
- Mysql c API
- Arduino pin map
- ubuntu
- Arduino
- mysql api
- database
- html input
- 아두이노 프로미니
- mysql c
- 아두이노 핀 맵
- 아두이노
- 라즈베리파이
- Raspberry Pi
- Arduino pin
- 데이터베이스
- vm
- Virtual Box
- HTML
- 아두이노 핀맵
- 웹 프로그래밍
- 아두이노 와이파이
- 아두이노 wifi
- Codility
- MySQL
- wifi멀티탭
- 알고리즘
- 아두이노 핀
- Today
- Total
offfff
[MySQL C API] 8. 데이터베이스에 이미지 저장하기(Inserting images into MySQL database) 본문
[MySQL C API] 8. 데이터베이스에 이미지 저장하기(Inserting images into MySQL database)
offfff 2016. 8. 18. 09:00이미지는 바이너리(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://zetcode.com/db/mysqlc/
다른 참조
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
'프로그래밍' 카테고리의 다른 글
HTML 소개(HTML Introduction) (0) | 2016.08.20 |
---|---|
[MySQL C API] 9. 데이터베이스에 저장된 이미지 가져오기(Selecting images from MySQL database) (0) | 2016.08.19 |
[MySQL C API] 7. 한 쿼리로 다중구문 수행하기(Multiple statements) (0) | 2016.08.17 |
[MySQL C API] 6. 컬럼 이름 가져오기(Column header) (0) | 2016.08.16 |
[MySQL C API] 5. 마지막 삽입행 ID 알아내기(Last inserted row id) (0) | 2016.08.15 |