当前位置: 首页 > news >正文

iOS实际开发中使用Alamofire实现多文件上传(以个人相册为例)

引言

在移动应用中,图片上传是一个常见的功能,尤其是在个人中心或社交平台场景中,用户经常需要上传图片到服务器,用以展示个人风采或记录美好瞬间。然而,实现多图片上传的过程中,如何设计高效的上传逻辑并结合用户体验显得尤为重要。

本篇博客将通过一个具体实例——个人中心的相册功能,来介绍如何使用 Alamofire 实现多文件上传。我们将从图片选择开始,逐步实现图片数据的封装、上传请求的构建以及上传进度的展示,最终完成一套完整的多图片上传解决方案。如果你也正在寻找一种高效的实现方法,希望本文能为你提供一些思路与启发。

功能需求分析

用户的个人信息页面会展示用户的一些精选照片墙,为了补充这个信息在用户编辑页添加了的上传相册图片的功能,上传到相册的图片最多为6张,可以多选,但是已选择和已上传的总数不能超过6。

关于相册选择我们可以使用系统为我们提供的PHPickerViewController来实现,使用起来十分方便,可以直接设置选择资源类型和最大个数,关于它的使用在之前的博客中也有过介绍,有兴趣的同学可以看下面这篇博客。

iOS 系统提供的媒体资源选择器(PHPickerViewController)-CSDN博客文章浏览阅读1.6k次,点赞10次,收藏20次。在前面的博客中我们已经介绍了一个系统为我们提供的媒体选择器UIImagePickerController,它的功能很强大,但是唯一的不足就是只能选取单个媒体资源,而PHPickerViewController恰恰弥补了这一空缺。PHPickerViewController是iOS 14及更高版本中引入的一个现代化媒体选择器,旨在替代UIImagePickerController。它不仅提供了更灵活的媒体选择功能,还拥有更现代的用户界面。_phpickerviewcontrollerhttps://blog.csdn.net/weixin_39339407/article/details/140918416选择了相册的图片之后,下一步就上传图片资源到相册。Alamofire为我们提供了完整的上传方法但是由于我们需要上传多个资源,所以仍然有一些数据来需要我们处理。

接口设计

在讨论如何实现多文件上传之前,我们首先需要了解一下后端的接口设计,每个服务端对于文件上传的方式都并不是一成不变的。

在上传相册图片的需求中,整个过程被分成了两个接口:

  1. 文件上传接口:负责文件上传,需要设置文件的类型,以及自定义参数,来决定文件上传是用来做什么的,每次只能上传一个文件。会返回上传后的资源地址。
  2. 相册更新接口:使用上传后的资源地址构建列表,来更新相册。

代码实现

那我们跳过关于UI的布局刷新以及照片的选择功能,将所有精力集中到文件上传,照片选择完成之后会传递回来一个等待上传的图片数组,为了更符合业务场景,我们需要根据图片数组来构建一组新的相册模型,并添加到相册列表,然后执行上传操作。

选择照片后的处理:

    /// 选择相册图片/// - Parameter images: 图片数组func uploadAlbum(images: [UIImage]) {//1. 先刷新for image in images {let model = MWEditPhotoModel()model.image = imagemodel.uploadStatus = .noneeditUserPhotos.append(model)}self.tableView.reloadData()//2.开始执行上传操作self.presenter.requestUploadAlbumData(editPhotoModels: editUserPhotos) {[weak self] inguard let self = self else { return }self.tableView.reloadData()}}
  1. 根据选择上传的图片数组构建新的相册模型,并添加到相册列表中,刷新列表。
  2. 将整个相册列表当做参数传递到上传方法中。

