Alamofire 上传图片入门教程(上)

提示:

本教程基于 Xcode 7.3, iOS 9.3, 和 Swift 2.2

Alamofire 是一个为 iOS 和 Mac OS,基于 Swift 的 HTTP 网络库。它在 Apple 的基础网络库上提供了一个优雅的接口,简化了许多常见的网络任务。

Alamofire 提供链式地请求与响应方法,JSON 参数以及响应的序列化和授权,等等。在本教程中,你将使用 Alamofire 来完成基本的网络任务,包括上传文件以及使用第三方库 RESTful API 来请求数据。

注:关于 RESTful API 可以参考这篇博客 RESTful API

Alamofire 的优雅是一个事实,它完全由 Swift 编写,没有任何 Objective-C 的代码,也不是继承自 AFNetworking。

你需要理解一些 HTTP 的基本概念,以及了解苹果的网络类,比如 NSURLSessionNSURLConnection

虽然 Alamofire 掩盖了一些实现细节,但是如果你需要解决你的网络请求,有一些背景知识也是不错的。你也需要使用 CocoaPods 把 Alamofire 安装到你的教程工程中。

入门

下载 初始工程。下载完成后,打开工程运行,会发现中间有一个按钮可以选择图片,点击按钮会访问系统的相册,随便选择一张图片,背景就会被这张图片代替。但是现在的工程是一个初始工程,好多功能没有完成,如下图:

最终完成的效果图会是下面的样子:

Imagga API

Imagga 是一个把图像识别作为服务的平台,为开发者和企业提供了可扩展的图像标注 API,以及图像密集的云应用。你可以从 他们的自动标注服务 下载一个示例程序。

你需要在 Imagga 网站创建一个免费的开发者账户,才能完成这次教程。Imagga 在每次进行 HTTP 请求的时候都需要一个授权头,所以必须注册才能使用他们的服务。去 https://imagga.com/auth/signup/hacker 完成注册吧。注册成功后你会看到类似下面的界面:

Authorization 选项是一个 token,后面你会用到。

注意:

确保你拷贝了整个 token,因为这个 token 很长。

你将会使用 Imagga 作为上传图片的服务器,给图片标注和设置颜色。你可以在 http://docs.imagga.com 了解到所有的 API。

安装依赖包

在工程主目录创建一个名为 Podfile 的文件,并打开这个文件,可以使用下面的方式:

touch Podfile
open Podfile

然后输入下面的内容:

platform :ios, ‘9.0‘

inhibit_all_warnings!
use_frameworks!

target ‘PhotoTagger‘ do
  pod ‘Alamofire‘, ‘~> 3.1.2‘
end

接下来,如果你的电脑上没有安装 CocoaPods,点击下面链接进行安装 最新版 CocoaPods 的安装流程

关闭刚才打开的工程,打开终端进入工程目录。输入 pod install,稍等片刻,完成后会生成一个名为 PhotoTagger.xcworkspace 的文件。然后编译运行你的工程,你会发现没有任何改变,这就对了,下一步从 RESTful 服务器添加一些 HTTP 请求,会返回 JSON 数据。

Alamofire 的好处是什么?

苹果已经提供了 NSURLSession 和其他的类来下载内容,那为什么还要用 Alamofire 呢?因为 Alamofire 基于 NSURLSession,并且它可以让你编写的代码的时候更容易。你不用话费太大的精力就可以获取互联网上的数据,而且你的代码还会很简洁易读。

下面是 Alamofire 中几个主要的函数:

.upload:以分解成部分的形式,数据流的形式,整个文件的形式或者数据的形式上传文件。

.download:下载文件或者暂停后继续下载。

.request:每一个 HTTP 请求和文件传输都不相关。

这些 Alamofire 函数的范围是一个 module,而不是类或结构体。Alamofire 的底层都是类和结构体,比如有 ManagerRequest, 和Response,但是你一开始用的时候不必全都理解 Alamofire 其他的结构体。下面是一个例子:

下面直接使用的 NSURLSession

