2011-03-17 4 views
3

sqlite에서 nsdata로 일부 이미지를 캐싱하려고하는데 sqlite3_exec 및 원시 SQL 문자열을 사용하여 바이트 배열을 삽입하려고하면 문제가 발생합니다 (NSString으로)Object-c에서 sqlite3_exec을 사용하여 BLOB로 UIImage를 삽입하는 방법

NSData *imgData = UIImagePNGRepresentation(img); 
NSString* sql = [NSString stringWithFormat:@"INSERT INTO persistedimg (imgx,idvalx) VALUES (%@,'%@')", imgData, idValue]; 
rc = sqlite3_exec(db, [sql UTF8String], callbackFunction, (void*)contextObject, &zErrMsg); 

위의 문제는 바이트 대신 SQL 문자열에 NSData를 직접 추가하는 것입니다.

나는이

... [imgData bytes], [imgData length] 

같은 것을하고 싶어하지만 방법과 같은 전형적인 "_bind_blob"를 사용하지 않는 때문에 원시 문자열 승/그것을 할 방법을 잘 모르겠어요

나는 이미지 삽입을 지원하기 위해 승 스틱/단순히 새로운 방법을 쓰고 싶은 래퍼를 사용하고 업데이트

/쿼리

명령

아래 내 전체 래퍼 클래스가 지금까지

**

#import "SQLiteAccess.h" 
#import <sqlite3.h> 

@implementation SQLiteAccess 

+ (NSString *)pathToDB { 
    NSString *dbName = @"test123"; 
    NSString *originalDBPath = [[NSBundle mainBundle] pathForResource:dbName ofType:@"db"]; 
    NSString *path = nil; 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *appSupportDir = [paths objectAtIndex:0]; 
    NSString *dbNameDir = [NSString stringWithFormat:@"%@/test123", appSupportDir]; 
    NSFileManager *fileManager = [NSFileManager defaultManager]; 
    BOOL isDir = NO; 
    BOOL dirExists = [fileManager fileExistsAtPath:dbNameDir isDirectory:&isDir]; 
    NSString *dbPath = [NSString stringWithFormat:@"%@/%@.db", dbNameDir, dbName]; 
    if(dirExists && isDir) { 
    BOOL dbExists = [fileManager fileExistsAtPath:dbPath]; 
    if(!dbExists) { 
     NSError *error = nil; 
     BOOL success = [fileManager copyItemAtPath:originalDBPath toPath:dbPath error:&error]; 
     if(!success) { 
     NSLog(@"error = %@", error); 
     } else { 
     path = dbPath; 
     } 
    } else { 
     path = dbPath; 
    } 
    } else if(!dirExists) { 
    NSError *error = nil; 
    BOOL success =[fileManager createDirectoryAtPath:dbNameDir attributes:nil]; 
    if(!success) { 
     NSLog(@"failed to create dir"); 
    } 
    success = [fileManager copyItemAtPath:originalDBPath toPath:dbPath error:&error]; 
    if(!success) { 
     NSLog(@"error = %@", error); 
    } else { 
     path = dbPath; 
    } 
    } 
    return path; 
} 

+ (NSNumber *)executeSQL:(NSString *)sql withCallback:(void *)callbackFunction context:(id)contextObject { 
    NSString *path = [self pathToDB]; 
    sqlite3 *db = NULL; 
    int rc = SQLITE_OK; 
    NSInteger lastRowId = 0; 
    rc = sqlite3_open([path UTF8String], &db); 
    if(SQLITE_OK != rc) { 
    NSLog(@"Can't open database: %s\n", sqlite3_errmsg(db)); 
    sqlite3_close(db); 
    return nil; 
    } else { 
    char *zErrMsg = NULL; 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    rc = sqlite3_exec(db, [sql UTF8String], callbackFunction, (void*)contextObject, &zErrMsg); 
    if(SQLITE_OK != rc) { 
     NSLog(@"Can't run query '%@' error message: %s\n", sql, sqlite3_errmsg(db)); 
     sqlite3_free(zErrMsg); 
    } 
    lastRowId = sqlite3_last_insert_rowid(db); 
    sqlite3_close(db); 
    [pool release]; 
    } 
    NSNumber *lastInsertRowId = nil; 
    if(0 != lastRowId) { 
    lastInsertRowId = [NSNumber numberWithInteger:lastRowId]; 
    } 
    return lastInsertRowId; 
} 

static int singleRowCallback(void *queryValuesVP, int columnCount, char **values, char **columnNames) { 
    NSMutableDictionary *queryValues = (NSMutableDictionary *)queryValuesVP; 
    int i; 
    for(i=0; i<columnCount; i++) { 
    [queryValues setObject:values[i] ? [NSString stringWithFormat:@"%s",values[i]] : [NSNull null] 
        forKey:[NSString stringWithFormat:@"%s", columnNames[i]]]; 
    } 
    return 0; 
} 

+ (NSString *)selectOneValueSQL:(NSString *)sql { 
    NSMutableDictionary *queryValues = [NSMutableDictionary dictionary]; 
    [self executeSQL:sql withCallback:singleRowCallback context:queryValues]; 
    NSString *value = nil; 
    if([queryValues count] == 1) { 
     value = [[queryValues objectEnumerator] nextObject]; 
    } 
    return value; 
} 