首先会根据传递进来的相册列表数据进行过滤,获取到需要上传的进行上传:

    /// 信号量最大并发控制为2private let semaphore = DispatchSemaphore(value: 2)
  /// 上传用户相册func requestUploadAlbumData(editPhotoModels:[MWEditPhotoModel], completion: (() -> Void)?) {uploadAlbumCompletion = completion// 获取需要上传的模型let needUploadModels = editPhotoModels.filter { $0.uploadStatus == .none && $0.isAdd == false }// 上传图片var needUploadCount = needUploadModels.countlet semaphore = self.semaphoreglobalQueue.async {[weak self] inguard let self = self else {return}for model in needUploadModels {model.uploadStatus = .uploadingsemaphore.wait()MWLogHelper.debug("开始上传图片",context: "MWEditProfilePresenter")self.uploadAlbumImages(model) {[weak self] success inMWLogHelper.debug("上传图片结果\(success)",context: "MWEditProfilePresenter")defer {// 无论结果如何,确保释放信号量semaphore.signal()}guard let self = self else {return}if success == false {model.uploadStatus = .fail}needUploadCount -= 1if needUploadCount == 0 {self.requestUpdateAlbumData(editPhotoModels: editPhotoModels)}}}}}
  1. 首先创建了一个信号量来控制可以同时上传的最大个数。
  2. 过滤出需要上传的数据执行上传操作。
  3. 当所有资源上传完成之后在根据列表数据来更新相册。

那我们先来看一下上传方法:

    // 上传相册图片private func uploadAlbumImages(_ editPhotoModel: MWEditPhotoModel,complection:((Bool)->Void)? = nil) {guard let imageData = editPhotoModel.image?.compressImageQuality(maxSize: Int(640 * 640)) else {complection?(false)return}MWNetworkHelper.upLoadFile(data: imageData, kinds: "photo", fileType: MWUploadFileType.image, fileName: "photo.jpg") { result inif let imageUrl = result?["photo"] {var albumModel =  MWProfileAlbumModel()albumModel.littleUrl = imageUrlalbumModel.srcUrl = imageUrleditPhotoModel.albumModel = albumModeleditPhotoModel.uploadStatus = .successcomplection?(true)} else {editPhotoModel.uploadStatus = .failcomplection?(false)}} notRedirectErrorCallback: {complection?(false)MWToast.showToast(MWLocaleStringHelper.getString("Upload Failed"))}}
    /// 上传文件/// - Parameters:///  - data: 文件数据///  - kinds: 参数///  cover 封面, facebookSharedImage 分享, portrait 头像, pic  图片, zip  压缩包, video  视频,   , ,photo 相册, photoProcess  相册模糊, liveCover 直播间封面 photoWall, 个人主页, photoWallBig 个人主页大图///  - fileType: 文件类型///  - fileName: 文件名称///  - completion: 上传结果public class func upLoadFile(data: Data, kinds: String, fileType: MWUploadFileType, fileName: String, completion: @escaping (([String: String]?) -> Void), notRedirectErrorCallback: @escaping () -> Void) {// 上传地址var url = "\(MWAPIHost.host)/rest/api/usergate/uploadFile"// 上传headervar headers = [String:String]()// 重定向let redirectHandler = MWRedirectHandler(data: data, kinds: kinds, fileType: fileType, fileName: fileName, completion: completion)AF.upload(multipartFormData: { multipartFormData in//图片var mimeType = "image/jpg"if fileType == .video {mimeType = "video/mp4"}multipartFormData.append(data, withName: "file",fileName: fileName, mimeType: mimeType)// 上传 kinds 参数if let kindsData = kinds.data(using: .utf8) {multipartFormData.append(kindsData, withName: "kinds")}}, to: url,method: .post, headers: MWAPIEncryEndpoint.api_postDynamic.headers).redirect(using: redirectHandler).responseJSON { response inif response.response?.statusCode != 307 {notRedirectErrorCallback()}}}
  1. 文件名称和文件类型需要根据文件的具体内容来设置。
  2. kinds为服务端自定的参数。
  3. 关于重定向的暂且可以不考虑,我们的文件上传接口进行了接口的重定向,一般来讲是不需要单独处理的。
  4. 文件上传成功之后根据返回结果,构建了新的相册数据,并同步到相册列表。

最后我们只需要等待所有文件上传完成之后,将所有数据同步到相册接口即可:

    /// 更新用户相册/// - Parameters:///  - editPhotoModels: 编辑相册模型///  - completion: 完成回调func requestUpdateAlbumData(editPhotoModels:[MWEditPhotoModel]) {let needUpdateModels = editPhotoModels.filter { $0.uploadStatus == .success || $0.uploadStatus == .noNeed }let albumList = needUpdateModels.map { $0.albumModel }var photoList = [[String: Any]]()for albumModel in albumList {var dict = [String: Any]()dict["littleUrl"] = albumModel?.littleUrldict["srcUrl"] = albumModel?.srcUrlphotoList.append(dict)}let params: [String: Any] = ["photoList": photoList]MWNetworkHelper.request(endpoint: MWAPIEncryEndpoint.api_updateUserPhotos, parameters: params) {[weak self] (model, data, error) inguard let self = self else { return }if error != nil {for model in needUpdateModels {if model.uploadStatus == .success {model.uploadStatus = .fail}}}self.uploadAlbumCompletion?()}}

