如何在核心数据中存储图像


74

我是iOS新手。我一直在尝试制作一个应用程序,它将将从相机捕获的图像存储到中CoreData。我现在知道如何存储NSStrings之类的数据,NSDate以及其他类型的数据,但是很难存储图像。我读过很多文章,说您必须将其写入磁盘并写入文件,但是我似乎无法理解。

以下代码是我用来将其他数据存储到核心数据的代码。

- (IBAction)submitReportButton:(id)sender
{
    UrbanRangerAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

    managedObjectContext = [appDelegate managedObjectContext];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"PotholesDB" inManagedObjectContext:appDelegate.managedObjectContext];
    NSManagedObject *newPothole = [[NSManagedObject alloc]initWithEntity:entity insertIntoManagedObjectContext:managedObjectContext];


    [newPothole setValue:self.relevantBody.text forKey:@"relevantBody"];
    [newPothole setValue:self.subjectReport.text forKey:@"subjectReport"];
    [newPothole setValue:self.detailReport.text forKey:@"detailReport"];
//    [newPothole setValue:self.imageView forKey:@"photo"];

    NSDate *now = [NSDate date];
    //NSLog(@"now : %@", now);
    NSString *strDate = [[NSString alloc] initWithFormat:@"%@", now];
    NSArray *arr = [strDate componentsSeparatedByString:@" "];
    NSString *str;
    str = [arr objectAtIndex:0];
    NSLog(@"now : %@", str);

    [newPothole setValue:now forKey:@"photoDate"];
    [newPothole setValue:self.latitudeLabel.text forKey:@"latitude"];
    [newPothole setValue:self.longitudeLabel.text forKey:@"longitude"];
    [newPothole setValue:self.addressLabel.text forKey:@"streetName"];
    [newPothole setValue:streeNameLocation forKey:@"location"];


    NSError *error;
    [managedObjectContext save:&error];

    UIAlertView *ll = [[UIAlertView alloc] initWithTitle:@"Saving" message:@"Saved data" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

    [ll show];
} 

1
您的图像可能存储在一个UIImage对象中。如果是这样,您可以使用以下方法将其转换为NSData对象:NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(myUIImage)];我相信将NSData对象存储在其中CoreData并不难。
rdurand

7
IMO,通常不建议在数据库中存储二进制数据。检索比从磁盘读取要慢,并且由于Core Data可能会在内部使用SQLite,因此您将有效地将文件存储在文件(SQLite DB)中,从而导致其大小迅速扩展,并显着降低了DB操作的速度。我只是将路径存储在数据库中,然后将图像保存到磁盘。
dandan78

我同意dandan78。将图像保存到商店是不明智的。您绝对应该保存图像的路径。
Mikael

您可以使用UIImagePNGRepresentaion或使用UIImageJPEGrepresentation获取图像数据并保存数据。提取数据时,您将使用创建图像+imageWithData:。当然,您可以将它们保存到文件中,而只需存储文件名。我都做过 给我一点时间,我将寻找一些代码片段。
赫尔曼·克莱克

1
克服它。您可以根据大小将图像存储在Core Data中。如果压缩率低于100k,则它们甚至不必位于外部文件中。我们发现性能还不错。
RegularExpression

Answers:


173

您可以使用Binary Data属性类型将图像存储在Core Data中。但是,您应该注意以下几点:

  • 始终将UIImage转换为可移植的数据格式,例如png或jpg例如:

    NSData *imageData = UIImagePNGRepresentation(image);

  • 如果在此属性上启用“允许外部存储”,则 描述 Core Data会在达到特定阈值时将数据移至外部文件。该文件也由Core Data完全管理,因此您不必担心。

  • 如果遇到性能问题,请尝试将“二进制数据”属性移动到单独的实体。

  • 您应该在NSManagedObject子类的接口之后抽象到NSData的转换,因此您不必担心从UIImage到NSData的转换,反之亦然。

  • 如果您的图像与模型中的实体没有密切关系,则建议将其存储在Core Data之外。


对于“您应该在NSManagedObject子类的接口之后抽象到NSData的转换,因此您不必担心从UIImage到NSData的转换,反之亦然。” 您能详细说明如何做到吗?
道格·史密斯

