在 Swift 中使用 URLRequest 或 Alamofire 将图像发送到 api

问题描述 投票:0回答:1

(斯威夫特 5 / Alamofire 5 / Xcode / IOS)

我正在尝试将我拍摄的照片发送到 mathpix api。 mathpix 的 api 文档中是这样显示的:

curl -X POST https://api.mathpix.com/v3/text \
-H 'app_id: APP_ID' \
-H 'app_key: APP_KEY' \
--form 'file=@"cases_hw.jpg"' \
--form 'options_json="{\"math_inline_delimiters\": [\"$\", \"$\"], \"rm_spaces\": true}"'

或者使用python

#!/usr/bin/env python
import requests
import json

r = requests.post("https://api.mathpix.com/v3/text",
    files={"file": open("cases_hw.jpg","rb")},
    data={
      "options_json": json.dumps({
        "math_inline_delimiters": ["$", "$"],
        "rm_spaces": True
      })
    },
    headers={
        "app_id": "APP_ID",
        "app_key": "APP_KEY"
    }
)
print(json.dumps(r.json(), indent=4, sort_keys=True))

这是我的代码:

@State private var selectedImage: UIImage? // The taken photo is stored here

@State private var imagePath = "" //  Full path of image

请求函数代码(获取selectImage/imagePath后调用的函数):

                let image = selectedImage
                let imageData = image!.jpegData(compressionQuality: 1.0)!
                let imageKey = "takenPhoto"
                let urlString = "https://api.mathpix.com/v3/text"
                let headers: HTTPHeaders = [
                        "app_id": "my_working_app_id_here",
                        "app_key": "my_working_app_key_here"
                    ]
                
                AF.upload(multipartFormData: { multiPart in
                    multiPart.append(imageData, withName: imageKey, fileName: "takenPhoto.jpg", mimeType: "image/jpg")
                }, to: urlString, headers: headers).responseJSON { response in
                    switch response.result {
                    case .success(_):
                        if let dictionary = response.value as? [String:Any] {
                            print("success", dictionary)
                        } else {
                            print("error")
                        }
                    case .failure(let error):
                        print("error", error.localizedDescription)
                    }
                }

此代码给出

["error": Internal error, "error_info": {
    id = "sys_exception";
    message = "Internal error";
}]

我还尝试使用 https://github.com/zonble/CurlDSL 库来创建 cURL 请求:

try CURL("curl -X POST https://api.mathpix.com/v3/text -H \'app_id: my_app_id_here\' -H \'app_key: my_app_key_here\' -F image@=\(homeDirectoryString)"

但是,它会抛出与上面相同的错误。

我上面的代码只有在我用来自互联网的全局链接替换图像时才有效(比如 https://mathpix-ocr-examples.s3.amazonaws.com/cases_hw.jpg

如果我在 mac 的终端中使用 curl 命令,它也可以很好地处理本地图像(我需要的)。

我在这个问题上浪费了大约一个星期,但没有找到任何有效的解决方案。我确定错误一定是在查询语法(swift/curl)中,但我不知道如何找到它。

swift curl alamofire
1个回答
0
投票

Swift 代码中的问题是:

  1. curl
    命令中用于图像的键名是
    file
    ,而不是
    takenPhoto
  2. options_json
    里面的
    curl
    根本就没有供应

你可能会做类似的事情

let imageData = …
let imageKey = "file"
let filename = "takenPhoto.jpg"

let optionsKey = "options_json"
let options: [String: Any] = ["math_inline_delimiters": ["$", "$"], "rm_spaces": true]
let optionsData = try JSONSerialization.data(withJSONObject: options)

AF.upload(multipartFormData: { multiPart in
    multiPart.append(imageData, withName: imageKey, fileName: filename, mimeType: "image/jpg")
    multiPart.append(optionsData, withName: optionsKey)
}, to: urlString, headers: headers).responseJSON { response in
    …
}

一些小观察:

  • 我会反对

    try?
    ,因为它会丢弃任何已抛出的有意义的诊断信息;

  • 我通常会建议不要强制解包运算符 (

    !
    );

  • 当出于诊断目的打印错误时,我建议只打印

    error.localizedDescription
    而不是
    error
    localizedDescription
    用于 UI 的友好字符串,但出于诊断目的,
    error
    对开发人员更具启发性;和

  • responseJSON
    已弃用,您可能希望在不久的将来切换到
    responseDecodable
    ;和

  • 我会警惕对 mime 类型和文件扩展名进行硬编码。我通常会根据原始资产的 URL 确定 mimetype:

    extension URL {
        /// Mime type for the URL
        ///
        /// Requires `import UniformTypeIdentifiers` for iOS 14 solution.
        /// Requires `import MobileCoreServices` for pre-iOS 14 solution
    
        var mimeType: String {
            if #available(iOS 14.0, *) {
                return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream"
            } else {
                guard
                    let identifier = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
                    let mimeType = UTTypeCopyPreferredTagWithClass(identifier, kUTTagClassMIMEType)?.takeRetainedValue() as String?
                else {
                    return "application/octet-stream"
                }
    
                return mimeType
            }
        }
    }
    

    不用说,如果你不需要对早期 iOS 版本的支持,你可以简化它。但它说明了用于确定 mime 类型的 API。选择图像时,可能会有各种不同的 mimetypes。

© www.soinside.com 2019 - 2024. All rights reserved.