结语

本篇博客主要介绍了使用Alamofire实现多文件的上传功能,关于文件上传的具体方案还是需要根据服务端的接口设计来实施。本文以同步用户相册为例,将上传相册图片分为两个部分,上传和同步,当所有资源上传完成之后,执行相册的同步操作。并使用信号量来控制上传的最大并发数。

希望本篇博客能够在文件上传中给大家一些启发。

相关文章:

iOS实际开发中使用Alamofire实现多文件上传(以个人相册为例)

引言 在移动应用中,图片上传是一个常见的功能,尤其是在个人中心或社交平台场景中,用户经常需要上传图片到服务器,用以展示个人风采或记录美好瞬间。然而,实现多图片上传的过程中,如何设计高效的上传逻辑并…...

如何将分割的mask转为为分割标签

将分割的mask转换为分割标签通常涉及将每个像素的类别标识(在mask中以不同的灰度值或颜色表示)转换为整数标签。这些标签通常用于机器学习或深度学习模型的训练、验证和测试阶段。 使用方式,控制台或者命令行使用以下命令: pyth…...

【动手学电机驱动】STM32-MBD(5)Simulink 模型开发之 PWM 输出

STM32-MBD(1)安装 Simulink STM32 硬件支持包 STM32-MBD(2)Simulink 模型部署入门 STM32-MBD(3)Simulink 状态机模型的部署 STM32-MBD(4)Simulink 状态机实现按键控制 STM32-MBD&…...

MySQL进阶突击系列(05)突击MVCC核心原理 | 左右护法ReadView视图和undoLog版本链强强联合

2024小结:在写作分享上,这里特别感谢CSDN社区提供平台,支持大家持续学习分享交流,共同进步。社区诚意满满的干货,让大家收获满满。 对我而言,珍惜每一篇投稿分享,每一篇内容字数大概6000字左右&…...

vue2日历组件

这个代码可以直接运行&#xff0c;未防止有组件库没安装&#xff0c;将组件库的代码&#xff0c;转成文字了 vue页面 <template><div class"about"><div style"height: 450px; width: 400px"><div style"height: 100%; overflo…...

【PyQt】多行纯文本框

[toc]qt多行纯文本框 QPlainTextEdit QPlainTextEdit 是可以多行的纯文本编辑框 文本浏览框 内置了一个** QTextDocument **类型的对象 &#xff0c;存放文档。 1.信号&#xff1a;文本被修改 当文本框中的内容被键盘编辑&#xff0c;被点击就会发出 textChanged 信号&…...

workerman5.0篇〡异步非阻塞协程HTTP客户端

概述 workerman/http-client 是一个异步http客户端组件。所有请求响应异步非阻塞&#xff0c;内置连接池&#xff0c;消息请求和响应符合PSR7规范。 Workerman 5.0 版本中的异步HTTP协程客户端组件是一个基于PHP协程的高性能HTTP客户端&#xff0c;它能够充分利用PHP的异步特…...

JavaScript 延迟加载的方法( 7种 )

JavaScript脚本的延迟加载&#xff08;也称为懒加载&#xff09;是指在网页的主要内容已经加载并显示给用户之后&#xff0c;再加载或执行额外的JavaScript代码。这样做可以加快页面的初始加载速度&#xff0c;改善用户体验&#xff0c;并减少服务器的压力。 以下是几种常见的延…...

python+pymysql

python操作mysql 一、python操作数据库 1、下载pymysql 库&#xff0c; 方法一&#xff1a;pip3 install pymysql 或pip install pymysql 方法二&#xff1a;在pycharm中setting下载pymysql 2、打开虚拟机上的数据库 3、pymysql连接 dbpymysql.Connection(host&qu…...

基于 Selenium 实现上海大学校园网自动登录

基于 Selenium 实现上海大学校园网自动登录 一、技术方案 核心工具&#xff1a; Selenium&#xff1a;一个用于自动化测试的工具&#xff0c;能够模拟用户在浏览器上的操作。Edge WebDriver&#xff1a;用于控制 Edge 浏览器的驱动程序。 功能设计&#xff1a; 检测网络状…...