+ (NSNumber *)insertWithSQL:(NSString *)sql { 
    sql = [NSString stringWithFormat:@"BEGIN TRANSACTION; %@; COMMIT TRANSACTION;", sql]; 
    return [self executeSQL:sql withCallback:NULL context:NULL]; 
} 

+ (void)updateWithSQL:(NSString *)sql { 
    sql = [NSString stringWithFormat:@"BEGIN TRANSACTION; %@; COMMIT TRANSACTION;", sql]; 
    [self executeSQL:sql withCallback:NULL context:nil]; 
} 

@end 

입니다 **

이 솔루션에 어떤 도움이 큰 것!

+0

나는 현상금을 거절했으나 개인적으로 필요한 경우 2 주 동안 두 가지 현상금을 지급하는 것이 더 나은 결과를 얻을 것이라고 생각합니다. –

답변

10

여기서는 SQLite3 API를 너무 단순화하려고 노력하고 있습니다. API는 텍스트 SQL 쿼리를 실행하기위한 것이 아닙니다. 준비된 명령문과 바인드 매개 변수가 존재합니다. 바이너리 데이터를 문자열에 삽입해서는 안됩니다. 그것은 문제를 요구하는 것입니다. 특히 바이너리 데이터에 null이있는 경우에 특히 그렇습니다.

얼룩을 삽입하려면 sqlite3_bind_blobsqlite3_prepare_v2을 사용해야합니다. BLOB를 바인드 할 때 BLOB 데이터로 [imgData bytes]도 사용해야합니다.

API를 재구성하여 특정 이미지 캐싱 유스 케이스에 대해 이러한 종류의 작업을보다 쉽게 ​​수행 할 수있는 도움이 필요하십니까?

편집

여기에 바이너리 데이터를 삽입하는 바인딩을 사용하여 간단한 예입니다. 두 개의 열이있는 my_table이라는 표가 있다고 가정하십시오. nameVARCHAR이고 dataBLOB입니다. 테스트 나 테스트를하지 않았으므로 오타 나 오류가있을 수 있습니다.

sqlite3 *database; 

// Open a connection to the database given its file path. 
if (sqlite3_open("/path/to/sqlite/database.sqlite3", &database) != SQLITE_OK) { 
    // error handling... 
} 

// Construct the query and empty prepared statement. 
const char *sql = "INSERT INTO `my_table` (`name`, `data`) VALUES (?, ?)"; 
sqlite3_stmt *statement; 

// Prepare the data to bind. 
NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:@"something"]); 
NSString *nameParam = @"Some name"; 

// Prepare the statement. 
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) { 
    // Bind the parameters (note that these use a 1-based index, not 0). 
    sqlite3_bind_text(statement, 1, nameParam); 
    sqlite3_bind_blob(statement, 2, [imageData bytes], [imageData length], SQLITE_STATIC); 
    // SQLITE_STATIC tells SQLite that it doesn't have to worry about freeing the binary data. 
} 

// Execute the statement. 
if (sqlite3_step(statement) != SQLITE_DONE) { 
    // error handling... 
} 

// Clean up and delete the resources used by the prepared statement. 
sqlite3_finalize(statement); 

// Now let's try to query! Just select the data column. 
const char *selectSql = "SELECT `data` FROM `my_table` WHERE `name` = ?"; 
sqlite3_stmt *selectStatement; 

if (sqlite3_prepare_v2(database, selectSql, -1, &selectStatement, NULL) == SQLITE_OK) { 
    // Bind the name parameter. 
    sqlite3_bind_text(selectStatement, 1, nameParam); 
} 

// Execute the statement and iterate over all the resulting rows. 
while (sqlite3_step(selectStatement) == SQLITE_ROW) { 
    // We got a row back. Let's extract that BLOB. 
    // Notice the columns have 0-based indices here. 
    const void *blobBytes = sqlite3_column_blob(selectStatement, 0); 
    int blobBytesLength = sqlite3_column_bytes(selectStatement, 0); // Count the number of bytes in the BLOB. 
    NSData *blobData = [NSData dataWithBytes:blobBytes length:blobBytesLength]; 
    NSLog("Here's that data!\n%@", blobData); 
} 

// Clean up the select statement 
sqlite3_finalize(selectStatement); 

// Close the connection to the database. 
sqlite3_close(database); 
+1

이미 SQLite에 대한 몇 가지 좋은 래퍼가 있지만 이미 자신의 롤링 대신 사용할 수 있습니다. 이 질문을 확인하십시오 : http://stackoverflow.com/questions/640885/best-cocoa-objective-c-wrapper-library-for-sqlite-on-iphone –

+0

나는이 방법을 결합하는 방법을 찾고 있다고 가정합니다. 이미 제 자리에있는 것. 처음부터 끝까지 삽입 및 쿼리를 보여주는 간단한 예제를 제공 할 수 있습니까? 이 가이드를 따르기 시작했지만 그 저자 src에 무슨 문제가 있었는지 http://www.iphonesdkarticles.com/2009/02/sqlite-tutorial-saving-images-in.html –

+0

추가했습니다. –

관련 문제