【问题标题】:How to store an image in core data如何将图像存储在核心数据中
【发布时间】:2013-05-17 03:40:49
【问题描述】:

我是 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];
} 

【问题讨论】:

  • 您的图像可能存储在UIImage 对象中。如果是这样,您可以使用以下命令将其转换为 NSData 对象:NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(myUIImage)]; 我相信将 NSData 对象存储在 CoreData 中并不难。
  • IMO,通常不建议将二进制数据存储在数据库中。检索比从磁盘读取要慢,并且由于 Core Data 可能会在内部使用 SQLite,因此您将有效地将文件存储在一个文件(SQLite DB)中,导致它的大小非常迅速地扩展并大大减慢了 DB 操作。我只是将路径存储在数据库中,然后将图像保存到磁盘。
  • 我同意 dandan78。将图像保存到商店是不明智的。您绝对应该保存图像的路径。
  • 可以使用UIImagePNGRepresentaion或者UIImageJPEGrepresentation获取图片的数据并保存。当您获取数据时,您使用 +imageWithData: 创建图像。当然,您可以将它们保存到文件中并仅存储文件名。我都做了。给我一分钟,我会寻找一些代码片段。
  • 克服它。您可以根据大小将图像存储在 Core Data 中。如果它们的压缩量低于 100k,它们甚至不必在外部文件中。我们发现性能还不错。

标签: ios objective-c core-data


【解决方案1】:

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

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

    NSData *imageData = UIImagePNGRepresentation(image);

  • 在此属性上启用“允许外部存储” 如果达到某个阈值,Core Data 会将数据移动到外部文件。这个文件也完全由 Core Data 管理,所以你不用担心。

  • 如果遇到性能问题,请尝试将 Binary Data 属性移至单独的实体。

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

  • 如果您的图像与模型中的实体没有很强的相关性,我建议将它们存储在 Core Data 之外。

【讨论】:

  • 对于“您应该在 NSManagedObject 子类的接口后面抽象到 NSData 的转换,因此您不必担心从 UIImage 到 NSData 的转换,反之亦然。”你能详细说明如何做到这一点吗?
  • 创建一种方法来设置图像 - (void)setImage:(UIImage *)image;可能在一个类别中。在该方法中,使用 UIImagePNGRepresentaion 或 UIImageJPEGrepresentation 以便您只需执行一次(您不想保存为 PNG 并尝试转换为 JPEG)。
  • 如果与模型无关,为什么建议将图像存储在CoreData之外?这个推荐我已经看过好几次了,但我不知道为什么在CoreData中存储图像不好
  • 我也想知道这个
【解决方案2】:

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

@property (nonatomic, retain) NSData *imag;

在实现文件中.. 将UIImage 转换为NSData

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

NSData *dataImage = UIImageJPEGRepresentation(sampleimage, 0.0);

然后最后保存

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

【讨论】:

    【解决方案3】:

    对于 Swift 5Swift 4.2

    1. UIImage 转换为Data

      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

      该示例取自我目前正在进行的一个项目。

      在核心数据中存储大型数据对象(如图像)或将它们分隔在文件中各有利弊。将它们存储在文件中意味着您有责任在删除相关数据对象时删除它们。这提供了编码开销,并且可能因编程错误而不稳定。 另一方面,它可能会减慢底层数据库的速度。我还没有足够的经验来分享这种方法的性能。 最后,占用的磁盘存储总量大致相同。

      【讨论】:

        【解决方案5】:

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

        无论如何,我在开发副项目时发现的最佳方法如下所示

        1. Set the entity codeGen to Manual/None

        2. 选择你想成为图像类型的属性并选择transformable作为它的类型

        3. Enter Custom class identifier as UIImage

        4. 进入编辑器并选择Create NSManagedObject Subclass

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

        如果单击Jump to Definition 以获取@NSManaged 公共变量UIImage definition,您的工作就完成了。

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

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

        【讨论】:

          【解决方案6】:
          【解决方案7】:

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

          请参考以下网址,如何在文档目录中存储和检索图像。

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

          Saving image to Documents directory and retrieving for email attachment

          How do I create a temporary file with Cocoa?

          【讨论】:

          • 很多很好的理由。您可能想要复制您的数据库并将其发送到不同的设备。如果它位于 sqlite 商店中,则容易得多。总体而言,无需维护大量蹩脚的代码就更容易管理。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-09-04
          • 1970-01-01
          • 2016-07-26
          • 1970-01-01
          • 2011-01-06
          • 1970-01-01
          相关资源
          最近更新 更多