Swift — UIKit 之(11)—— 持久层|SQLite 图片的存取

2023-11-07 10:10

本文主要是介绍Swift — UIKit 之(11)—— 持久层|SQLite 图片的存取,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

        • 0. 本篇重点
        • 1. 目标演示
        • 2. 引入 SQLite 库
          • 2.1 新建 Single View App
          • 2.2 新建一个纯代码文件 SQLiteManager.swift(数据库的基本操作)
          • 2.3 新建一个纯代码文件 ImageDAL.swift(对图片的数据库操作类)
          • 2.4 新建一个纯代码文件 GetImageFromWeb.swift (用网络上获取图片)
          • 2.5 添加本地资源文件
          • 2.6 添加 SQLite 库
          • 2.7 通过建立一个Objective-C 文件来获得桥接头文件
          • 2.8 删除 Objective-C 文件
          • 2.9 在桥接文件中引入 SQLite 的头文件
        • 3. 编写 SQLiteManager.swift 代码
        • 4. 编写 ImageDAL.swift 代码
        • 5. 编写 GetImageFromWeb.swift 代码
        • 6. 设计界面
        • 7. 编写 ViewController.swift 代码
        • 8. 效果演示

0. 本篇重点

本篇主要是总结如何用 SQLite 来存储和读取图片资源。

首先先了解一个什么是 BLOB 数据类型:

BLOB:二进制大对象,是一个可以存储二进制文件的容器。一些大文件,如图片视频音频等,在数据库中就是用BLOB 存储的。

  • 从远端下载图片:WebURLSession 的应用
  • 本地加载图片
  • 保存图片到数据库
  • 从数据库中读取图片
1. 目标演示

image-20200512214938890

2. 引入 SQLite 库
2.1 新建 Single View App
2.2 新建一个纯代码文件 SQLiteManager.swift(数据库的基本操作)
2.3 新建一个纯代码文件 ImageDAL.swift(对图片的数据库操作类)
2.4 新建一个纯代码文件 GetImageFromWeb.swift (用网络上获取图片)
2.5 添加本地资源文件

image-20200512213014197

2.6 添加 SQLite 库

在这里插入图片描述
选择最新版
在这里插入图片描述

2.7 通过建立一个Objective-C 文件来获得桥接头文件

我们要做的就是调用引入的 libsqlite3.tbd 里面的接口。

因为它是用 C 语言写的,所以我们需要一个桥接头文件(bridging header)。

image-20200512170516175

image-20200512170613669

2.8 删除 Objective-C 文件
2.9 在桥接文件中引入 SQLite 的头文件

image-20200512170724065

这样我们就可以调用引用进来的 libsqlite3.tbd 里面的接口了。

3. 编写 SQLiteManager.swift 代码

与之前(10)中不同之处在于:我们新增了两个函数:

  • execSaveBlob:保存 Blob 类型的数据
  • execLoadBlob:加载 Blob 类型的数据
