I am unable to unzip a zip file saved in Sqlite As Blob , after i query the db it returns me a NSData . I tried the inflate method but it returns this error "Decompression of data failed with code -3". Any opinions??
Thanks !
This is the code
NSData *content = [[NSData alloc] initWithBytes:sqlite3_column_blob(statement, 3) length:sqlite3_column_bytes(statement, 3)];
content = [NSData gzipInflate:content];
/// Ns data category
#implementation NSData (Unzip)
+ (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;
}
#end
Related
I am trying to stream AAC encoded audio data received as CMSampleBuffer. AudioConverterFillComplexBuffer returns 0 status code.
But after passing this data to my FFMPEG HLSWriter audio is not correctly saved (short truncated signals).
Below is the sample code.
static OSStatus inInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData)
{
KFAACEncoder *encoder = (__bridge KFAACEncoder *)(inUserData);
UInt32 requestedPackets = *ioNumberDataPackets;
if(requestedPackets > encoder.cycledBuffer.size / 2)
{
//NSLog(#"PCM buffer isn't full enough!");
*ioNumberDataPackets = 0;
return -1;
}
static size_t staticBuffSize = 4096;
static void* staticBuff = nil;
if(!staticBuff)
{
staticBuff = malloc(staticBuffSize);
}
size_t outputBytesSize = requestedPackets * 2;
[encoder.cycledBuffer popToBuffer:staticBuff bytes: outputBytesSize];
ioData->mBuffers[0].mData = staticBuff;
ioData->mBuffers[0].mDataByteSize = (int)outputBytesSize;
*ioNumberDataPackets = ioData->mBuffers[0].mDataByteSize / 2;
return noErr;
}
- (void) encodeSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
CFRetain(sampleBuffer);
dispatch_async(self.encoderQueue,
^{
if (!_audioConverter)
{
[self setupAACEncoderFromSampleBuffer:sampleBuffer];
}
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
CFRetain(blockBuffer);
size_t pcmBufferSize = 0;
void* pcmBuffer = nil;
OSStatus status = CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &pcmBufferSize, &pcmBuffer);
[_cycledBuffer push:pcmBuffer size:pcmBufferSize];
NSError *error = nil;
if (status != kCMBlockBufferNoErr)
{
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
}
memset(_aacBuffer, 0, _aacBufferSize);
AudioBufferList outAudioBufferList = {0};
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = 1;
outAudioBufferList.mBuffers[0].mDataByteSize = _aacBufferSize;
outAudioBufferList.mBuffers[0].mData = _aacBuffer;
AudioStreamPacketDescription *outPacketDescription = NULL;
UInt32 ioOutputDataPacketSize = 1;
status = AudioConverterFillComplexBuffer(_audioConverter,
inInputDataProc,
(__bridge void *)(self),
&ioOutputDataPacketSize,
&outAudioBufferList,
NULL);
NSData *data = nil;
if (status == 0)
{
NSData *rawAAC = [NSData dataWithBytes:outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];
if (_addADTSHeader) {
NSData *adtsHeader = [self adtsDataForPacketLength:rawAAC.length];
NSMutableData *fullData = [NSMutableData dataWithData:adtsHeader];
[fullData appendData:rawAAC];
data = fullData;
} else {
data = rawAAC;
}
} else {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
}
if (self.delegate) {
KFFrame *frame = [[KFFrame alloc] initWithData:data pts:pts];
NSLog(#"Bytes of data %lu", (unsigned long)data.length);
dispatch_async(self.callbackQueue, ^{
[self.delegate encoder:self encodedFrame:frame];
});
}
CFRelease(sampleBuffer);
CFRelease(blockBuffer);
});
}
I am using rc4 algorithm to encrypt and decrypt a video. I am taking 64 bytes and encrypting.
as the same way I am doing the decryption also but my video is not playing.Anyone have idea about this???
- (void)encryptOnFly:(UInt8*)buffer bytesRead:(NSInteger)bytesRead
{
for(long long i=0;i<bytesRead;i++)
{
[self push:buffer[i]];
if([self isFull])
{
unsigned char *buf11=[self retrieveArray];
unsigned char buf1[64];
for(int j=0;j<64;j++)
{
buf1[j]=*buf11++;
}
NSData *data = [NSData dataWithBytes:buf1 length:sizeof(buf1)];
aesCrypt = [data RC4EncryptDecryptWithKey:#"ABCD" operation:kCCEncrypt];
// aesCrypt=[data EncryptAES:#"akey"];
const uint8_t *bytes = (const uint8_t*)[aesCrypt bytes];
for(int j=0;j<63;j++)
{
buf1[j]=*bytes++;
}
[[self fileDownloadOutputStream] write:buf1 maxLength:64];
[self empty];
}
}
}
this is the code to handle a file which is divisible by 64....
switch (type) {
case kCFStreamEventHasBytesAvailable:
[self handleBytesAvailable];
break;
case kCFStreamEventEndEncountered:
NSLog(#"REACHED :: -- > kCFStreamEventEndEncountered");
if (isEncryptionRequired)
{
if(pointer>-1)
{
remBytes=pointer+1;
int tmp=64-remBytes;
for(int k=0;k<tmp;k++)
{
if (pointer!=62)
{
if(![self isFull])
[self push:0];
}
else
if(![self isFull])
[self push:remBytes];
if ([self isFull])
{
unsigned char *buf11= [self retrieveArray];
unsigned char buf1[64];
for(int j=0;j<64;j++)
{
buf1[j]=*buf11++;
}
NSData *data = [NSData dataWithBytes:buf1 length:sizeof(buf1)];
aesCrypt = [data RC4EncryptDecryptWithKey:#"ABCD" operation:kCCEncrypt];
// aesCrypt=[data EncryptAES:#"akey"];
const uint8_t *bytes = (const uint8_t*)[aesCrypt bytes];
for(int j=0;j<63;j++)
{
buf1[j]=*bytes++;
}
[[self fileDownloadOutputStream] write:buf1 maxLength:16];
[self empty];
}}
}
else
{
unsigned char extrabuf=0;
[[self fileDownloadOutputStream] write:extrabuf maxLength:1];
///add
}
}
else
{
[[self fileDownloadOutputStream] write:buf maxLength:16];
}
[self handleStreamComplete];
break;
case kCFStreamEventErrorOccurred:
[self handleStreamError];
break;
default:
break;
}
this is the method to decrypt a file......
- (void)decryptFile:(NSString *)OMfileUrl
{
// NSLog(#"omurl::%#",OMfileUrl);
#try
{
__block NSURL *movieURL;
isOffline = YES;
{
const char *srcfile = [OMfileUrl cStringUsingEncoding:NSUTF8StringEncoding];
source = fopen(srcfile, "rb");
filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"assetforplay.mp4"];
AppDelegate *delegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
delegate.filePath = filePath;
filePathCharArr = [filePath cStringUsingEncoding:NSUTF8StringEncoding];
remove([filePath cStringUsingEncoding:NSUTF8StringEncoding]);
if (source)
{
destination = fopen(filePathCharArr, "wb");
cancelDecryption = NO;
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
unsigned char temp[64];
long long totalBytes=0;
fseek(source, 0L, SEEK_END);
long long sz = ftell(source);
fseek(source, 0L, SEEK_SET);
int percentage=0,prevPercentage=0;
int xx=sz%64;
percentageLabel.hidden = NO;
if (xx==0)
{
NSData *data;
while (totalBytes<=(sz-64))
{
if(!cancelDecryption)
{
percentage =((double)totalBytes/(double)sz)*100;
if (percentage!=prevPercentage)
{
prevPercentage=percentage;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[percentageLabel setText:[NSString stringWithFormat:#"%i%%",percentage]];
}];
}
fread(buffer,1, 64, source);
// memcpy( buf, buffer, 16);
// memcpy( keyoffline, [delegate secretKey], 16 + 0 * 8);
// aes_set_key( &ctx, keyoffline, 128 + 0 * 64);
// aes_decrypt( &ctx, buf, buf );
data = [NSData dataWithBytes:buffer length:64];
aesCrypt = [data RC4EncryptDecryptWithKey:#"ABCD" operation:kCCDecrypt];
// aesCrypt=[data DecryptAES:#"akey"];
const uint8_t *bytes = (const uint8_t*)[aesCrypt bytes];
for(int j=0;j<63;j++)
{
buf[j]=*bytes++;
}
fwrite(buf, 1, 64, destination);
totalBytes=totalBytes+64;
}
else
{
percentageLabel.text=#"";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *err;
if (![fileManager removeItemAtPath:filePath error:&err])
{
NSLog(#"Unable to delete the essence. Reason:%#",[err description]);
}
break;
}
}
fseek(source, -64, SEEK_END);
sz = ftell(source);
// fread(buffer, 1, 16, source);
// memcpy( buf, buffer, 16);
// memcpy( keyoffline, [delegate secretKey], 16 + 0 * 8);
// aes_set_key( &ctx, keyoffline, 128 + 0 * 64);
// aes_decrypt( &ctx, buf, buf );
data = [NSData dataWithBytes:buffer length:64];
aesCrypt = [data RC4EncryptDecryptWithKey:#"ABCD" operation:kCCDecrypt];
// aesCrypt=[data DecryptAES:#"akey"];
const uint8_t *bytes = (const uint8_t*)[aesCrypt bytes];
for(int j=0;j<63;j++)
{
buf[j]=*bytes++;
}
int buf1 = buf[63];
for(int j=0;j<buf1;j++)
{
temp[j] = buf[j];
}
fwrite(temp, 1, buf1, destination);
}
else
{
NSData *data;
while (totalBytes<=(sz-1))
{
if(!cancelDecryption)
{
percentage =((double)totalBytes/(double)sz)*100;
if (percentage!=prevPercentage)
{
prevPercentage=percentage;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[percentageLabel setText:[NSString stringWithFormat:#"%i%%",percentage]];
}];
}
fread(buffer,1, 64, source);
// memcpy( buf, buffer, 16);
// memcpy( keyoffline, [delegate secretKey], 16 + 0 * 8);
// aes_set_key( &ctx, keyoffline, 128 + 0 * 64);
// aes_decrypt( &ctx, buf, buf );
data = [NSData dataWithBytes:buffer length:64];
aesCrypt = [data RC4EncryptDecryptWithKey:#"ABCD" operation:kCCDecrypt];
// aesCrypt=[data DecryptAES:#"akey"];
const uint8_t *bytes = (const uint8_t*)[aesCrypt bytes];
for(int j=0;j<63;j++)
{
buf[j]=*bytes++;
}
fwrite(buf, 1, 64, destination);
totalBytes=totalBytes+64;
}
else
{
percentageLabel.text=#"";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *err;
if (![fileManager removeItemAtPath:filePath error:&err])
{
NSLog(#"Unable to delete the essence. Reason:%#",[err description]);
}
break;
}
}
}
dispatch_async(dispatch_get_main_queue(), ^{
percentageLabel.text=#"";
if (!cancelDecryption)
{
percentageLabel.hidden = YES;
movieURL = [NSURL fileURLWithPath:filePath];
[self.mediaPanelViewCtrl setURL:movieURL];
}
});
});
}
}
}
#catch (NSException *exception)
{
NSLog(#"Exception in decrypt file::%#",[exception description]);
}
}
this is the method to do encryption and decryption using RC4....
- (NSData*)RC4EncryptDecryptWithKey:(NSString *)key operation:(CCOperation)operation
{
// convert to C string..
int keySize = [key length];
char keyPtr[keySize];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr
maxLength:sizeof(keyPtr)
encoding:NSUTF8StringEncoding];
// encode/decode
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength;
void *buffer = malloc(bufferSize);
size_t numBytesOut = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation,
kCCAlgorithmRC4,
kCCOptionECBMode,
keyPtr,
kCCKeySizeMinRC4,
NULL,
[self bytes],
dataLength,
buffer,
bufferSize,
&numBytesOut);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer
length:numBytesOut
freeWhenDone:YES];
}
free(buffer);
return nil;
}
I have just recently found Objective Zip Ihave been reading through the instructions to get it set up in my project. However I am not really sure how to use it to decompress some NSData I have that I am wanting to decompress.
I have looked at the example solution and it seems to be performing the unzip on a zip file the code looks roughly like this
ZipFile *unzipFile= [[ZipFile alloc] initWithFileName:filePath mode:ZipFileModeUnzip];
[unzipFile goToFirstFileInZip];
ZipReadStream *read1= [unzipFile readCurrentFileInZip];
give or take some other instructions this is how they show you to use it, their sample code is here
I would like to know how to do the same thing but using NSData? or would I have to convert the NSData into a zipFile? if so how is that performed properly?
The NSData I am trying to unzip if zlib compressed... any example code would be helpful
here it is https://stackoverflow.com/a/6466832/751885
I use the following two methods process NSData
and call
saveToFile
method write on disk.
[[self compressData:uncompressedData] writeToFile:#"fileName.zip" atomically:YES];
Compress:
-(NSData*) compressData:(NSData* )uncompressedData {
if ([uncompressedData length] == 0) return uncompressedData;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.total_out = 0;
strm.next_in=(Bytef *)[uncompressedData bytes];
strm.avail_in = (unsigned int)[uncompressedData 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 = (unsigned int)([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];
}
Uncompress:
-(NSData*) uncompressGZip:(NSData*) compressedData {
if ([compressedData length] == 0) return compressedData;
NSUInteger full_length = [compressedData length];
NSUInteger half_length = [compressedData length] / 2;
NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
BOOL done = NO;
int status;
z_stream strm;
strm.next_in = (Bytef *)[compressedData bytes];
strm.avail_in = (unsigned int)[compressedData 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 = (unsigned int)([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;
}
}
I'm successfully sending int and CGPoint data in the struct of the GKTank example, BUT I tried desperately to send a NSString. I also tried it with char instead of NSString with no success...
Here's the code for
sending:
NSString *playerName = #"My Name";
NSData *dat = [playerName dataUsingEncoding:NSUTF8StringEncoding];
[BTINconnector sendNetworkPacketwithPacketID:NETWORK_PLAYERNAME withData:dat ofLength:sizeof(dat) reliable:YES];
the method (known from GKTank, modified)
- (void)sendNetworkPacketwithPacketID:(int)packetID withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend {
//NSLog(#"bmpc:ok 3 send packet to %i",gamePeerId);
// the packet we'll send is resued
static unsigned char networkPacket[kMaxPacketSize];
const unsigned int packetHeaderSize = 2 * sizeof(int); // we have two "ints" for our header
if(length < (kMaxPacketSize - packetHeaderSize)) { // our networkPacket buffer size minus the size of the header info
int *pIntData = (int *)&networkPacket[0];
// header info
pIntData[0] = gamePacketNumber++;
pIntData[1] = packetID;
// copy data in after the header
memcpy( &networkPacket[packetHeaderSize], data, length );
NSData *packet = [NSData dataWithBytes: networkPacket length: (length+8)];
if(howtosend == YES) {
[mySession sendData:packet toPeers:[NSArray arrayWithObject:gamePeerId] withDataMode:GKSendDataReliable error:nil];
} else {
[mySession sendData:packet toPeers:[NSArrayarrayWithObject:gamePeerId] withDataMode:GKSendDataUnreliable error:nil];
}
}
}
and the method for receiving (from GKTank modified by me)
- (void)receiveDataDG:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context {
static int lastPacketTime = -1;
unsigned char *incomingPacket = (unsigned char *)[data bytes];
int *pIntData = (int *)&incomingPacket[0];
NSData* nData = (NSData*)&incomingPacket[0];
//NSData* bData = nData[8];
//
// developer check the network time and make sure packers are in order
//
int packetTime = pIntData[0];
int packetID = pIntData[1];
if(packetTime < lastPacketTime && packetID != NETWORK_COINTOSS) {
NSLog(#"bmc: EXITED");
return;
}
lastPacketTime = packetTime;
switch( packetID ) {
case NETWORK_PLAYERNAME:
{
NSData *hjk = [NSData dataWithBytes:&pIntData[2] length:sizeof(int)];
NSString *gotitb = [[NSString alloc] initWithData:hjk encoding:NSUTF8StringEncoding];
NSLog(#"bmc:str %#,%#",gotitb,gotitb);
…
The gotitb returns null, but I don't know why. Please help.
You don't want sizeof(dat) you want [dat length].
I'm trying to cache some images in sqlite as nsdata and I'm having an issue when I attempt to insert the byte array using sqlite3_exec and a raw SQL string (as NSString)
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);
But the problem with the above is I'm adding NSData to the sql string directly instead of the bytes.
I wanted to do something like this
... [imgData bytes], [imgData length]
But because I'm not using the typical "_bind_blob" like approach I'm not sure how to do it w/ a raw string
Update
I'm using a wrapper that I'd like to stick w/ and simply write a new method to support image insert / query commands
the below is my entire wrapper class so far
**
#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
**
Any help with this solution would be huge!
I think a large part of the issue you are running into here is that you are trying to simplify the SQLite3 APIs too much. The APIs are not just for executing textual SQL queries; prepared statements and bind parameters exist for a reason. You shouldn't be trying to insert binary data in a string. That's just asking for problems, especially if your binary data has nulls in it.
To insert blobs, you really do need to use sqlite3_bind_blob with sqlite3_prepare_v2. When you bind the blob, you will need to also use [imgData bytes] as the blob data.
Are you perhaps looking for help reconstructing your API to make this sort of thing easier for this particular image caching use case?
Edit
Here's a simple example using bind to insert binary data. Assume there is a table called my_table with 2 columns: name of type VARCHAR and data of type BLOB. Please note that I have not tested or even tried compiling this, so there may be typos or errors.
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);