2012-04-19 7 views
9

Sto lavorando su un'applicazione DB multimediale. Ho un modello personalizzato con archiviazione dei dati e penso di riscriverlo su Core Data. Un caso d'uso che è di particolare interesse per me è l'archiviazione di film. Memorizzo file di film nel DB, ma il framework multimediale può solo leggere film da file (non dati).Posso accedere ai file utilizzati per l'archiviazione binaria esterna in Core Data?

Core Data offre una comoda funzione denominata "archiviazione binaria esterna", in cui i dati dell'entità non sono memorizzati nel DB, ma in un file esterno. Questo è trasparente per l'utente dell'API Core Data. La mia domanda è: posso ottenere il percorso del file esterno, in modo da poter archiviare un film utilizzando Core Data e caricarlo facilmente dal suo file esterno di Core Data?

risposta

3

Se si desidera accedere ai dati direttamente (ad esempio, non tramite CoreData), potrebbe essere preferibile assegnare a ciascun file un UUID come nome e archiviare tale nome nel database e archiviare personalmente il file.

Se si utilizza UIManagedDocument, sono disponibili diverse opzioni. Utilizzando la tecnica di cui sopra, è possibile memorizzare i file accanto al database, perché UIManagedDocument è in realtà un pacchetto di file.

In alternativa, è possibile sottoclasse da UIManagedDocument e sovrascrivere i metodi che gestiscono la lettura/scrittura dei file "extra". Questo ti darà accesso ai file stessi. Puoi collegarti lì per fare quello che vuoi, tra cui l'effettivo URL del file che CoreData crea automaticamente.

- (id)additionalContentForURL:(NSURL *)absoluteURL error:(NSError **)error 
- (BOOL)readAdditionalContentFromURL:(NSURL *)absoluteURL error:(NSError **)error 
- (BOOL)writeAdditionalContent:(id)content toURL:(NSURL *)absoluteURL originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError **)error 
+0

Bella risposta, grazie. Non sono molto entusiasta di gestire i file da solo e di monitorare gli ID solo tramite i dati principali, perché eliminare la gestione manuale dei file è stata una delle cose principali che mi ha fatto prendere in considerazione la possibilità di riscrivere i dati di base in primo luogo.E toccare l'API con 'UIManagedDocument' sembra troppo impreciso, troppo lontano dai casi d'uso previsti per farmi sentire al sicuro. – zoul

+0

Impossibile capire UIManagedDocument gestire il contenuto aggiuntivo. Come posso cancellare BLOB, per esempio? – Shmidt

2

Sì, è possibile accedere ai file archiviati nella memoria esterna. Ci vuole un po 'di hacking e può non essere completamente kosher con l'App Store di Apple, ma puoi farlo facilmente .

Supponendo che abbiamo un NSManagedObject sottoclasse 'media', con una proprietà 'dati' che è stato impostato a 'consente la memorizzazione esterno' nel Data Editor Nucleo:

// Media.h 
// Examples 
// 
// Created by Garrett Shearer on 11/21/12. 
// Copyright (c) 2012 Garrett Shearer. All rights reserved. 
// 

#import <Foundation/Foundation.h> 
#import <CoreData/CoreData.h> 


@interface CRMMedia : NSManagedObject 

@property (nonatomic, retain) NSString * name; 
@property (nonatomic, retain) NSData * data; 

@end 

e una categoria NSString pratico-dandy :

// NSString+Parse.m 
// Examples 
// 
// Created by Garrett Shearer on 11/21/12. 
// Copyright (c) 2012 Garrett Shearer. All rights reserved. 
// 

#import "NSString+Parse.h" 

@implementation NSString (Parse) 

- (NSString*)returnBetweenString:(NSString *)inString1 
         andString:(NSString *)inString2 
{ 
    NSRange substringRange = [self rangeBetweenString:inString1 
              andString:inString2]; 
    logger(@"substringRange: (%d, %d)",substringRange.location,substringRange.length); 
    logger(@"string (self): %@",self); 
    return [self substringWithRange:substringRange]; 
} 


/* 
Return the range of a substring, searching between a starting and ending delimeters 
Original Source: <http://cocoa.karelia.com/Foundation_Categories/NSString/Return_the_range_of.m> 
(See copyright notice at <http://cocoa.karelia.com>) 
*/ 

/*" Find a string between the two given strings with the default options; the delimeter strings are not included in the result. 
"*/ 

- (NSRange) rangeBetweenString:(NSString *)inString1 andString:(NSString *)inString2 
{ 
    return [self rangeBetweenString:inString1 andString:inString2 options:0]; 
} 

/*" Find a string between the two given strings with the given options inMask; the delimeter strings are not included in the result. The inMask parameter is the same as is passed to [NSString rangeOfString:options:range:]. 
"*/ 

- (NSRange) rangeBetweenString:(NSString *)inString1 andString:(NSString *)inString2 
         options:(unsigned)inMask 
{ 
    return [self rangeBetweenString:inString1 andString:inString2 
          options:inMask 
           range:NSMakeRange(0,[self length])]; 
} 

/*" Find a string between the two given strings with the given options inMask and the given substring range inSearchRange; the delimeter strings are not included in the result. The inMask parameter is the same as is passed to [NSString rangeOfString:options:range:]. 
"*/ 

