【问题标题】:How to properly send an image to CloudKit as CKAsset?如何正确地将图像作为 CKAsset 发送到 CloudKit?
【发布时间】:2016-05-01 16:59:27
【问题描述】:

我有一个图像(UIImage,它也是 url),我试图将它作为 CKAsset 发送到 CloudKit,但我遇到了这个错误:Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Non-file URL'。代码如下:

override func viewDidLoad() {
        super.viewDidLoad()

        send2Cloud()
    }

func send2Cloud() {
    let newUser = CKRecord(recordType: "User")

    let url = NSURL(string: self.photoURL)

    let asset = CKAsset(fileURL: url!)

    newUser["name"] = self.name
    newUser["photo"] = asset

    let publicData = CKContainer.defaultContainer().publicCloudDatabase

    publicData.saveRecord(newUser, completionHandler: { (record: CKRecord?, error: NSError?) in

        if error == nil {

            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                print("User saved")
            })

        } else {
            print(error?.localizedDescription)
        }
    })
}

我有 URL,我可以打印它,复制并粘贴到我的导航器,它会显示我的图像!所以,我不知道这里发生了什么......

如果我使用 UIImage 而不是它的 URL 会更容易吗?因为,正如我之前所说,我拥有它们!非常感谢任何帮助!谢谢各位!!

【问题讨论】:

  • 读取错误。 self.photoURL 不是文件 URL。它是什么?打印网址并在您的问题中发布输出。
  • self.photoURL 是一个字符串。我使用 Facebook 请求获取用户数据!这是输出:(fbcdn-profile-a.akamaihd.net/hprofile-ak-prn2/v/t1.0-1/p200x200/…)
  • 如果字符串表示本地文件路径,您需要做的就是使用fileURLWithPath 初始化程序而不是string 初始化程序正确创建NSURL

标签: ios cloudkit


【解决方案1】:

根据我的经验,将上传UIImage 保存为CKAsset 的唯一方法是:

  1. 将图像临时保存到磁盘
  2. 创建 CKA 资产
  3. 删除临时文件

let data = UIImagePNGRepresentation(myImage); // UIImage -> NSData, see also UIImageJPEGRepresentation
let url = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(NSUUID().UUIDString+".dat")
do {
    try data!.writeToURL(url, options: [])
} catch let e as NSError {
    print("Error! \(e)");
    return
}
newUser["photo"] = CKAsset(fileURL: url)

// ...

publicData.saveRecord(newUser, completionHandler: { (record: CKRecord?, error: NSError?) in
    // Delete the temporary file
    do { try NSFileManager.defaultManager().removeItemAtURL(url) }
    catch let e { print("Error deleting temp file: \(e)") }

    // ...
}


几个月前我提交了一份错误报告,要求能够从内存中的NSData 初始化CKAsset,但还没有完成。

【讨论】:

  • 谢谢圣诞老人!!
  • 你会如何反其道而行之,将 CKAsset 对象转换为 Data 对象,或将 CKAsset 对象转换为图像?
  • 我一直看到Value of type 'Data' has no member 'writeToURL'
【解决方案2】:

这是如何将图像保存到 Cloudkit 的 Objective C 版本

这需要大量的挖掘,因为没有太多信息可以继续,但这是可行的

  if([results count] <= 0) {



            NSLog(@"this Record doesnt exist so add it ok!! %@", error);

            CKRecordID *wellKnownID = [[CKRecordID alloc]
                                       initWithRecordName:idString];


            CKRecord *entitiesName = [[CKRecord alloc] initWithRecordType:@"mySavedDetails"
                                                             recordID:wellKnownID];


            [entitiesName setObject:idString
                         forKey:@"myDetailsId"];

            [entitiesName setObject:self.myName.text
                         forKey:@"myName"];



    if (myUIImage.image != nil)
                 {
                     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                                          NSUserDomainMask, YES);
                     NSString *documentsDirectory = [paths objectAtIndex:0];
                     NSString* path = [documentsDirectory stringByAppendingPathComponent:
                                       @"test.png" ];
                     NSData* data = UIImagePNGRepresentation(myUIImage.image.image);
                     [data writeToFile:path atomically:YES];

    //so we get the full path of the uiimage

                     NSLog(@"Path details %@",path);


                     NSURL* myImagePath = nil;
                     myImagePath =
                     [[NSBundle mainBundle] URLForResource:path
                                             withExtension:@"png"];



 //here we change the path of Image which is a string to a URL
                     NSURL *yourURL = [NSURL fileURLWithPath:path];

                     CKAsset* myImageAsset = nil;
                     myImageAsset =
                     [[CKAsset alloc] initWithFileURL:yourURL];




                [entitiesName setObject: myImageAsset
                              forKey:@"myImage"];




                [publicDatabase saveRecord: entitiesName
                          completionHandler:^(CKRecord *savedState, NSError *error) {
                              if (error) {
                                  NSLog(@"ERROR SAVING: %@", error);
                              }


                          }];



                 }




            }

【讨论】:

  • 您应该将文件写入temporaryDirectory而不是documentsDirectory
【解决方案3】:

我做了一些不同的事情:我创建了一个可以在多个地方使用的类,并且由于 Swift 具有有效的反初始化功能(与 C++ 不同),它会自行清理:

//
//  ImageAsset.swift
//

import CloudKit
import UIKit

class ImageAsset {
    
    let image:UIImage
    
    var url:NSURL?
    
    var asset:CKAsset? {
        get {
            let data = UIImagePNGRepresentation(self.image)
            self.url = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(NSUUID().UUIDString+".dat")
            if let url = self.url {
                do {
                    try data!.writeToURL(url, options: [])
                } catch let e as NSError {
                    print("Error! \(e)")
                }
                return CKAsset(fileURL: url)
            }
            return nil
        }
    }
    
    init(image:UIImage){
        self.image = image
    }
    
    deinit {
        if let url = self.url {
            do {
                try NSFileManager.defaultManager().removeItemAtURL(url) }
            catch let e {
                print("Error deleting temp file: \(e)")
            }
        }
    }
    
    
}

这里有一个单元测试来练习它(假设测试目标中有一个名为秒表的图像):

//
//  ImageExtensionTests.swift
//

import CloudKit
import XCTest
@testable import BudgetImpactEstimator

class ImageExtensionTests: XCTestCase {

    let testImageName = "stopwatch" // provide the name of an image in test bundle
    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }

    func testConvertingImageToAsset() {
        guard let image = UIImage(named: self.testImageName) else {
            XCTFail("failed to load image")
            return
        }
        let imageAsset = ImageAsset(image: image)
        XCTAssertNotNil(imageAsset)
        
        guard let asset = imageAsset.asset else {
            XCTFail("failed to get asset from image")
            return
        }
        
        print("constructed asset: \(asset)")
    }


}

原本打算将它作为 UIImage 的扩展,但后来 deinit 让我转到了一个班级。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    • 2016-05-28
    • 2020-05-01
    • 2016-03-05
    • 2011-08-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多