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 图片的存取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/362913

相关文章

C#实现将Excel表格转换为图片(JPG/ PNG)

《C#实现将Excel表格转换为图片(JPG/PNG)》Excel表格可能会因为不同设备或字体缺失等问题,导致格式错乱或数据显示异常,转换为图片后,能确保数据的排版等保持一致,下面我们看看如何使用C... 目录通过C# 转换Excel工作表到图片通过C# 转换指定单元格区域到图片知识扩展C# 将 Excel

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效

Python实现AVIF图片与其他图片格式间的批量转换

《Python实现AVIF图片与其他图片格式间的批量转换》这篇文章主要为大家详细介绍了如何使用Pillow库实现AVIF与其他格式的相互转换,即将AVIF转换为常见的格式,比如JPG或PNG,需要的小... 目录环境配置1.将单个 AVIF 图片转换为 JPG 和 PNG2.批量转换目录下所有 AVIF 图

详解如何通过Python批量转换图片为PDF

《详解如何通过Python批量转换图片为PDF》:本文主要介绍如何基于Python+Tkinter开发的图片批量转PDF工具,可以支持批量添加图片,拖拽等操作,感兴趣的小伙伴可以参考一下... 目录1. 概述2. 功能亮点2.1 主要功能2.2 界面设计3. 使用指南3.1 运行环境3.2 使用步骤4. 核

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当