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

Posted

技术标签:

【中文标题】图像未通过 POST 完整传输到服务器【英文标题】:The image is not transferred in full via POST to the server 【发布时间】:2021-04-09 09:18:10 【问题描述】:

我通过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()
    


【问题讨论】:

imageStr 的大小是多少?您的请求中是否包含 Content-Length?您在服务器上获得正确的大小吗?您是使用“基本帖子”,还是使用 multipart/url 表单数据? 我在我的问题中添加了 POST 请求代码。 如何初始化类? newsMessage 是否包含 UIImage 的字符串?! 我已经更新了我的问题并添加了初始化类的代码。 newsMessage - 是字符串。 ShareHelper(message: validateForm.content, user: validateForm.user, email: validateForm.email, media: imageStr) 【参考方案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 时需要一些帮助,只需在 *** 上提出一个新问题即可。

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 中相关的键。如果您需要使用组合和缓存系统获取图像的解决方案,只需在 *** 上提出一个新问题。

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
  

【讨论】:

感谢您的代码!我明天测试一下!【参考方案2】:

我在这个帖子How to upload images to a server in ios with Swift?看到了一些解决你问题的方法

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

【讨论】:

当然可以。我已经搜索了许多具有相同问题的帖子。但他们有Storyboard 的解决方案,我需要SwiftUI。而且我不仅发送图像。除了图片,我还有很多文本数据要发送。所有这些都需要一键发送。 您答案中的链接非常旧。这个问题是 6 年前提出的。 LeXxy,你说得对,帖子很旧,但答案仍然相关。我也不明白 Storyboard 或 SwiftUI 与这些解决方案有什么关系,您正在谈论将图像表示数据发布到服务器【参考方案3】:

最佳做法是提供仅用于上传图片的上传 api。您可以使用多部分 POST 来使其做得很好。然后获取上传图片 ID 的响应,并将其添加到您的 shareNews api 请求中。

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

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

【讨论】:

以上是关于图像未通过 POST 完整传输到服务器的主要内容,如果未能解决你的问题,请参考以下文章

图像未通过目标 C 中的 POST 上传到服务器

Android 相机图像未上传到服务器。使用多部分数据 Http post

通过 POST 命令将音频从 ios 流式传输到 REST 服务器

在 Windows 7 上通过 QTcpSocket 流式传输图像

完整图像资产未转换为 base64 字符串

通过 POST 发送图像未正确发送