8
创建一种方法来设置图像-(void)setImage:(UIImage *)image; 可能在一个类别中。在该方法中,请使用UIImagePNGRepresentaion或UIImageJPEGrepresentation,以便您只需执行一次(您不希望另存为PNG并尝试转换为JPEG)。
凯文(Kevin)

13

在xcdatamodelId子类中,将图像实体声明为NSData...,UIImage因为图像数据为二进制格式,所以不能使用format。

@property (nonatomic, retain) NSData *imag;

在实现文件中..转换UIImageNSData

UIImage *sampleimage = [UIImage imageNamed:@"sampleImage.jpg"];

NSData *dataImage = UIImageJPEGRepresentation(sampleimage, 0.0);

然后最后保存

[obj setValue:dataImage forKey:@"imageEntity"]; // obj refers to NSManagedObject

6

对于Swift 5Swift 4.2

  1. 转换UIImageData

    let imageData = image.jpegData(compressionQuality: 1.0)
    
  2. 保存到CoreData

    let object = MyEntity(context: managedContext)   //create object of type MyEntity
    object.image = imageData                         //add image to object
    
    do {
        try managedContext.save()                   //save object to CoreData
    } catch let error as NSError {
        print("\(error), \(error.userInfo)")
    }
    

4
- (void)imageChanged:(UIImage*)image{

    if (self.detailItem) {
        [self.detailItem setValue:UIImagePNGRepresentation(image) forKey:kSignatureImage];
    }
}

在这个非常简单的例子中。self是使用核心数据的视图控制器,而self.detailItem是通常的NSManagedObject。在那个项目中,我没有为实体创建模型类,而是严格使用KVC模式访问属性。您可能会猜到,该属性的名称"signatureImage"是我在#define常量中定义的kSignatureImage

这是从核心数据还原映像的位置:

self.signatureCanvas.image =  [UIImage imageWithData:[self.detailItem valueForKey:kSignatureImage]];

同样,它self是一个视图控制器,signatureCanvas是一个UIImageView子类,.image并且它的常规image属性是从继承的UIImageViewdetailItem同样,这是平常的NSManagedObject

该示例取自我当前正在从事的项目。

将大型数据对象(例如图像)存储在核心数据中或将它们分隔在文件中有优缺点。将它们存储在文件中意味着您有责任在删除相关数据对象时删除它们。这提供了编码开销,并且对于编程错误可能是易变的。另一方面,它可能会降低基础数据库的速度。对于这种方法,我还没有足够的经验可以与我分享。最后,占用的磁盘存储空间总计大致相同。



1

可以将图像作为UIImage存储在Core Data中。尽管这不被认为是一种好习惯,因为数据库并不用于存储文件。我们不使用核心数据直接存储图像,而是使用文件路径将图像数据存储在手机上。

无论如何,下面展示了我在开发附带项目时发现的最佳方法

  1. 将实体codeGen设置为Manual / None

  2. 选择要为图像类型的属性,然后选择可变形的类型

  3. 输入自定义类标识符作为UIImage

  4. 转到编辑器,然后选择“创建NSManagedObject子类”

  5. 该流程将根据您的实体数量创建2个swift文件(我只有1个实体)。现在,选择<EntityName> + CoreDataProperties.swift文件并导入UIKit

如果在@NSManaged公共变量上单击“跳转到定义”,则说明UIImage definition您的工作已完成。

就像您对实体执行操作一样,您将能够通过向下广播获取,保存,编辑图像属性<EntityName>?.value(forKey: "<attributeName>") as? UIImage

我不得不使用实体作为NSManagedObject类型,并且由于某种原因无法直接从创建的子类访问图像。


0

我不知道为什么要在核心数据中存储图像,而iOS中还有其他持久存储。如果仅出于存储目的而存储图像,则可以将其存储在文档目录或缓存目录中。快乐编码...

请参阅下面的URL,如何在文档目录中存储和检索图像。

http://www.wmdeveloper.com/2010/09/save-and-load-uiimage-in-documents.html

将图像保存到“文档”目录并检索电子邮件附件

如何使用Cocoa创建临时文件?


1
有很多充分的理由。您可能需要复制数据库并将其发送到其他设备。如果它位于sqlite商店中,则容易很多。总体而言,无需维护大量笨拙的代码,就更容易管理。
RegularExpression
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.