// With NSURLSession
public func fetchAllRooms(completion: ([RemoteRoom]?) -> Void) {
  let url = NSURL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true")!

  let urlRequest = NSMutableURLRequest(
    URL: url,
    cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData,
    timeoutInterval: 10.0 * 1000)
  urlRequest.HTTPMethod = "GET"
  urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")

  let task = urlSession.dataTaskWithRequest(urlRequest)
    { (data, response, error) -> Void in
    guard error == nil else {
      print("Error while fetching remote rooms: \(error)")
      completion(nil)
      return
    }

    guard let json = try? NSJSONSerialization.JSONObjectWithData(data!,
      options: []) as? [String: AnyObject] else {
        print("Nil data received from fetchAllRooms service")
        completion(nil)
        return
    }

    guard let rows = json["rows"] as? [[String: AnyObject]] {
      print("Malformed data received from fetchAllRooms service")
      completion(nil)
      return
    }

    var rooms = [RemoteRoom]()
    for roomDict in rows {
      rooms.append(RemoteRoom(jsonData: roomDict))
    }

    completion(rooms)
  }

  task.resume()
}

下面是使用 Alamofire 的方式:

// With Alamofire
func fetchAllRooms(completion: ([RemoteRoom]?) -> Void) {
  Alamofire.request(
    .GET,
    "http://localhost:5984/rooms/_all_docs",
    parameters: ["include_docs": "true"],
    encoding: .URL)
    .validate()
    .responseJSON { (response) -> Void in
      guard response.result.isSuccess else {
        print("Error while fetching remote rooms: \(response.result.error)")
        completion(nil)
        return
      }

      guard let value = response.result.value as? [String: AnyObject],
        rows = value["rows"] as? [[String: AnyObject]] else {
          print("Malformed data received from fetchAllRooms service")
           completion(nil)
           return
      }

      var rooms = [RemoteRoom]()
      for roomDict in rows {
        rooms.append(RemoteRoom(jsonData: roomDict))
      }

      completion(rooms)
  }
}

上传文件

打开 ViewController.swift,把下面的代码加到类的底部:

// Networking calls
extension ViewController {
  func uploadImage(image: UIImage, progress: (percent: Float) -> Void,
    completion: (tags: [String], colors: [PhotoColor]) -> Void) {
    guard let imageData = UIImageJPEGRepresentation(image, 0.5) else {
      print("Could not get JPEG representation of UIImage")
      return
    }
  }
}

上面的代码的作用是上传图片到 Imagga。

接下来,在 imagePickerController(_:didFinishPickingMediaWithInfo:) 方法里,在 imageView.image = image 后面加入下面的代码:

// 1
takePictureButton.hidden = true
progressView.progress = 0.0
progressView.hidden = false
activityIndicatorView.startAnimating()

uploadImage(
  image,
  progress: { [unowned self] percent in
    // 2
    self.progressView.setProgress(percent, animated: true)
  },
  completion: { [unowned self] tags, colors in
    // 3
    self.takePictureButton.hidden = false
    self.progressView.hidden = true
    self.activityIndicatorView.stopAnimating()

    self.tags = tags
    self.colors = colors

    // 4
    self.performSegueWithIdentifier("ShowResults", sender: self)
})

在 Alamofire 中所有对象都是异步的,也就意味着你会以异步的方式更新 UI:

  1. 隐藏上传按钮,同时显示显示进度条。
  2. 当文件上传的时候,progress 闭包会以百分比的形式进行更新,改变的多少同步显示在进度条上。
  3. 当上传文件完成时,completion 闭包才会执行,设置控件的状态变为原始状态。
  4. 最后,Storyboard 会将成功的(或者不成功)上茶传结果显示到屏幕上,接口并不会基于错误发生改变。

接下来,在 ViewController.swift 中添加下面的代码:

import Alamofire

上面的代可以会让你使用 Alamofire 模块提供的代码。

下面,返回到 uploadImage(_:progress:completion:) 并且在guard 判断后面添加下面的代码:

