返回

Swift图片上传CamFind API 400错误解决

IOS

Swift 图片上传 CamFind API 问题解决

在使用 Swift 进行图片上传,尤其是需要用到像 CamFind 这样的 API 时,经常会遇到 400 错误。 这通常是因为请求的格式不符合 API 的要求。本文分析并解决通过 Swift 发送 multipart/form-data 请求至 CamFind API 时遇到的常见问题。

问题分析:CamFind API 请求格式

CamFind API 需要 multipart/form-data 编码的请求,其包含以下重要组成部分:

  • Headers: 请求头必须包含 X-Mashape-Key 以及 Content-Typemultipart/form-data 的声明。
  • Parameters: 请求参数需要包含 image_request[locale]image_request[image] 。 实际 image 需要是图片二进制数据,而不是本地文件路径。
  • Multipart 结构: 数据的组织结构必须符合 multipart/form-data 的格式,需要有正确的 boundary 分隔符。

原有的代码逻辑错误的地方:

  • 使用文件路径的方式传递图片参数, API 需要的是图片二进制数据。
  • 请求 body 里使用了归档方式 NSKeyedArchiver.archivedDataWithRootObject(parameters) 将参数进行序列化,应该将参数进行 form-data 的方式拼接。

解决方案

以下为逐步解析的解决方案,展示了如何正确构造 multipart/form-data 请求并成功上传图片:

1. 准备请求数据

首先,读取图片数据,将其转换为 Data 对象。 API 所要求的 image 字段的内容为图像的二进制数据,而非本地文件路径。 使用UIImagePNGRepresentationUIImageJPEGRepresentationUIImage 对象转换为 Data 对象。同时,需注意准备 API 要求的其他参数,如 image_request[locale]

func prepareFormData(image: UIImage, locale: String, mashapeKey: String) -> (Data?, String?) {
    guard let imageData = image.pngData() else {
        print("Failed to convert image to data")
        return (nil, nil)
    }

    let boundary = UUID().uuidString  // 动态生成边界符
    let lineBreak = "\r\n"
    
    var body = Data()
   
    // image_request[locale] parameter
    body.append(("--\(boundary)" + lineBreak).data(using: .utf8)!)
    body.append("Content-Disposition: form-data; name=\"image_request[locale]\"".data(using: .utf8)!)
    body.append(lineBreak.data(using: .utf8)!)
    body.append(lineBreak.data(using: .utf8)!)
    body.append(locale.data(using: .utf8)!)
    body.append(lineBreak.data(using: .utf8)!)


    // image_request[image] parameter
    body.append(("--\(boundary)" + lineBreak).data(using: .utf8)!)
    body.append("Content-Disposition: form-data; name=\"image_request[image]\"; filename=\"image.png\"".data(using: .utf8)!)
    body.append(lineBreak.data(using: .utf8)!)
    body.append("Content-Type: image/png".data(using: .utf8)!)
    body.append(lineBreak.data(using: .utf8)!)
    body.append(lineBreak.data(using: .utf8)!)
    body.append(imageData)
    body.append(lineBreak.data(using: .utf8)!)


    // End of multipart/form-data body
    body.append(("--\(boundary)--" + lineBreak).data(using: .utf8)!)


    return (body, boundary)
}

2. 创建 URLRequest 并发送

使用准备好的数据创建 URLRequest,设置请求方法、头部,并使用 URLSession 发起网络请求。 Content-Type 头部必须正确设置为 multipart/form-data; boundary=你的边界符。 同时 X-Mashape-Key 头也必须存在。

func postImage(image: UIImage, locale: String, mashapeKey: String){
     let url = URL(string: "https://camfind.p.mashape.com/image_requests")!

    guard let (bodyData, boundary) = prepareFormData(image: image, locale: locale, mashapeKey: mashapeKey) else {
        return
    }

    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("multipart/form-data; boundary=\(boundary!)", forHTTPHeaderField: "Content-Type")
    request.setValue(mashapeKey, forHTTPHeaderField: "X-Mashape-Key")

    request.httpBody = bodyData
  
    let session = URLSession.shared
    let dataTask = session.dataTask(with: request) { (data, response, error) in
        if let error = error {
            print("Error: \(error)")
        } else if let httpResponse = response as? HTTPURLResponse{
            if let data = data{
                if let jsonResponse = try? JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]{
                      print(jsonResponse)
                  }
                let statusDescription = httpResponse.description
                 print("Status Code:\(httpResponse.statusCode) \n \(statusDescription) \n Response:\(data) \n ")
                
              }
              
           }
     
    }
    dataTask.resume()
}

调用方式:

let image = imageView.image!
let locale = "en_US"
let apiKey = "9hcyYCUJEsmsh4lNTgpgVX1xRq0Ip1uogovjsn5Mte0ONVBtes"
postImage(image: image, locale: locale, mashapeKey: apiKey)

安全建议

  • API key 请勿硬编码,请安全存储。
  • 实际业务中根据需求进行请求头和参数配置。
  • 请及时处理和判断请求失败的 case,打印错误日志或者进行重试操作。
  • multipart/form-data 请求通常涉及大量数据,应当注意网络情况并做错误处理。

总结

要成功地使用 Swift 与 CamFind API 进行图片上传,必须严格按照 API 文档要求构建请求,特别是 multipart/form-data 的格式以及 X-Mashape-Keyimage_request 参数的处理方式。本文的示例代码应能解决你遇到的 400 错误问题,希望帮助你掌握相关技术。