2012-04-19 14 views
9

Tôi đang làm việc trên ứng dụng DB truyền thông. Tôi có một mô hình tùy chỉnh với lưu trữ dữ liệu và suy nghĩ về việc viết lại nó với Dữ liệu cốt lõi. Một trường hợp sử dụng mà tôi quan tâm đặc biệt là bộ nhớ phim. Tôi lưu trữ các tệp phim trong DB, nhưng khung phương tiện truyền thông chỉ có thể đọc phim từ các tệp (không phải dữ liệu).Tôi có thể truy cập các tệp được sử dụng để lưu trữ nhị phân bên ngoài trong Dữ liệu chính không?

Dữ liệu chính cung cấp tính năng tiện dụng được gọi là "bộ nhớ nhị phân bên ngoài", nơi dữ liệu thực thể không được lưu trữ trong DB, nhưng trong tệp bên ngoài. Điều này minh bạch đối với người dùng API dữ liệu cốt lõi. Câu hỏi của tôi là, tôi có thể lấy đường dẫn đến tệp bên ngoài, để tôi có thể lưu trữ một bộ phim bằng cách sử dụng Dữ liệu cốt lõi và sau đó dễ dàng tải nó từ tệp bên ngoài Dữ liệu lõi của nó không?

Trả lời

3

Nếu bạn muốn truy cập dữ liệu trực tiếp (tức là, không thông qua CoreData), bạn có thể tắt cho mỗi tệp UUID dưới dạng tên và lưu tên đó trong cơ sở dữ liệu và lưu trữ tệp thực.

Nếu bạn sử dụng UIManagedDocument, bạn có một số tùy chọn. Sử dụng kỹ thuật trên, bạn có thể lưu trữ các tệp cùng với cơ sở dữ liệu, vì UIManagedDocument thực sự là một gói tệp.

Hoặc, bạn có thể phân lớp từ UIManagedDocument và ghi đè lên các phương thức xử lý việc đọc/ghi tệp "phụ". Điều này sẽ cung cấp cho bạn quyền truy cập vào các tệp. Bạn có thể móc ở đó để làm bất cứ điều gì bạn muốn, bao gồm việc lấy URL thực sự vào tệp CoreData tự động tạo.

- (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

Câu trả lời hay, cảm ơn bạn. Tôi không muốn tự xử lý các tệp và chỉ theo dõi các ID thông qua Dữ liệu cốt lõi, vì việc loại bỏ quản lý tệp thủ công là một trong những điều chính khiến tôi cân nhắc viết lại dữ liệu cốt lõi ngay từ đầu.Và khai thác vào API thông qua 'UIManagedDocument' cảm thấy quá hung hăng, quá xa so với các trường hợp sử dụng dự kiến ​​để tôi cảm thấy an toàn về nó. – zoul

+0

Không thể hiểu được UIManagedDocument xử lý nội dung bổ sung. Làm cách nào để xóa blob, ví dụ:? – Shmidt

2

Có, bạn CÓ THỂ truy cập các tệp được lưu trữ trong Bộ nhớ ngoài. Phải mất một chút hack, và có thể không hoàn toàn kosher với App Store của Apple, nhưng bạn có thể làm điều đó khá một cách dễ dàng.

Giả sử chúng ta có một NSManagedObject Subclass 'Media', với một tài sản 'dữ liệu' đã được thiết lập để 'Cho phép lưu trữ bên ngoài' trong Core Data Editor:

// 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 

Và một loại NSString tiện dụng-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 

Bây giờ là lúc dùng một số phép thuật .... Chúng ta sẽ tạo phương pháp Phân loại phân tích tên tệp từ chuỗi [mô tả dữ liệu]. Khi hoạt động trên một thể hiện của lớp con Media, 'dữ liệu' thực sự là 'Tham chiếu bộ nhớ ngoài', không phải là một đối tượng NSData. Tên tệp của dữ liệu thực được lưu trữ trong chuỗi mô tả.

// 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 

Bây giờ bạn có đường dẫn NSString và đường dẫn NSURL vào tệp. JOY !!!

Một điều cần lưu ý là tôi đã gặp phải sự cố khi tải phim bằng phương pháp này ... nhưng tôi cũng đã đưa ra giải pháp. Có vẻ như MPMoviePlayer sẽ không truy cập các tệp trong thư mục này, do đó giải pháp là tạm thời sao chép tệp vào thư mục tài liệu và phát tệp đó. Sau đó xóa bản sao tạm thời khi tôi bỏ chế độ xem của tôi:

- (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]; 
    } 
} 

Chúc may mắn!

+0

Nếu bạn đang sao chép nó, có lợi thế gì hơn là chỉ cần xóa blob 'NSData' vào đĩa trong một thư mục tạm thời và truy cập vào đó không? –

+0

không thực sự :), bệnh cập nhật câu trả lời của tôi sau này với giải pháp hiện tại tôi sử dụng. Tôi vẫn * sử dụng dữ liệu lõi *, nhưng sử dụng các thuộc tính tạm thời để quản lý các tệp/NSData bản thân mình –

+0

Điều này sẽ không hoạt động nếu kích thước tệp (tương đối) nhỏ. Tôi đoán các tệp nhỏ hơn 24 KB được lưu trữ dưới dạng blob thay vì tệp bên ngoài. Vì vậy, 'NSString * filename = [description returnBetweenString: @" path = "andString: @"; "];' sẽ cho nil. – yoninja

Các vấn đề liên quan