Alamofire.upload(
  .POST,
  "http://api.imagga.com/v1/content",
  headers: ["Authorization" : "Basic xxx"],
  multipartFormData: { multipartFormData in
    multipartFormData.appendBodyPart(data: imageData, name: "imagefile",
      fileName: "image.jpg", mimeType: "image/jpeg")
  },
  encodingCompletion: { encodingResult in
  }
)

确保替换『Basic xxx』用你自己在 Imagga 网站生成的 token。

接下来,在 encodingCompletion闭包里添加下面的代码:

switch encodingResult {
case .Success(let upload, _, _):
  upload.progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in
    dispatch_async(dispatch_get_main_queue()) {
      let percent = (Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
      progress(percent: percent)
    }
  }
  upload.validate()
  upload.responseJSON { response in
  }
case .Failure(let encodingError):
  print(encodingError)
}

这段代码调用 Alamofire 上传功能,通过在计算来更新进度条。

注意:

Alamofire 并不会保证调用过程会在主队列上回调;因此你必须通过向主队列调度来更新用户界面。有一些 Alamofire 的回调,比如 responseJSON,都会默认在主队列调用。例如:

dispatch_async(queue ?? dispatch_get_main_queue()) {
  let response = ...
  completionHandler(response)
}

为了改变默认的做法,你需要给 Alamofire 提供一个 dispatch_queue_t

添加下面的代码到 upload.responseJSON

// 1.
guard response.result.isSuccess else {
  print("Error while uploading file: \(response.result.error)")
  completion(tags: [String](), colors: [PhotoColor]())
  return
}
// 2.
guard let responseJSON = response.result.value as? [String: AnyObject],
  uploadedFiles = responseJSON["uploaded"] as? [AnyObject],
  firstFile = uploadedFiles.first as? [String: AnyObject],
  firstFileID = firstFile["id"] as? String else {
    print("Invalid information received from service")
    completion(tags: [String](), colors: [PhotoColor]())
    return
}
print("Content uploaded with ID: \(firstFileID)")
// 3.
completion(tags: [String](), colors: [PhotoColor]())

下面是每步的解释:

1. 检查响应是否成功;如果不成功,输出错误信息并调用 completion

2. 检查响应的每个部分,验证所期望的类型是收到的实际类型,如果 firstFileID 没有被解析,那么输出错误信息,并调用completion

3. 调用 completion 来更新 UI。你没有任何下载的标志或颜色,所以简化调用数据为空的情况。

注意:

每一个响应都有一个结果,包括枚举值和类型。使用自动验证,当它返回一个合法的 HTTP 码(在200-299之间的内容类型都被认为是可以接受的)。

你可以通过添加 .validate 手动执行验证,代码如下:

Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
  .validate(statusCode: 200..<300)
  .validate(contentType: ["application/json"])
  .response { response in
    // response handling code
}

如果你在上传期间发生错误,UI 并不会显示错误;它仅仅会返回没有标志或颜色给用户。这不是良好的用户体验,但是在本次教程中确实有上面所说的。

编译运行工程代码,选择一个 image 然后会看到进度条发生了变化,上传完成后,你会看到下面的信息:

你已经成功的上传图片!

原帖地址

时间: 2024-11-05 20:34:37

Alamofire 上传图片入门教程(上)的相关文章

django入门教程(上)

相信用过python的人都听过Django的大名,知道它是一个web框架,用来支持动态网站.网络应用程序以及网络服务的开发.那么为什么我们需要一个web框架,而不是直接用python来写web应用呢?其实简单来说,就是为了偷懒. 如果不用框架的话,你可能需要连接数据库.查询数据库.关闭数据库,在python代码文件里掺杂html标签.css样式等.并且每次开始一个web应用,你都要从头开始写起,重复许多枯燥无味的代码. 而web框架提供了通用web开发模式的高度抽象,使我们可以专注于编写清晰.易

Scrapy安装、爬虫入门教程、爬虫实例(豆瓣电影爬虫)