啥!GitHub Copilot也免费使用了

文章目录 前言免费版直接修复代码多文件上下文Agent模式总结 前言 最近&#xff0c;GitHub 给开发者们带来了一个好消息&#xff1a;他们的 AI 编程助手 GitHub Copilot 现在可以免费使用了&#xff01;以前&#xff0c;每个月要花 10 美元才能享受的服务&#xff0c;现在对所…...

Spring配置文件中:密码明文改为密文处理方式(通用方法)

目录 一、背景 二、思路 A) 普通方式 B) 适合bootstrap.properties方式 三、示例 A) 普通方式&#xff08;连接Redis集群&#xff09; A) 普通方式&#xff08;连接RocketMQ&#xff09; B) 适合bootstrap.properties方式 四、总结 一、背景 SpringBoot和Sprin…...

Linux下ext2文件系统

文章目录 一 :penguin:基本概述二 :star: ext2文件系统:star:​ 1. :star:​Boot Block&#xff08;引导块&#xff09;位置与作用 三 Block Group(块组):star:​1.:star:​ Super Block(超级块):star:​2.:star:​ Group Descriptor&#xff08;块组描述符&#xff09;:star:​…...

BUUCTF:web刷题记录(1)

目录 [极客大挑战 2019]EasySQL1 [极客大挑战 2019]Havefun1 [极客大挑战 2019]EasySQL1 根据题目以及页面内容&#xff0c;这是一个sql注入的题目。 直接就套用万能密码试试。 admin or 1 # 轻松拿到flag 换种方式也可以轻松拿到flag 我们再看一下网页源码 这段 HTML 代码…...

【微服务】面试题 6、分布式事务

分布式事务面试题讲解 一、问题背景与解决方案概述 因微服务项目涉及远程调用可能引发分布式事务问题&#xff0c;需解决。主流解决方案有阿里 Seata 框架&#xff08;含 XA、AT、TCC 模式&#xff09;和 MQ。 二、Seata 框架关键角色 事务协调者&#xff08;TC&#xff09;&…...

【2024年华为OD机试】(C卷,100分)- 分割均衡字符串 (Java JS PythonC/C++)

一、问题描述 题目描述 均衡串定义&#xff1a;字符串中只包含两种字符&#xff0c;且这两种字符的个数相同。 给定一个均衡字符串&#xff0c;请给出可分割成新的均衡子串的最大个数。 约定&#xff1a;字符串中只包含大写的 X 和 Y 两种字符。 输入描述 输入一个均衡串…...

Spring Data Elasticsearch简介

一、Spring Data Elasticsearch简介 1 SpringData ElasticSearch简介 Elasticsearch是一个实时的分布式搜索和分析引擎。它底层封装了Lucene框架,可以提供分布式多用户的全文搜索服务。 Spring Data ElasticSearch是SpringData技术对ElasticSearch原生API封装之后的产物,它通…...

GESP202312 四级【小杨的字典】题解(AC)

》》》点我查看「视频」详解》》》 [GESP202312 四级] 小杨的字典 题目描述 在遥远的星球&#xff0c;有两个国家 A 国和 B 国&#xff0c;他们使用着不同的语言&#xff1a;A 语言和 B 语言。小杨是 B 国的翻译官&#xff0c;他的工作是将 A 语言的文章翻译成 B 语言的文章…...

键盘过滤驱动

文章目录 概述注意源码参考资料 概述 irp请求会从io管理器中传递到设备栈中依次向下发送&#xff0c;当到达底层真实设备处理完成后&#xff0c;会依次返回&#xff0c;这时如果在设备栈中有我们自己注册的设备&#xff0c;就可以起到一个过滤的功能。键盘过滤驱动就是如此&am…...

dolphinscheduler2.0.9升级3.1.9版本问题记录

相关版本说明 JDK&#xff1a;JDK (1.8&#xff09; DolphinScheduler &#xff1a;3.1.9 数据库&#xff1a;MySQL (8)&#xff0c;驱动&#xff1a;MySQL JDBC Driver 8.0.16 注册中心&#xff1a;ZooKeeper (3.8.4) 问题一&#xff1a;dolphinscheduler2.0.9对应zk版本使用…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...