如果您是移动应用程序开发人员,则在某个时间点您需要与后端进行互动。您可能需要做的其中一项任务是从服务器检索并显示图像,或将图像提交给该服务器。提交图像时应该使用什么格式?如何将从服务调用接收的字节转换为图像?

让我们将整个堆栈从服务器构建到iOS应用程序,以了解如何实现。


设置后端

我们将首先构建一个提供RESTful API Kitura服务器来完成两件事:

从客户端接收图像

向客户提供最新的图像


创建服务器项目

创建一个目录,并初始化一个新的可执行Swift包。

mkdir mkdir SwiftImageServer && cd SwiftImageServerswift package init --type executable

编辑您的Package.swift文件以指定您需要Kitura软件包。

import PackageDescription

 

let package = Package( name: "SwiftImageServer", dependencies: [

  .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1)

  ])


你可以运行一个swift package fetch,你应该看到SwiftPM克隆Kitura和它需要的一切。

旋转xcodeproj swift package generate-xcodeproj并让我们编码!


创建一个Kitura服务器

后端将会非常简单,所以我们只是在努力main.swift

首先添加我们需要的所有样板:

import Kitura

import Foundation

 

// Create a Router that we can use to create REST endpoints

let router = Router()

 

// Specify that we want an HTTP server that we can reach with http://localhost:8090

Kitura.addHTTPServer(onPort: 8090, with: router)

 

// Start the server

Kitura.run()


三行代码,你有一个服务器运行。这是一个耻辱,它不能做太多。我们来解决这个问题。


GET端点返回图像

var latestImage: Data? = nil

 

// http://localhost:8090/latestImage

router.get("/latestImage") {

    request, response, next in

defer { next() }

 

guard let image = latestImage else {

        response.status(.preconditionFailed).send("No image is available")

        return

    }

response.send(data: image)

}

你以为我们正在发送图像?这看起来像一个数据对象,而不是 UIImage?这就是有趣的地方。你永远不会将图像作为图像发送。所有图像都以简单易用的格式进行数据打包。当我们向服务器发送图像和从服务器发送图像时,我们需要将其打包为数据对象,然后发送。我们将在iOS应用程序中将其表示为UIImage

注意guard let image = latestImage。在我们设置latestImage变量之前,这会失败。让我们构建接收图像的端点,以便设置latestImage变量。


将图像提交给POST端点

接下来,我们将构建将用于提交图像的端点。

// Create a POST endpoint: http://localhost:8090/image

router.post("/image") {

    request, response, next in

   

defer { next() }

var data = Data()

 

do {

        // Read the body of the request into the data object

        try _ = request.read(into: &data)

        latestImage = data

        response.status(.OK).send("Image received")

    } catch(let error) {

        response.status(.internalServerError)

                .send("Something went wrong when reading the image data")

    }

}

我们已经创建了一个端点,该端点需要包含图像数据的原始主体的POST请求。请记住,服务器只知道数据,而不是UIImage,因此iOS应用程序将不得不将图像转换为数据对象。

这是我们的整个服务器完成!


正在运行

需要已完成的服务器,可以关注并私信我

运行可执行目标。这是矩阵式电脑屏幕,而不是×××的饭盒。

2.png

继续运行,我们将构建iOS应用程序。


客户端应用程序

关注我并且私信我,提供iOS应用程序的完整示例代码。


创建项目

创建一个新的Single View iOS应用程序。我们将需要修改Info.plist。我们需要获得访问照片库的权限才能选择图片。我们还需要修改App Transport安全设置以发出HTTP网络请求,而不是HTTPS(我们的本地Kitura服务器为HTTP)。

将以下内容添加到项目中Info.plist

3.png

我们将添加一个按钮,允许我们从照片库中选择一幅图像,并将其提交给我们之前构建的服务器。

我们将通过在视图控制器中添加一个按钮来开始Main.storyboard

4.png

现在,将其挂接ViewController.swift并添加代码以将其发布到我们的后端。

让我们将IBAction连接到“Pick Image”按钮,我们使用UIImagePickerController来选择图像。

@IBAction func pickImage(_ sender: Any) {

 

  guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else { return }

  let imagePickerController = UIImagePickerController()

  imagePickerController.sourceType = .photoLibrary

  imagePickerController.delegate = self

  present(imagePickerController, animated: true, completion: nil)

}

我们将ViewController设置为UIImagePickerController的委托。添加一个符合委托协议的扩展,该协议也处理任何拾取的图像。设置委托也需要符合UINavigationControllerDelegate,所以也要添加它。

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

   

    public func imagePickerController(

                _ picker: UIImagePickerController,

                didFinishPickingMediaWithInfo info: [String: Any]) {

       

        if let image = info[UIImagePickerControllerOriginalImage]

            as? UIImage {

            submit(image: image)

        } else if let image = info[UIImagePickerControllerEditedImage as? UIImage {

            submit(image: image)

        }

       

        picker.dismiss(animated: true)

    }

}

我们可以选择一张图片,我们随时可以处理它。注意submit(image:)上面例子中的函数。我们现在将创建该功能,并将图像提交给我们的服务器。

func submit(image: UIImage) {

 

        let session = URLSession(

                     configuration: URLSessionConfiguration.default)

       

        guard let url = URL(string: "http://localhost:8090/image") else { return }

        var request = URLRequest(url: url)

       

        request.httpMethod = "POST"

        request.httpBody = UIImagePNGRepresentation(image)

       

        let dataTask = session.dataTask(with: request) {

            (data, response, error) in

           

            if let error = error {

                print("Something went wrong: \(error)")

            }

           

            if let response = response {

                print("Response: \n \(response)")

            }

        }

       

        dataTask.resume()

    }


从服务器接收图像

现在我们可以将图像作为数据对象提交,我们需要构建用于接收图像作为数据并将其转换为普通旧UIImage的功能。

首先在Main.storyboard中添加一个图像视图和另一个按钮到视图控制器:

5.png

当用户点击新按钮时,我们将调用latestImage端点来检索作为数据对象发送到服务器的最后一个图像。然后我们将它转换为UIImage并将其显示在图像视图中。

@IBOutlet weak var imageView: UIImageView!

   

    @IBAction func showLatestImage(_ sender: Any) {

       

        let session = URLSession(

                    configuration: URLSessionConfiguration.default)

       

        guard let url = URL(

                string: "http://localhost:8090/latestImage") else {

            return

        }

       

        var request = URLRequest(url: url)

        request.httpMethod = "GET"

       

        session.dataTask(with: request) { (data, response, error) in

           

            if let error = error {

                print("Something went wrong: \(error)")

            }

           

            if let imageData = data {

                DispatchQueue.main.async {

                    self.imageView.image = UIImage(data: imageData)

                }

            }

        }.resume()

    }


成品

这是我们的应用程序已经提交了一个图像到服务器,并从服务器拉出一个图像来显示。

1.png

当你点击Pick Image时,你会看到一个UIIImagePickerController,它允许你从照片库中选择一个图像,并在将其转换为数据对象后将其提交给我们的后端。显示最新图像按钮向我们的服务器发出GET请求,以检索最后发送的图像,将其转换为UIImage,然后将其显示在我们的UIImageView中。

您现在应该对如何将文本以外的对象发送到服务器有一个基本的了解 - 以及如何检索它们。恭喜!