图像未通过 POST 完整传输到服务器

时间:2021-04-09 09:18:10

标签: swift post swiftui

我通过 POST 将带有图片的文本发送到服务器。文字正确,但图片不完整。 10%的图片显示正确,其他的只是灰色背景。 Swift 使用 base64EncodedString() 将图像文件转换为文本。

似乎Swift执行转换有错误,或者服务器没有完全接收到数据。但是我增加了 POST 的限制,但没有帮助。我还使用 compressionQuality 更改了图像压缩值,但没有帮助。

来自视图文件的代码:

Button(action: {
    self.checkBoxStatus = false

    let uiImage: UIImage = self.selectedImage.asUIImage()
    let imageData: Data = uiImage.jpegData(compressionQuality: 0.9) ?? Data()
    let imageStr: String = imageData.base64EncodedString()

    let shareHelper = ShareHelper(message: validateForm.content, user: validateForm.user, email: validateForm.email, media: imageStr)
    shareHelper.RequestPost { (dataString) in
        self.checkRequestStatus = true
        validateForm.content = ""
        validateForm.user = ""
        validateForm.email = ""
        validateForm.media = ""
        self.selectedImage = Image("")
    }
}, label: {
    Text("Send")
})

如何解决?

附注:

POST 请求代码:

import Foundation

class ShareHelper {

    var dataString: String = ""
    var newsMessage: String
    var newsUser: String
    var newsEmail: String
    var newsMedia: String
    let newsAPI: String = "https://example.com/api/shareNews"

    init(message: String, user: String, email: String, media: String) {
        self.newsMessage = message
        self.newsUser = user
        self.newsEmail = email
        self.newsMedia = media
    }

    func RequestPost(completion: @escaping((String) -> Void)) {
        let url = URL(string: self.newsAPI)
        guard let requestUrl = url else { fatalError() }
        var request = URLRequest(url: requestUrl)
        request.httpMethod = "POST"
        let postString = "message=\(self.newsMessage)&user=\(self.newsUser)&email=\(self.newsEmail)&media=\(self.newsMedia)"
        request.httpBody = postString.data(using: String.Encoding.utf8)
        let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            if error != nil {
                return
            }
            if let data = data, let dataString = String(data: data, encoding: .utf8) {
                DispatchQueue.main.async {
                    self.dataString = dataString
                    completion(dataString)
                }
            }
        }
        task.resume()
    }

}

3 个答案:

答案 0 :(得分:1)

我在此线程中看到了一些解决您问题的方法 How to upload images to a server in iOS with Swift?

在此线程中还有一些答案演示了如何通过 POST 方法将图像上传到服务器。

答案 1 :(得分:1)

最好的做法是提供一个只上传图片的上传api。您可以使用多部分 POST 来完成它。然后获取带有上传图片 ID 的响应,并将其添加到您的 shareNews api 请求中。

服务端应该通过 id 来管理图片。

对于您当前的代码,我想它运行良好,请尝试询问后端开发人员他们如何解码您的 base64-ed 数据。

答案 2 :(得分:1)

您可以使用使用 SerialQueue 的组合框架,并将您的图像与与您的新闻相关的数据分开发送。

所以这是按住按钮的 SwiftUI 视图。你会注意到我引入了一个视图模型来避免视图内部的任何逻辑。

import SwiftUI

struct ContentView: View {

  /// Used to separate the logic from the view.
  @ObservedObject var viewModel = ContentViewModel()

  var body: some View {
    Button(action: { viewModel.sendNewsData() }) {
      Text("Send")
    }
  }
}

所以这是视图模型本身,发送数据的逻辑发生在这里。您将不得不单独处理发送新闻数据。如果您在使用 Combine 时需要一些帮助,只需在 StackOverflow 上提出一个新问题即可。

import Combine
import SwiftUI

final class ContentViewModel: ObservableObject {

  let networkRequestManager = NetworkRequestManager()

  let image = UIImage(named: "MySpecialImage")!  // The image you want to send

  var cancellables = Set<AnyCancellable>()

  /// Send the news data and the image in one function
  /// to be used in your SwiftUI view.
  func sendNewsData() {
    postImageData(of: image)
    postNewsData()
  }

  /// Send the image on its own method.
  ///
  /// The data encoded string printed is related
  /// to your image that comes back from the api.
  func postImageData(of image: UIImage) {
    networkRequestManager
      .sendImage(image)
      .sink(
        receiveCompletion: { completion in
          print(completion) },
        receiveValue: { data in
          print(data) }) // your image
      .store(in: &cancellables)
  }

  func postNewsData() {
    // Just post your news data without the image
    // for it to be sent separately.
  }
}

所以这里是 NetworkRequestManager 类,它处理将编码为字符串的图像发送到您的 api 端点。只需根据需要更改 url。 不要忘记将与图像关联的密钥更改为 api 中的相关密钥。如果您需要使用组合和缓存系统取回图像的解决方案,只需在 StackOverflow 上提出一个新问题即可。

import Combine
import SwiftUI

final class NetworkRequestManager {

  /// Send the image in a serial queue to not obstruct the main queue.
  let imageSerialQueue = DispatchQueue(label: "imageSerialQueue")

  /// This is where you will encode your image data to send it to your api.
  func sendImage(_ image: UIImage) -> AnyPublisher<String, Error> {

    let url = URL(string: "https://example.com/api/shareNews")!
    let body = setupBody(with: image)
    let urlRequest = setupURLRequest(url: url, body: body)

    return URLSession.shared
      .dataTaskPublisher(for: urlRequest)
      .subscribe(on: imageSerialQueue)
      .map { $0.data }
      .encode(encoder: JSONEncoder())
      .decode(type: String.self, decoder: JSONDecoder())
      .eraseToAnyPublisher()
  }

  /// The body related to your endpoint.
  ///
  /// Make sure that the dictionary key matches the one in your api.
  func setupBody(with image: UIImage) -> [String: Any] {
    let jpegData = image.jpegData(compressionQuality: 1)
    return ["newsMedia": jpegData?.base64EncodedString() as Any]
  }

  /// Setup the url request to send a POST method with your image
  /// in a json format.
  func setupURLRequest(url: URL,
                       body: [String: Any]) -> URLRequest {

    var urlRequest = URLRequest(url: url)
    urlRequest.allowsConstrainedNetworkAccess = true
    urlRequest.httpMethod = "POST"
    urlRequest.setValue("application/json",
                        forHTTPHeaderField: "Content-Type")
    urlRequest.httpBody = try? JSONSerialization.data(withJSONObject: body)
    return urlRequest
  }
}
相关问题