Scrapy在window上的安装教程见下面的链接:Scrapy安装教程 上述安装教程已实践,可行.本来打算在ubuntu上安装Scrapy的,但是Ubuntu 磁盘空间太少了,还没扩展磁盘空间,暂时不想再上面装太多软件. Scrapy的入门教程见下面链接:Scrapy入门教程 上面的入门教程是很基础的,先跟着作者走一遍,要动起来哟,不要只是阅读上面的那篇入门教程,下面我简单总结一下Scrapy爬虫过程: 1.在Item中定义自己要抓取的数据: movie_name就像是字典中的“键”,爬到的数

无废话ExtJs 入门教程十三[上传图片:File]

无废话ExtJs 入门教程十三[上传图片:File] extjs技术交流,欢迎加群(201926085) 1.代码如下: 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/199

【教程】原创:历上最简单的游戏编程入门教程(基于cocos2d-js)

前言: 大家好.我是一个游戏开发者.曾就职于cocos2d-x这个手机游戏引擎的开发的公司. 在这边我准备了一个最简单的教程,想告诉大家制作一个游戏有多简单. 回忆起当年刚刚步入游戏这个行业,我也抱着非常多的疑问. 所以如果大家对游戏有兴趣的朋友,可以在下面留言. 这个教程我会讲的非常通俗易懂.争取几句话之内就让你看到一个效果. 另外教程里面有丰富的图文讲解.我保证你学完之后掌握了做游戏的真髓. 你完全可以马上开始做自己的游戏.并且能够让你的游戏在网页上,ios,android 还有pc平台上跑

深入学习:Windows下Git入门教程(上)

一,安装Git: 1.1Linux上安装命令: sudo apt-get install git 1.2在Windows上安装Git: 使用Windows版的msysgit,官方下载地址:http://msysgit.github.io/,点击进入官网,如果官网无法正常下载我这里有当前的最新版,已经上传到CSDN上,下载地址为:http://download.csdn.net/detail/huangyabin001/7564005,点击进入下载 1.3安装完成进行配置: $ git confi

史上最浅显易懂的RxJava入门教程

RxJava是一个神奇的框架,用法很简单,但内部实现有点复杂,代码逻辑有点绕.我读源码时,确实有点似懂非懂的感觉.网上关于RxJava源码分析的文章,源码贴了一大堆,代码逻辑绕来绕去的,让人看得云里雾里的.既然用拆轮子的方式来分析源码比较难啃,不如换种方式,以造轮子的方式,将源码中与性能.兼容性.扩展性有关的代码剔除,留下核心代码带大家揭秘 RxJava 的实现原理. 什么是RxJava ? Rx是Reactive Extensions的简写,翻译为响应的扩展.也就是通过由一方发出信息,另一方响

无废话ExtJs 入门教程十四[文本编辑器:Editor]

无废话ExtJs 入门教程十四[文本编辑器:Editor] extjs技术交流,欢迎加群(201926085) ExtJs自带的编辑器没有图片上传的功能,大部分时候能够满足我们的需要. 但有时候这个功能还是需要的.我在这里对keeditor进行了整合. 首先要下载keeditor和上传时需要引用的LitJson.dll.由于ke的版本不同,我这里提供的下载文件只适用于当前整合代码,供参考. 1.代码如下: 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML

【转载】GBDT(MART) 迭代决策树入门教程 | 简介

      转载地址:http://blog.csdn.net/w28971023/article/details/8240756        GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,所有树的结论累加起来做最终答案.它在被提出之初就和SVM一起被认为是泛化能力(generalization)较强的算法.近些年更因为被用于搜索排

Android基础入门教程——10.12 传感器专题(3)——加速度-陀螺仪传感器

Android基础入门教程--10.12 传感器专题(3)--加速度/陀螺仪传感器 标签(空格分隔): Android基础入门教程 本节引言: 本节继续来扣Android中的传感器,本节带来的是加速度传感器(Accelerometer sensor)以及 陀螺仪传感器(Gyroscope sensor),和上一节的方向传感器一样有着x,y,z 三个轴, 还是要说一点:x,y轴的坐标要和绘图那里的x,y轴区分开来!传感器的是以左下角 为原点的!x向右,y向上!好的,带着我们的套路来学本节的传感器吧