//
//  GetIamgeFromWeb.swift
//  S1102
//
//  Created by Hedon - on 2020/5/12.
//  Copyright © 2020 Hedon -. All rights reserved.
//
import Foundationclass SQLiteManager:NSObject
{private var dbPath:String!    //数据库所存储的文件所在路径private var database:OpaquePointer?=nil   //数据库,是一个指针,指向一个结构体//共享一个实例化对象 —————— 单例变量static var shareInstance:SQLiteManager{return SQLiteManager()}override init() {let dirpath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!dbPath =  dirpath.appendingPathComponent("app.sqlite").path}//打开数据库func openDB() -> Bool {let result = sqlite3_open(dbPath, &database)if result != SQLITE_OK{print("打开数据库失败\n")return false}return true}//关闭数据库func closeDB(){sqlite3_close(database)}/**执行数据库语句1. 创建 create2. 插入 insert3. 更新 update4. 删除 delete*/func execNoneQuerySQL(sql:String)->Bool{var errMsg:UnsafeMutablePointer<Int8>? = nil   //错误信息的指针(不安全、可修改)let cSql = sql.cString(using: String.Encoding.utf8)!  //给sql语句进行编码/**参数说明:1. 已打开的数据库句柄2. 执行的sql语句3. 回调函数4. 自定义指针,会传递到回调函数内5. 错误信息指针*///执行成功if sqlite3_exec(database, cSql, nil, nil, &errMsg) == SQLITE_OK {return true}//执行不成功let msg = String.init(cString: errMsg!)print(msg)return false}/**执行数据库语句1. 查询*/func execQuerySQL(sql:String)->[[String:AnyObject]]?{let cSql = sql.cString(using: String.Encoding.utf8)!  //对sql语句进行utf8编码var statement:OpaquePointer? = nil  //语句的指针/**参数说明:1. 已打开的数据库句柄2. 执行的sql语句3. 以字节为单位的sql语句长度,-1表示自动计算4. 语句句柄,据此获取查询结果,需要调用 sqlite3_finalize 释放5. 未使用的指针地址,通常传入nil*///执行不成功if sqlite3_prepare_v2(database, cSql, -1, &statement, nil) != SQLITE_OK{sqlite3_finalize(statement)print("执行 \(sql) 错误\n")let errmsg = sqlite3_errmsg(database)if errmsg != nil{print(errmsg!)}return nil}//成功:需要从 statement 里面取数据,定义 rows 变量来接收数据var rows = [[String:AnyObject]]()//每次取 1 行while sqlite3_step(statement) == SQLITE_ROW{rows.append(record(stmt: statement!))}//最后释放空间sqlite3_finalize(statement)//返回结果return rows}//读一行记录一行private func record(stmt:OpaquePointer)->[String:AnyObject]{var row = [String:AnyObject]()//遍历所有列,获取每一列的信息for col in 0..<sqlite3_column_count(stmt){let cName = sqlite3_column_name(stmt, col) //获取列名let name = String(cString: cName!,encoding: String.Encoding.utf8)var value:AnyObject?switch sqlite3_column_type(stmt, col) {case SQLITE_FLOAT:value = sqlite3_column_double(stmt, col) as AnyObjectcase SQLITE_INTEGER:value = Int(sqlite3_column_int(stmt, col)) as AnyObjectcase SQLITE_TEXT:let cText = sqlite3_column_text(stmt, col)value = String.init(cString: cText!) as AnyObjectcase SQLITE_NULL:value = NSNull()default:print("不支持的数据类型\n")}row[name!] = value ?? NSNull()}return row}//保存 BLOB 类型的数据到数据库中func execSaveBlob(sql: String, blob: NSData){let csql = sql.cString(using: .utf8)!var statement:OpaquePointer? = nil/**参数说明:1. 已打开的数据库句柄2. 执行的sql语句3. 以字节为单位的sql语句长度,-1表示自动计算4. 语句句柄,据此获取查询结果,需要调用 sqlite3_finalize 释放5. 未使用的指针地址,通常传入nil*///如果执行不成功if sqlite3_prepare_v2(database, csql, -1, &statement, nil) != SQLITE_OK{sqlite3_finalize(statement)print("Prepare error: \(sql)\n")return}let paramCnt = sqlite3_bind_parameter_count(statement)//我们本项目只支持一个传一张照片if paramCnt != 1{print("只支持一个参数:\(sql)\n")sqlite3_finalize(statement)return}/**BLOB绑定函数:sqlite3_bind_blob(<#T##OpaquePointer!#>, <#T##Int32#>, <#T##UnsafeRawPointer!#>, <#T##n: Int32##Int32#>, <#T##((UnsafeMutableRawPointer?) -> Void)!##((UnsafeMutableRawPointer?) -> Void)!##(UnsafeMutableRawPointer?) -> Void#>)参数说明:1. 准备语句指针,该指针要由 sqlite3_prepare() 函数得到2. 要绑定的 BLOB 下标,从1开始3. BLOB 数据的只恨(blob.btyes 就是获取首指针)4. BLOB 数据长度(blob.length 就是获取长度)5. 析构回调函数,一般默认为空*///如果BLOB绑定失败(会先进行绑定,绑定成功这不执行if里面的东西,但是已经进行了绑定)if sqlite3_bind_blob(statement, 1, blob.bytes, Int32(blob.length), nil) != SQLITE_OK{print("Bind blob error: \(sql)\n")sqlite3_finalize(statement)return}//执行图片保存操作let result = sqlite3_step(statement)if result != SQLITE_OK && result != SQLITE_DONE{print("extue blob error: \(sql)\n")sqlite3_finalize(statement)return}//如果能到这里,说明一切顺利sqlite3_finalize(statement)return}//从数据库中加载 BLOB 类型的数据func execLoadBlob(sql: String) -> Data?{let csql = sql.cString(using: String.Encoding.utf8)var statement:OpaquePointer? = nil/**参数说明:1. 已打开的数据库句柄2. 执行的sql语句3. 以字节为单位的sql语句长度,-1表示自动计算4. 语句句柄,据此获取查询结果,需要调用 sqlite3_finalize 释放5. 未使用的指针地址,通常传入nil*///如果执行不成功if sqlite3_prepare_v2(database, csql, -1, &statement, nil) != SQLITE_OK{sqlite3_finalize(statement)print("执行\(sql)错误\n")if let errmsg = sqlite3_errmsg(database){print(errmsg)}return nil}//循环读取:每次一行while sqlite3_step(statement) == SQLITE_ROW{//sqlite3_column_blob返回blob字段的内容的指针if let dataBlob = sqlite3_column_blob(statement!, 0){//sqlite3_column_bytes来返blob字段的长度let dataBlobLength = sqlite3_column_bytes(statement, 0)//将 blob 字段转换为 Data 数据let data = Data(bytes: dataBlob, count: Int(dataBlobLength))sqlite3_finalize(statement)//已经读取到图片了return data}}//如果走到这里,说明没有读取到图片sqlite3_finalize(statement)return nil}
}
4. 编写 ImageDAL.swift 代码