- (NSRange) rangeBetweenString:(NSString *)inString1 andString:(NSString *)inString2 
         options:(unsigned)inMask range:(NSRange)inSearchRange 
{ 
    NSRange result; 
    unsigned int foundLocation = inSearchRange.location; // if no start string, start here 
    NSRange stringEnd = NSMakeRange(NSMaxRange(inSearchRange),0); // if no end string, end here 
    NSRange endSearchRange; 
    if (nil != inString1) 
    { 
     // Find the range of the list start 
     NSRange stringStart = [self rangeOfString:inString1 options:inMask range:inSearchRange]; 
     if (NSNotFound == stringStart.location) 
     { 
      return stringStart; // not found 
     } 
     foundLocation = NSMaxRange(stringStart); 
    } 
    endSearchRange = NSMakeRange(foundLocation, NSMaxRange(inSearchRange) - foundLocation); 
    if (nil != inString2) 
    { 
     stringEnd = [self rangeOfString:inString2 options:inMask range:endSearchRange]; 
     if (NSNotFound == stringEnd.location) 
     { 
      return stringEnd; // not found 
     } 
    } 
    result = NSMakeRange(foundLocation, stringEnd.location - foundLocation); 
    return result; 
} 


@end 

Ora il suo tempo per un po 'di magia .... Stiamo per creare un metodo categoria che analizza il nome del file dalla [descrizione dei dati] stringa. Quando si opera su un'istanza della sottoclasse Media, "dati" è in realtà un "Riferimento di archiviazione esterno", non un oggetto NSData. Il nome file dei dati effettivi è memorizzato nella stringa descrittiva.

// Media+ExternalData.m 
// Examples 
// 
// Created by Garrett Shearer on 11/21/12. 
// Copyright (c) 2012 Garrett Shearer. All rights reserved. 
// 

#import "Media+ExternalData.h" 
#import "NSString+Parse.h" 

@implementation Media (ExternalData) 

- (NSString*)filePathString 
{ 
    // Parse out the filename 
    NSString *description = [self.data description]; 
    NSString *filename = [description returnBetweenString:@"path = " andString:@" ;"]; 
    // Determine the name of the store 
    NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator; 
    NSPersistentStore *ps = [psc.persistentStores objectAtIndex:0]; 
    NSURL *storeURL = [psc URLForPersistentStore:ps]; 
    NSString *storeNameWithExt = [storeURL lastPathComponent]; 
    NSString *storeName = [storeNameWithExt stringByDeletingPathExtension]; 
    // Generate path to the 'external data' directory 
    NSString *documentsPath = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory 
                     inDomains:NSUserDomainMask] lastObject] path]; 
    NSString *pathComponentToExternalStorage = [NSString stringWithFormat:@".%@_SUPPORT/_EXTERNAL_DATA",storeName]; 
    NSString *pathToExternalStorage = [documentsPath stringByAppendingPathComponent:pathComponentToExternalStorage]; 
    // Generate path to the media file 
    NSString *pathToMedia = [pathToExternalStorage stringByAppendingPathComponent:filename]; 
    logger(@"pathToMedia: %@",pathToMedia); 
    return pathToMedia; 
} 

- (NSURL*)filePathUrl 
{ 
    NSURL *urlToMedia = [NSURL fileURLWithPath:[self filePathString]]; 
    return urlToMedia; 
} 

@end 

Ora si ha un percorso NSString e un percorso NSURL per il file. GIOIA !!!

Qualcosa di cui prendere atto, ho avuto problemi nel caricare film con questo metodo ... ma ho anche trovato una soluzione alternativa. Sembra che MPMoviePlayer non acceda ai file in questa directory, quindi la soluzione è stata copiare temporaneamente il file nella directory dei documenti e riprodurlo. Quindi eliminare la copia temporanea quando scarico la vista:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self copyTmpFile]; 
} 

- (void)viewDidUnload 
{ 
    logger(@"viewDidUnload"); 
    [_moviePlayer stop]; 
    [_moviePlayer.view removeFromSuperview]; 
    [self cleanupTmpFile]; 
    [super viewDidUnload]; 
} 

- (NSString*)tmpFilePath 
{ 
    NSString *documentsPath = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory 
                     inDomains:NSUserDomainMask] lastObject] path]; 
    NSString *tmpFilePath = [documentsPath stringByAppendingPathComponent:@"temp_video.m4v"]; 
    return tmpFilePath; 
} 

- (void)copyTmpFile 
{ 
    NSString *tmpFilePath = [self tmpFilePath]; 
    NSFileManager *mgr = [NSFileManager defaultManager]; 
    NSError *err = nil; 
    if([mgr fileExistsAtPath:tmpFilePath]) 
    { 
     [mgr removeItemAtPath:tmpFilePath error:nil]; 
    } 

    [mgr copyItemAtPath:_media.filePathString toPath:tmpFilePath error:&err]; 
    if(err) 
    { 
     logger(@"error: %@",err.description); 
    } 
} 

- (void)cleanupTmpFile 
{ 
    NSString *tmpFilePath = [self tmpFilePath]; 
    NSFileManager *mgr = [NSFileManager defaultManager]; 
    if([mgr fileExistsAtPath:tmpFilePath]) 
    { 
     [mgr removeItemAtPath:tmpFilePath error:nil]; 
    } 
} 

Buona fortuna!

+0

Se si sta copiando lo stesso, c'è qualche vantaggio rispetto al semplice scaricamento del blob 'NSData' su disco in una directory temporanea e accesso a tale? –

+0

non proprio :), aggiorno la mia risposta più tardi con la soluzione corrente che uso. Uso ancora i dati di base *, ma uso le proprietà transitori per gestire i file/NSData da solo –

+0

Questo non funzionerà se la dimensione del file è (relativamente) piccola. Immagino che i file più piccoli di 24 KB siano memorizzati come blob invece di un file esterno. Quindi, 'NSString * filename = [description returnBetweenString: @" path = "andString: @"; "];' darà zero. – yoninja

Problemi correlati