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

作者: hrscy | 来源:发表于2016-07-18 22:45 被阅读1050次
    PhotoTaggerDemo.gif

    Imagga API

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

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

    Imagga-Dashboard.pngImagga-Dashboard.png

    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 然后会看到进度条发生了变化,上传完成后,你会看到下面的信息:

    ImaggaUploadConsole.pngImaggaUploadConsole.png

    你已经成功的上传图片!

    sad-forever-alone-happy.jpgsad-forever-alone-happy.jpg

    原帖地址

    相关文章

      网友评论

        本文标题:Alamofire 上传图片入门教程(上)

        本文链接:https://www.haomeiwen.com/subject/jqkmjttx.html