主要是三个函数:

  • initDB():初始化数据库
  • SaveImage:保存图片
  • LoadImage:加载图片
//
//  ImageDAL.swift
//  S1102
//
//  Created by Hedon - on 2020/5/12.
//  Copyright © 2020 Hedon -. All rights reserved.
//import Foundationimport UIKit   //我们后面用到 UIImage,所以需要导入 UIKitclass ImageDAL
{//初始化数据库static func initDB(){let sqlite = SQLiteManager.shareInstance//如果打开数据库失败,则返回if !sqlite.openDB(){return}let createSql = "CREATE TABLE IF NOT EXISTS student('id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + "'name' TEXT, 'phone' TEXT, 'pic' BLOB);"if !sqlite.execNoneQuerySQL(sql: createSql){sqlite.closeDB()return}let stu0 = "INSERT OR REPLACE INTO student(id,name,phone) VALUES(1,'张三','zhangsan@whu.edu.cn');"if !sqlite.execNoneQuerySQL(sql: stu0){sqlite.closeDB()return}let queryresult = sqlite.execQuerySQL(sql: "SELECT * FROM student;")print(queryresult!)sqlite.closeDB()}//保存图片static func SaveImage(id: Int, img: UIImage?){if img == nil{return}let sqlite = SQLiteManager.shareInstanceif !sqlite.openDB() {return}//我们这里设置一次只能上传一个图片let sql = "UPDATE student SET pic = ? WHERE id = \(id)"let data = img!.jpegData(compressionQuality: 1.0) as NSData?sqlite.execSaveBlob(sql: sql, blob: data!)sqlite.closeDB()return}//加载图片static func LoadImage(id: Int) -> UIImage{let sqlite = SQLiteManager.shareInstance//如果打开数据库失败,就显示我们预设的“错误图片”if !sqlite.openDB() {return UIImage(named: "nopic")!}let sql = "SELECT pic FROM student where id = \(id)"let data = sqlite.execLoadBlob(sql: sql)sqlite.closeDB()if data != nil{//打开数据库成功并且找到图片return UIImage(data: data!)!}else{//打开数据库成功但是没有找到图片return UIImage(named: "nopic")!}}
}
5. 编写 GetImageFromWeb.swift 代码
//
//  GetIamgeFromWeb.swift
//  S1102
//
//  Created by Hedon - on 2020/5/12.
//  Copyright © 2020 Hedon -. All rights reserved.
//import UIKit//为 UIImageView 扩展一个函数:从WEB异步下载图片
extension UIImageView
{func downloadAsyncFrom(url: String){let urlNet = URL(string: url)let task = URLSession.shared.dataTask(with: urlNet!){(data,response,error) inif let nsd = data{DispatchQueue.main.async {self.image = UIImage(data: nsd, scale: 1)self.contentMode = .scaleAspectFit}}}task.resume()}
}
6. 设计界面

image-20200512213602228

