Swift 从SQLite中存储的字符串重新转换时,UIImage颠倒

Swift 从SQLite中存储的字符串重新转换时,UIImage颠倒,swift,string,uiimage,Swift,String,Uiimage,我保存了来自模拟器照片库的图像 然后,我将图像保存为字符串,当用户单击“保存”时,它将从字符串重新转换为图像,并放置在另一个控制器的表视图中。由于某种原因,只有某些库存照片被倒置 将图像转换为字符串的代码: extension UIImage { func toString() -> String? { let data: Data? = self.pngData() return data?.base64EncodedString(op

我保存了来自模拟器照片库的图像

然后,我将图像保存为字符串,当用户单击“保存”时,它将从字符串重新转换为图像,并放置在另一个控制器的表视图中。由于某种原因,只有某些库存照片被倒置

将图像转换为字符串的代码:

extension UIImage
{
    func toString() -> String?
    {
        let data: Data? = self.pngData()
        return data?.base64EncodedString(options: .lineLength64Characters)
    }
}
extension String
{
    func toImage() -> UIImage?
    {
        if let data = Data(base64Encoded: self, options: .ignoreUnknownCharacters)
        {
            return UIImage(data: data)
        }
    
        return nil
   }
}
    /**
INSERT operation prepared statement for family

- Parameters
    - family: Contains the data for each child and parents' name and image
*/
func insertFamily(family: Family)
{
    var insertStatement: OpaquePointer? = nil
    let result = sqlite3_prepare_v2(self.db, self.insertFamilyQuery, -1, &insertStatement, nil)
    
    if result == SQLITE_OK
    {
        // Bind values to insert statement
        sqlite3_bind_text(insertStatement, 1, (family.childName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 2, (family.childImage! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 3, (family.parentOneName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 4, (family.parentOneImage! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 5, (family.parentTwoName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 6, (family.parentTwoImage! as NSString).utf8String, -1, nil)
        
        // Execute statement
        let result = sqlite3_step(insertStatement)
        sqlite3_finalize(insertStatement)   // Destroy statement to avoid memory leak
        
        if result == SQLITE_DONE
        {
            os_log("Inserted family for %s", log: self.oslog, type: .info, family.childName!)
        }
        else
        {
            os_log("Could not insert family", log: self.oslog, type: .error)
            os_log("Expected: %s Received: %s", log: self.oslog, type: .error, SQLITE_DONE, result)
        }
    }
    else 
    {
        os_log("INSERT statement could not be prepared", log: self.oslog, type: .error)
        os_log("Expected: %s Received: %s", log: self.oslog, type: .error, SQLITE_DONE, result)
    }
}
    /**
Pull the family for the given childName if it exists

- Parameters
    - childName: The child's name associated with the family

- Returns: The family found
*/
func pullFamily(childName: String) -> Family?
{
    var family = Family()
    var readStatement: OpaquePointer? = nil
    let selectStatement = self.pullFamilyQuery + childName + "'"
    
    // Read
    if sqlite3_prepare_v2(self.db, selectStatement, -1, &readStatement, nil) == SQLITE_OK
    {
        if sqlite3_step(readStatement) == SQLITE_ROW
        {
            let childName = String(describing: String(cString: sqlite3_column_text(readStatement, 0)))
            let childImage = String(describing: String(cString: sqlite3_column_text(readStatement, 1)))
            let parentOneName = String(describing: String(cString: sqlite3_column_text(readStatement, 2)))
            let parentOneImage = String(describing: String(cString: sqlite3_column_text(readStatement, 3)))
            let parentTwoName = String(describing: String(cString: sqlite3_column_text(readStatement, 4)))
            let parentTwoImage = String(describing: String(cString: sqlite3_column_text(readStatement, 5)))
            
            family = Family(childName: childName, 
                                childImage: childImage, 
                                parentOneName: parentOneName, 
                                parentOneImage: parentOneImage, 
                                parentTwoName: parentTwoName, 
                                parentTwoImage: parentTwoImage)
        }
    }
    else 
    {
        os_log("Could not pull family by child name ", log: self.oslog, type: .error, childName)
    }
    
    sqlite3_finalize(readStatement) // Destroy statement to avoid memory leak
    return family
}
将字符串重新转换回图像的代码:

extension UIImage
{
    func toString() -> String?
    {
        let data: Data? = self.pngData()
        return data?.base64EncodedString(options: .lineLength64Characters)
    }
}
extension String
{
    func toImage() -> UIImage?
    {
        if let data = Data(base64Encoded: self, options: .ignoreUnknownCharacters)
        {
            return UIImage(data: data)
        }
    
        return nil
   }
}
    /**
INSERT operation prepared statement for family

- Parameters
    - family: Contains the data for each child and parents' name and image
*/
func insertFamily(family: Family)
{
    var insertStatement: OpaquePointer? = nil
    let result = sqlite3_prepare_v2(self.db, self.insertFamilyQuery, -1, &insertStatement, nil)
    
    if result == SQLITE_OK
    {
        // Bind values to insert statement
        sqlite3_bind_text(insertStatement, 1, (family.childName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 2, (family.childImage! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 3, (family.parentOneName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 4, (family.parentOneImage! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 5, (family.parentTwoName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 6, (family.parentTwoImage! as NSString).utf8String, -1, nil)
        
        // Execute statement
        let result = sqlite3_step(insertStatement)
        sqlite3_finalize(insertStatement)   // Destroy statement to avoid memory leak
        
        if result == SQLITE_DONE
        {
            os_log("Inserted family for %s", log: self.oslog, type: .info, family.childName!)
        }
        else
        {
            os_log("Could not insert family", log: self.oslog, type: .error)
            os_log("Expected: %s Received: %s", log: self.oslog, type: .error, SQLITE_DONE, result)
        }
    }
    else 
    {
        os_log("INSERT statement could not be prepared", log: self.oslog, type: .error)
        os_log("Expected: %s Received: %s", log: self.oslog, type: .error, SQLITE_DONE, result)
    }
}
    /**
Pull the family for the given childName if it exists

- Parameters
    - childName: The child's name associated with the family

- Returns: The family found
*/
func pullFamily(childName: String) -> Family?
{
    var family = Family()
    var readStatement: OpaquePointer? = nil
    let selectStatement = self.pullFamilyQuery + childName + "'"
    
    // Read
    if sqlite3_prepare_v2(self.db, selectStatement, -1, &readStatement, nil) == SQLITE_OK
    {
        if sqlite3_step(readStatement) == SQLITE_ROW
        {
            let childName = String(describing: String(cString: sqlite3_column_text(readStatement, 0)))
            let childImage = String(describing: String(cString: sqlite3_column_text(readStatement, 1)))
            let parentOneName = String(describing: String(cString: sqlite3_column_text(readStatement, 2)))
            let parentOneImage = String(describing: String(cString: sqlite3_column_text(readStatement, 3)))
            let parentTwoName = String(describing: String(cString: sqlite3_column_text(readStatement, 4)))
            let parentTwoImage = String(describing: String(cString: sqlite3_column_text(readStatement, 5)))
            
            family = Family(childName: childName, 
                                childImage: childImage, 
                                parentOneName: parentOneName, 
                                parentOneImage: parentOneImage, 
                                parentTwoName: parentTwoName, 
                                parentTwoImage: parentTwoImage)
        }
    }
    else 
    {
        os_log("Could not pull family by child name ", log: self.oslog, type: .error, childName)
    }
    
    sqlite3_finalize(readStatement) // Destroy statement to avoid memory leak
    return family
}
转换是基于另一个StackOverflow页面的派生:

整个“系列”的数据库存储代码:

extension UIImage
{
    func toString() -> String?
    {
        let data: Data? = self.pngData()
        return data?.base64EncodedString(options: .lineLength64Characters)
    }
}
extension String
{
    func toImage() -> UIImage?
    {
        if let data = Data(base64Encoded: self, options: .ignoreUnknownCharacters)
        {
            return UIImage(data: data)
        }
    
        return nil
   }
}
    /**
INSERT operation prepared statement for family

- Parameters
    - family: Contains the data for each child and parents' name and image
*/
func insertFamily(family: Family)
{
    var insertStatement: OpaquePointer? = nil
    let result = sqlite3_prepare_v2(self.db, self.insertFamilyQuery, -1, &insertStatement, nil)
    
    if result == SQLITE_OK
    {
        // Bind values to insert statement
        sqlite3_bind_text(insertStatement, 1, (family.childName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 2, (family.childImage! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 3, (family.parentOneName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 4, (family.parentOneImage! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 5, (family.parentTwoName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 6, (family.parentTwoImage! as NSString).utf8String, -1, nil)
        
        // Execute statement
        let result = sqlite3_step(insertStatement)
        sqlite3_finalize(insertStatement)   // Destroy statement to avoid memory leak
        
        if result == SQLITE_DONE
        {
            os_log("Inserted family for %s", log: self.oslog, type: .info, family.childName!)
        }
        else
        {
            os_log("Could not insert family", log: self.oslog, type: .error)
            os_log("Expected: %s Received: %s", log: self.oslog, type: .error, SQLITE_DONE, result)
        }
    }
    else 
    {
        os_log("INSERT statement could not be prepared", log: self.oslog, type: .error)
        os_log("Expected: %s Received: %s", log: self.oslog, type: .error, SQLITE_DONE, result)
    }
}
    /**
Pull the family for the given childName if it exists

- Parameters
    - childName: The child's name associated with the family

- Returns: The family found
*/
func pullFamily(childName: String) -> Family?
{
    var family = Family()
    var readStatement: OpaquePointer? = nil
    let selectStatement = self.pullFamilyQuery + childName + "'"
    
    // Read
    if sqlite3_prepare_v2(self.db, selectStatement, -1, &readStatement, nil) == SQLITE_OK
    {
        if sqlite3_step(readStatement) == SQLITE_ROW
        {
            let childName = String(describing: String(cString: sqlite3_column_text(readStatement, 0)))
            let childImage = String(describing: String(cString: sqlite3_column_text(readStatement, 1)))
            let parentOneName = String(describing: String(cString: sqlite3_column_text(readStatement, 2)))
            let parentOneImage = String(describing: String(cString: sqlite3_column_text(readStatement, 3)))
            let parentTwoName = String(describing: String(cString: sqlite3_column_text(readStatement, 4)))
            let parentTwoImage = String(describing: String(cString: sqlite3_column_text(readStatement, 5)))
            
            family = Family(childName: childName, 
                                childImage: childImage, 
                                parentOneName: parentOneName, 
                                parentOneImage: parentOneImage, 
                                parentTwoName: parentTwoName, 
                                parentTwoImage: parentTwoImage)
        }
    }
    else 
    {
        os_log("Could not pull family by child name ", log: self.oslog, type: .error, childName)
    }
    
    sqlite3_finalize(readStatement) // Destroy statement to avoid memory leak
    return family
}
整个“系列”的数据库读取代码:

extension UIImage
{
    func toString() -> String?
    {
        let data: Data? = self.pngData()
        return data?.base64EncodedString(options: .lineLength64Characters)
    }
}
extension String
{
    func toImage() -> UIImage?
    {
        if let data = Data(base64Encoded: self, options: .ignoreUnknownCharacters)
        {
            return UIImage(data: data)
        }
    
        return nil
   }
}
    /**
INSERT operation prepared statement for family

- Parameters
    - family: Contains the data for each child and parents' name and image
*/
func insertFamily(family: Family)
{
    var insertStatement: OpaquePointer? = nil
    let result = sqlite3_prepare_v2(self.db, self.insertFamilyQuery, -1, &insertStatement, nil)
    
    if result == SQLITE_OK
    {
        // Bind values to insert statement
        sqlite3_bind_text(insertStatement, 1, (family.childName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 2, (family.childImage! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 3, (family.parentOneName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 4, (family.parentOneImage! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 5, (family.parentTwoName! as NSString).utf8String, -1, nil)
        sqlite3_bind_text(insertStatement, 6, (family.parentTwoImage! as NSString).utf8String, -1, nil)
        
        // Execute statement
        let result = sqlite3_step(insertStatement)
        sqlite3_finalize(insertStatement)   // Destroy statement to avoid memory leak
        
        if result == SQLITE_DONE
        {
            os_log("Inserted family for %s", log: self.oslog, type: .info, family.childName!)
        }
        else
        {
            os_log("Could not insert family", log: self.oslog, type: .error)
            os_log("Expected: %s Received: %s", log: self.oslog, type: .error, SQLITE_DONE, result)
        }
    }
    else 
    {
        os_log("INSERT statement could not be prepared", log: self.oslog, type: .error)
        os_log("Expected: %s Received: %s", log: self.oslog, type: .error, SQLITE_DONE, result)
    }
}
    /**
Pull the family for the given childName if it exists

- Parameters
    - childName: The child's name associated with the family

- Returns: The family found
*/
func pullFamily(childName: String) -> Family?
{
    var family = Family()
    var readStatement: OpaquePointer? = nil
    let selectStatement = self.pullFamilyQuery + childName + "'"
    
    // Read
    if sqlite3_prepare_v2(self.db, selectStatement, -1, &readStatement, nil) == SQLITE_OK
    {
        if sqlite3_step(readStatement) == SQLITE_ROW
        {
            let childName = String(describing: String(cString: sqlite3_column_text(readStatement, 0)))
            let childImage = String(describing: String(cString: sqlite3_column_text(readStatement, 1)))
            let parentOneName = String(describing: String(cString: sqlite3_column_text(readStatement, 2)))
            let parentOneImage = String(describing: String(cString: sqlite3_column_text(readStatement, 3)))
            let parentTwoName = String(describing: String(cString: sqlite3_column_text(readStatement, 4)))
            let parentTwoImage = String(describing: String(cString: sqlite3_column_text(readStatement, 5)))
            
            family = Family(childName: childName, 
                                childImage: childImage, 
                                parentOneName: parentOneName, 
                                parentOneImage: parentOneImage, 
                                parentTwoName: parentTwoName, 
                                parentTwoImage: parentTwoImage)
        }
    }
    else 
    {
        os_log("Could not pull family by child name ", log: self.oslog, type: .error, childName)
    }
    
    sqlite3_finalize(readStatement) // Destroy statement to avoid memory leak
    return family
}
族初始值设定项

init(childName: String?, childImage: UIImage?,
     parentOneName: String?, parentOneImage: UIImage?,
     parentTwoName: String?, parentTwoImage: UIImage?)
    {
        self.childName = childName
        self.childImage = childImage
        self.parentOneName = parentOneName
        self.parentOneImage = parentOneImage
        self.parentTwoName = parentTwoName
        self.parentTwoImage = parentTwoImage
    }
Blob写入的测试代码

// Child Image
result = (family.childImage!.unrotatedPngData()?.withUnsafeBytes { bufferPointer -> Int32 in
        sqlite3_bind_blob(insertStatement, 1, bufferPointer.baseAddress, Int32(bufferPointer.count), SQLITE_TRANSIENT) })!

guard result == SQLITE_OK else {
    logger.error("[\(#function); \(#line)] ERROR: Could not bind child image")
    logger.error("[\(#function); \(#line)] ERROR: Expected - \(SQLITE_OK), Received - \(result)")
    logger.error("[\(#function); \(#line)] [ERROR: \(self.getDatabaseError())")
    return
Blob Read的测试代码

while sqlite3_step(readStatement) == SQLITE_ROW
{
    guard
        let childName = sqlite3_column_text(readStatement, 0).flatMap({ String(cString: $0) }),
        let childImageBlob = sqlite3_column_blob(readStatement, 1)
        //let parentOneName = sqlite3_column_text(readStatement, 2).flatMap({ String(cString: $0) }),
        //let parentOneImageBlob = sqlite3_column_blob(readStatement, 3),
        //let parentTwoName = sqlite3_column_text(readStatement, 4).flatMap({ String(cString: $0) }),
        //let parentTwoImageBlob = sqlite3_column_blob(readStatement, 5)
    else {
        logger.error("[\(#function); \(#line)] Could not pull family")
        logger.error("\(String(cString: sqlite3_errmsg(self.db)))")
        return families
    }
            
    // Convert child image data to child image
    let childData = Data(bytes: childImageBlob, count: Int(sqlite3_column_bytes(readStatement, 1)))
    guard let childImage = UIImage(data: childData) else {
        logger.error("Could not convert child image for child name \(childName)")
        return families
    }

    ...
}

问题不在于将base-64编码为字符串(也不在于数据库)。问题在于
pngData
不能保持图像方向。如果我在纵向模式下使用手机拍照,则生成的
UIImage
imageOrientation
UIImage.Orientation.right
,如果我在横向模式下使用顶部的音量按钮拍照,则方向是
.down
,等等。如果我直接使用这些图像,或抓取它们的
jpgData
,他们很好。但是,如果我使用
pngData
为这些图像提取
数据
,则生成的资源将分别错误地旋转90度或180度

因此,您有几个选择:

  • 您可以使用
    jpgData
    ,它确实尊重图像的方向。虽然像0.7或0.8这样的压缩值在适当的压缩下看起来相当不错,但请注意这是一种有损格式

  • 在提取
    pngData
    之前,可以重新渲染图像(必要时)。例如,这里有一个
    UIImage
    扩展来检索未旋转的PNG:

    扩展UIImage{ func unrotatedPngData()->数据{ 如果imageOrientation==.up{ 返回pngData() } let format=uigraphicsimagerenderformat() format.scale=比例 返回UIGraphicsImageRenderer(大小:大小,格式:格式)。图像{uu}in 绘图(在:.0处) }.pngData() } }
  • 应该注意的是,还有其他更模糊的方法可以使图像方向混乱,但是
    pngData
    可能是罪魁祸首。如果你想测试这篇论文,试一下
    jpgData
    ,如果这行得通,这确实是
    pngData
    的问题。但是,如果
    jpgData
    也不起作用,那么问题将在您的管道中更早地解决,但我们需要这样做才能进一步诊断它。但是智能货币是在pngData上的。无论如何,base-64编码和数据库都不是问题的根源


    尽管如此,我不建议使用base-64编码图像来存储在数据库中。这会更慢,并且会将已保存资产的大小增加⅓. 我只想将图像保存为一个BLOB:

    var语句:OpaquePointer?
    var result=sqlite3_prepare(db,“插入图像(文件名,图像)值(?,)”,-1,&语句,nil)
    保护结果==SQLITE\u正常其他{
    打印错误(单位:db)
    返回
    }
    延迟{sqlite3_finalize(语句)}
    结果=sqlite3\u绑定\u文本(语句,1,文件名,-1,SQLITE\u瞬态)
    保护结果==SQLITE\u正常其他{
    打印错误(单位:db)
    返回
    }
    结果=image.unrotatedPngData()?.withUnsafeBytes{bufferPointer->Int32 in
    result=sqlite3\u bind\u blob(语句,2,bufferPointer.baseAddress,Int32(bufferPointer.count),SQLITE\u瞬态)
    }
    保护结果==SQLITE\u正常其他{
    打印错误(单位:db)
    返回
    }
    结果=sqlite3\u步骤(语句)
    保护结果==SQLITE\u完成其他操作{
    打印错误(单位:db)
    返回
    }
    
    您也可以将图像作为BLOB获取:

    var语句:OpaquePointer?
    让result=sqlite3_prepare(db,“选择文件名,来自图像的图像”、-1、&statement,nil)
    保护结果==SQLITE_OK else{…}
    延迟{sqlite3_finalize(语句)}
    而sqlite3_步骤(语句)==SQLITE_行{
    警卫
    让filename=sqlite3\u column\u text(语句,0).flatMap({String(cString:$0)}),
    let bytes=sqlite3\u column\u blob(语句,1)
    否则{
    打印错误(单位:db)
    返回
    }
    let data=data(字节:字节,计数:Int(sqlite3\u列\u字节(语句,1)))
    guard let image=UIImage(数据:data)else{…}
    //对图像做任何你想做的事情
    }
    
    在哪里

    func printError(){
    let message=sqlite3_errmsg(db).flatMap{String(cString:$0)}???“未知错误”
    打印(信息)
    }
    
    我宁愿避免将数据完全转换为字符串……为什么首先要将其转换为字符串?只是猜测一下,但CoreImage的“新手”可能会混淆的是原点(值(0,0))实际上是左下角,而不是左上角。你的行为看起来很相似。@Chris似乎更容易将其存储在数据库中顺便说一句,所有这些
    NSString
    强制转换都可以消除。例如,
    sqlite3\u bind\u文本(insertStatement,1,(family.childName!作为NSString)。utf8String,-1,nil)
    可以简化为
    sqlite3\u bind\u文本(insertStatement,1,family.childName!,-1,nil)
    。我建议不要对最后一个参数使用
    nil
    ,而是定义为
    SQLITE\u TRANSIENT
    。我喜欢这个想法。最初尝试以blob形式存储,但直到现在才在网上找到任何内容。我更新了我原来的帖子,加入了我目前用于存储和提取数据的全部功能。如何将您的方法合并到现有代码中?因此,对于withUnsafeBytes部分,我得到的错误是“表达式的类型在没有更多上下文的情况下是不明确的”,这似乎是由于withUnsafeBytes被弃用所致。我不确定从其他帖子中找到的解决方案