本文主要是介绍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. 目标演示
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 文件来获得桥接头文件
我们要做的就是调用引入的 libsqlite3.tbd 里面的接口。
因为它是用 C 语言写的,所以我们需要一个桥接头文件(bridging header)。
2.8 删除 Objective-C 文件
2.9 在桥接文件中引入 SQLite 的头文件
这样我们就可以调用引用进来的 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. 设计界面
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. 效果演示
Over~
这篇关于Swift — UIKit 之(11)—— 持久层|SQLite 图片的存取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!