7. 编写 ViewController.swift 代码
//
//  ViewController.swift
//  S1102
//
//  Created by Hedon - on 2020/5/12.
//  Copyright © 2020 Hedon -. All rights reserved.
//import UIKitclass ViewController: UIViewController {@IBOutlet weak var imgView1: UIImageView!   @IBOutlet weak var imgView2: UIImageView!override func viewDidLoad() {super.viewDidLoad()ImageDAL.initDB()  //初始化数据库}//从网络地址下载图片@IBAction func downloadPressed(_ sender: UIButton) {imgView1.downloadAsyncFrom(url: "https://profile.csdnimg.cn/B/A/D/1_hedon954")}//从本地资源文件加载@IBAction func localPressed(_ sender: UIButton) {imgView1.image = UIImage(named: "china")}//将 imageView1 里面的图片保存到数据库@IBAction func savePressed(_ sender: UIButton) {ImageDAL.SaveImage(id: 1, img: imgView1.image)}//从数据库加载刚刚保存的图片显示在 imageView2@IBAction func loadPressed(_ sender: UIButton) {imgView2.image = ImageDAL.LoadImage(id: 1) }
}
8. 效果演示

image-20200512214938890

Over~

这篇关于Swift — UIKit 之(11)—— 持久层|SQLite 图片的存取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/Hedon954/article/details/106086086
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/362913

相关文章

Java实现数据库图片上传功能详解

《Java实现数据库图片上传功能详解》这篇文章主要为大家详细介绍了如何使用Java实现数据库图片上传功能,包含从数据库拿图片传递前端渲染,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、前言2、数据库搭建&nbsChina编程p; 3、后端实现将图片存储进数据库4、后端实现从数据库取出图片给前端5、前端拿到

Python使用PIL库将PNG图片转换为ICO图标的示例代码

《Python使用PIL库将PNG图片转换为ICO图标的示例代码》在软件开发和网站设计中,ICO图标是一种常用的图像格式,特别适用于应用程序图标、网页收藏夹图标等场景,本文将介绍如何使用Python的... 目录引言准备工作代码解析实践操作结果展示结语引言在软件开发和网站设计中,ICO图标是一种常用的图像

SpringBoot集成图片验证码框架easy-captcha的详细过程

《SpringBoot集成图片验证码框架easy-captcha的详细过程》本文介绍了如何将Easy-Captcha框架集成到SpringBoot项目中,实现图片验证码功能,Easy-Captcha是... 目录SpringBoot集成图片验证码框架easy-captcha一、引言二、依赖三、代码1. Ea

如何使用CSS3实现波浪式图片墙

《如何使用CSS3实现波浪式图片墙》:本文主要介绍了如何使用CSS3的transform属性和动画技巧实现波浪式图片墙,通过设置图片的垂直偏移量,并使用动画使其周期性地改变位置,可以创建出动态且具有波浪效果的图片墙,同时,还强调了响应式设计的重要性,以确保图片墙在不同设备上都能良好显示,详细内容请阅读本文,希望能对你有所帮助...

Python脚本实现图片文件批量命名

《Python脚本实现图片文件批量命名》这篇文章主要为大家详细介绍了一个用python第三方库pillow写的批量处理图片命名的脚本,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言源码批量处理图片尺寸脚本源码GUI界面源码打包成.exe可执行文件前言本文介绍一个用python第三方库pi

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图

Python利用PIL进行图片压缩

《Python利用PIL进行图片压缩》有时在发送一些文件如PPT、Word时,由于文件中的图片太大,导致文件也太大,无法发送,所以本文为大家介绍了Python中图片压缩的方法,需要的可以参考下... 有时在发送一些文件如PPT、Word时,由于文件中的图片太大,导致文件也太大,无法发送,所有可以对文件中的图

java获取图片的大小、宽度、高度方式

《java获取图片的大小、宽度、高度方式》文章介绍了如何将File对象转换为MultipartFile对象的过程,并分享了个人经验,希望能为读者提供参考... 目China编程录Java获取图片的大小、宽度、高度File对象(该对象里面是图片)MultipartFile对象(该对象里面是图片)总结java获取图片

Java实战之自助进行多张图片合成拼接

《Java实战之自助进行多张图片合成拼接》在当今数字化时代,图像处理技术在各个领域都发挥着至关重要的作用,本文为大家详细介绍了如何使用Java实现多张图片合成拼接,需要的可以了解下... 目录前言一、图片合成需求描述二、图片合成设计与实现1、编程语言2、基础数据准备3、图片合成流程4、图片合成实现三、总结前

使用Python实现图片和base64转换工具

《使用Python实现图片和base64转换工具》这篇文章主要为大家详细介绍了如何使用Python中的base64模块编写一个工具,可以实现图片和Base64编码之间的转换,感兴趣的小伙伴可以了解下... 简介使用python的base64模块来实现图片和Base64编码之间的转换。可以将图片转换为Bas