19.03.2011

Einleitung

Ausgehend von dem Tutorial Simple Core Data Command Line Importer (Xcode 3.2) wird im Datenmodell nun ein zusätzliches Attribut vom Typ Binary Data (NSData) hinzugefügt, um Binärdaten (BLOBs) zu speichern und zu lesen. In dem Beispiel werden Bilder (jpgs) verwendet. Allerdings können auf die gleiche Weise alle anderen Dateitypen gespeichert und gelesen werden. Beispielsweise PDF-Datein, Video-Dateien oder Musik-Dateien wie MP3s.

Datenmodell erweitern

Als erstes wird der Entity Person ein Attribut personimage vom Typ Binary Data (NSData) hinzugefügt. Dazu selektiert man die Entity Person und klickt auf das kleine Pluszeichen. In dem Menü wählt man dann Add Attribute aus:

Das neue Attribut bekommt den Namen personimage, den Typ Binary Data (NSData) und ist optional. Danach sollte das Code Data Datenmodell folgendermaßen aussehen:

In dem Tutorial Simple Core Data Command Line Importer (Xcode 3.2) wurde eine Textdatei Personen.txt für den Import verwendet. Diese wird um Dateipfade zu Bildern ergänzt:

Jörn Hameister ~/Pictures/HameisterJ.jpg
Donald Duck ~/Pictures/DuckD.jpg
Bart Simpson ~/Pictures/SimpsonB.jpg

Binärdaten (BLOBs) speichern

Im Folgenden wird beschrieben wie eine jpg-Datei von der Festplatte gelesen wird und anschließend als NSData-Objekt mit Core Data persistiert wird.

In der Datei SimpleCommandLineImporter.m muß die Methode NSData* loadImage(NSString* path) ergänzt werden.

NSData* loadImage(NSString* path) {
    NSString *imagePath = [path stringByResolvingSymlinksInPath];
    
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL fileExists = [fileManager fileExistsAtPath:imagePath];
    if(fileExists) {
        return [[NSData alloc] initWithContentsOfFile:imagePath];    
    }
    return nil;
}

Als erstes wird der Pfad path in einen absoluten Pfad umgewandelt (mehr dazu unter Dateipfade). Danach wird der NSFileManager verwendet, um zu überprüfen, ob die Datei vorhanden ist. Wenn die Datei existiert, dann wird sie in ein Object vom Typ NSData eingelesen und zurückgegeben.

Anschließend werden in der for-Schleife der main-Methode die zwei Zeilen unterhalb von // Add the image ergänzt:

for(id person in persons) {
    NSArray *firstnameLastname =  [person componentsSeparatedByString:@" "];
    
    // Add a new Person
    NSManagedObject *personMO = 
         [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    [personMO setValue:[firstnameLastname objectAtIndex:0] forKey:@"personfirstname"];
    [personMO setValue:[firstnameLastname objectAtIndex:1] forKey:@"personname"];
    
    
    // Add the image
    NSData * imageData = loadImage([firstnameLastname objectAtIndex:2]);
    [personMO setValue:imageData forKey:@"personimage"];
}

Die erste Zeile ruft die neue Methode zum Laden der Datei auf. Die zweite Zeile sorgt dafür, daß der Dateiinhalt in dem Attribut personimage gespeichert wird.

Binärdaten (BLOBs) auslesen

Hier wird erklärt, wie ein NSData-Objekt mit einem NSFetchRequest aus Core Data ausgelesen werden kann. Nach dem Auslesen, werden die Daten auf die Festplatte geschrieben. In dem Beispiel wird davon ausgegangen, daß es sich um die jpg-Datei handelt, die oben persistiert wurde.

In der Datei SimpleCommandLineImporter.m muß die Methode fetchImageAndSaveToDisk(NSString* toPath) ergänzt werden.

void fetchImageAndSaveToDisk(NSString *toPath) {
    NSManagedObjectContext *context = managedObjectContext();
    
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:@"Person" inManagedObjectContext:context]];
    NSError *error = nil;
    
    NSArray *persons = [context executeFetchRequest:request error:&error];
    if(error) {
        NSLog(@"Cannot execute NSFetchRequest: %@", [error localizedDescription]);
        return ;
    }
    
    NSString *imagePath = [toPath stringByResolvingSymlinksInPath];
    for(NSManagedObject *person in persons) {
        NSData * imageData = [person valueForKey:@"personimage"];
        [imageData writeToFile:
             [imagePath stringByAppendingPathComponent:[[NSString alloc] 
               initWithFormat:@"%@%c.jpg", 
                              [person valueForKey:@"personname"], 
                              [[person valueForKey:@"personfirstname"] characterAtIndex:0]]] 
          atomically:TRUE];
    }    
}

Der obere Teil der Methode (bis Zeile 12) ist identisch mit dem NSFetchRequest aus dem Beispiel Auslesen von Core Data-Daten mit NSFetchRequest. In der for-Schleife in Zeile 15 wird über alle Personen iteriert. Von jeder Person werden die Bilddaten ausgelesen und in einem Objekt von Typ NSData abgelegt. Diese Klasse hat eine Methode writeToFile:atomically, die benutzt wird um die Bilddaten auf die Festplatte zu schreiben. Der erste Parameter dieser Methode ist der Dateiname inklusive Pfad. Der Pfad ist in der Variable imagePath gespeichert. Der Dateiname wird aus Nachnamen (personname), dem ersten Buchstaben des Vornamens (personfirstname) und der Dateiendung (.jpg) zusammengesetzt.

Hinweis


In dem Beispiel wird nicht überprüft, ob schon eine Datei gleichen Namens vorhanden ist.

Um die Bilder auszulesen und auf Festplatte zu schreiben, muß nun die Methode fetchImageAndSaveToDisk(NSString *toPath) in der main-Methode aufgerufen werden.

fetchImageAndSaveToDisk(@"~/Documents/tmp/");

Es ist darauf zu achten, daß das Verzeichnis ~/Documents/tmp/ auf der Festplatte existiert.