通过FMDB从Sqlite存储和检索压缩文本为BLOB

时间:2014-11-03 10:57:55

标签: ios objective-c sqlite fmdb

我正在尝试将文本数据存储为压缩blob。

    [db executeUpdate:@"create table blobTable (a text, b blob)"];
    [db executeUpdate:@"insert into blobTable (a, b) values (?, compressMe('random text string'))", @"lord of the rings"];

然后我尝试使用以下方法查询它们:

FMResultSet *rs = [db executeQuery:@"select uncompressMe(b) as k from blobTable where a = ?", @"lord of the rings"];

其中compressMe和uncompressMe定义为:

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];

[queue inDatabase:^(FMDatabase *adb) {
    [adb makeFunctionNamed:@"compressMe" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
        if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) {

            @autoreleasepool {

                const char *c = (const char *)sqlite3_value_text(aargv[0]);

                NSString *s = [NSString stringWithUTF8String:c];

                NSLog(@"string to compress: %@", s);

                NSData *compressedBody = [self gzipDeflate:[s dataUsingEncoding:NSUTF8StringEncoding]];

                sqlite3_result_blob(context, (__bridge const void *)(compressedBody), [compressedBody length], nil);
            }
        }
        else {
            NSLog(@"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
            sqlite3_result_null(context);
        }
    }];
}];


[queue inDatabase:^(FMDatabase *adb) {
    [adb makeFunctionNamed:@"uncompressMe" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
        if (sqlite3_value_type(aargv[0]) == SQLITE_BLOB) {

            @autoreleasepool {

                NSLog(@"inside uncompressMe");

                NSUInteger len = sqlite3_value_bytes(aargv[0]);
                Byte *byteData = (Byte*)malloc(len);
                memcpy(byteData, sqlite3_value_blob(aargv[0]), len);

                NSData *data = [[NSData alloc] initWithBytes:byteData length:len];

                NSData *deflatedBody = [self gzipInflate:data];
                NSString *deflatedString = [[NSString alloc] initWithData:deflatedBody encoding:NSUTF8StringEncoding];
                sqlite3_result_text(context, (__bridge const void *)(deflatedString), -1, SQLITE_UTF8);
            }
        }
        else {
            NSLog(@"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
            sqlite3_result_null(context);
        }
    }];
}];

为了完整起见,zip函数定义如下:

- (NSData *)gzipInflate:(NSData*)data
{
    if ([data length] == 0) return data;

    unsigned full_length = [data length];
    unsigned half_length = [data length] / 2;

    NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
    BOOL done = NO;
    int status;

    z_stream strm;
    strm.next_in = (Bytef *)[data bytes];
    strm.avail_in = [data length];
    strm.total_out = 0;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;

    if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
    while (!done)
    {
        // Make sure we have enough room and reset the lengths.
        if (strm.total_out >= [decompressed length])
            [decompressed increaseLengthBy: half_length];
        strm.next_out = [decompressed mutableBytes] + strm.total_out;
        strm.avail_out = [decompressed length] - strm.total_out;

        // Inflate another chunk.
        status = inflate (&strm, Z_SYNC_FLUSH);
        if (status == Z_STREAM_END) done = YES;
        else if (status != Z_OK) break;
    }
    if (inflateEnd (&strm) != Z_OK) return nil;

    // Set real length.
    if (done)
    {
        [decompressed setLength: strm.total_out];
        return [NSData dataWithData: decompressed];
    }
    else return nil;
}

- (NSData *)gzipDeflate:(NSData*)data
{
    if ([data length] == 0) return data;

    z_stream strm;

    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.total_out = 0;
    strm.next_in=(Bytef *)[data bytes];
    strm.avail_in = [data length];

    // Compresssion Levels:
    //   Z_NO_COMPRESSION
    //   Z_BEST_SPEED
    //   Z_BEST_COMPRESSION
    //   Z_DEFAULT_COMPRESSION

    if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;

    NSMutableData *compressed = [NSMutableData dataWithLength:16384];  // 16K chunks for expansion

    do {

        if (strm.total_out >= [compressed length])
            [compressed increaseLengthBy: 16384];

        strm.next_out = [compressed mutableBytes] + strm.total_out;
        strm.avail_out = [compressed length] - strm.total_out;

        deflate(&strm, Z_FINISH);

    } while (strm.avail_out == 0);

    deflateEnd(&strm);

    [compressed setLength: strm.total_out];
    return [NSData dataWithData:compressed];
}

但是,uncompressMe功能似乎没有成功。它在Inflate方法中失败,该方法总是返回nil。有什么想法我可能做错了吗?我已检查以确保blob列确实已填充。

1 个答案:

答案 0 :(得分:0)

我觉得问题来自以下代码:

            NSUInteger len = sqlite3_value_bytes(aargv[0]);
            Byte *byteData = (Byte*)malloc(len);
            memcpy(byteData, sqlite3_value_blob(aargv[0]), len);

            NSData *data = [[NSData alloc] initWithBytes:byteData length:len];

尝试从数据库加载NSData,而不是创建字节变量并复制它。

const void *bytes = sqlite3_column_blob(statement, 3);
NSData *data = [[NSData alloc] initWithBytes:bytes length:size];