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

相关文章

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

Spring MVC 图片上传

引入需要的包 <dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-

Prompt - 将图片的表格转换成Markdown

Prompt - 将图片的表格转换成Markdown 0. 引言1. 提示词2. 原始版本 0. 引言 最近尝试将图片中的表格转换成Markdown格式,需要不断条件和优化提示词。记录一下调整好的提示词,以后在继续优化迭代。 1. 提示词 英文版本: You are an AI assistant tasked with extracting the content of

研究人员在RSA大会上演示利用恶意JPEG图片入侵企业内网

安全研究人员Marcus Murray在正在旧金山举行的RSA大会上公布了一种利用恶意JPEG图片入侵企业网络内部Windows服务器的新方法。  攻击流程及漏洞分析 最近,安全专家兼渗透测试员Marcus Murray发现了一种利用恶意JPEG图片来攻击Windows服务器的新方法,利用该方法还可以在目标网络中进行特权提升。几天前,在旧金山举行的RSA大会上,该Marcus现场展示了攻击流程,

恶意PNG:隐藏在图片中的“恶魔”

&lt;img src=&quot;https://i-blog.csdnimg.cn/blog_migrate/bffb187dc3546c6c5c6b8aa18b34b962.jpeg&quot; title=&quot;214201hhuuhubsuyuukbfy_meitu_1_meitu_2.jpg&quot;/&gt;&lt;/strong&gt;&lt;/span&gt;&lt;

PHP抓取网站图片脚本

方法一: <?phpheader("Content-type:image/jpeg"); class download_image{function read_url($str) { $file=fopen($str,"r");$result = ''; while(!feof($file)) { $result.=fgets($file,9999); } fclose($file); re

(入门篇)JavaScript 网页设计案例浅析-简单的交互式图片轮播

网页设计已经成为了每个前端开发者的必备技能,而 JavaScript 作为前端三大基础之一,更是为网页赋予了互动性和动态效果。本篇文章将通过一个简单的 JavaScript 案例,带你了解网页设计中的一些常见技巧和技术原理。今天就说一说一个常见的图片轮播效果。相信大家在各类电商网站、个人博客或者展示页面中,都看到过这种轮播图。它的核心功能是展示多张图片,并且用户可以通过点击按钮,左右切换图片。

sqlite不支持中文排序,采用java排序

方式一 不支持含有重复字段进行排序 /*** sqlite不支持中文排序,改用java排序* 根据指定的对象属性字段,排序对象集合,顺序* @param list* @param field* @return*/public static List sortListByField(List<?> list,String field){List temp = new ArrayList(

matplotlib绘图中插入图片

在使用matplotlib下的pyplot绘图时,有时处于各种原因,需要采用类似贴图的方式,插入外部的图片,例如添加自己的logo,或者其他的图形水印等。 一开始,查找到的资料都是使用imshow,但是这会有带来几个问题,一个是图形的原点发生了变化,另外一个问题就是图形比例也产生了变化,当然最大的问题是图形占据了整个绘图区域,完全喧宾夺主了,与我们设想的只在绘图区域中占据很小的一块不相符。 经