快速添加用于多部分/表单数据POST的附件

时间:2019-06-05 16:46:10

标签: swift post postman multipartform-data

Swift 5,Xcode版本10.2.1(10E1001)

大家好,我对此表示感谢。

我正在创建一个呼叫,以将附件(PNG)发布到我的POST呼叫中。我正在拨打ServiceNow。如果我在正文中使用与PostMan相同的键,则Postman中的调用会正常工作。但是,下面的附件似乎很难。此示例中的图片是PNG资产。

为进行比较,如果我在Postman中省略附件,则会得到完全相同的错误消息。我相信图片格式不正确...

预先感谢...

我从ServiceNow中收到以下错误:

{
    error =     {
        detail = "<null>";
        message = "Failed to create the attachment. File part might be missing in the request.";
    };
    status = failure;
}

这是我的代码:

func createDataBody() -> Data {

    let newLine = "\r\n"
    let twoNewLines = newLine + newLine

    let boundary = "----------------------------\(UUID().uuidString)" + newLine

    var body = Data()

    let stringEncoding = String.Encoding.utf16

    body.append(boundary.data(using: stringEncoding)!)

    let table_name = "Content-Disposition: form-data; name=\"table_name\"" + twoNewLines
    body.append(table_name.data(using: stringEncoding)!)

    //incident
    body.append("incident".data(using: stringEncoding)!)

    //new line
    body.append(newLine.data(using: stringEncoding)!)

    //boundary
    body.append(boundary.data(using: stringEncoding)!)

    let table_sys_id = "Content-Disposition: form-data; name=\"table_sys_id\"" + twoNewLines
    body.append(table_sys_id.data(using: stringEncoding)!)

    //ba931ddadbf93b00f7bbdd0b5e96193c
    body.append("ba931ddadbf93b00f7bbdd0b5e96193c".data(using: stringEncoding)!)

    //new line
    body.append(newLine.data(using: stringEncoding)!)

    //boundary
    body.append(boundary.data(using: stringEncoding)!)

    let file = "Content-Disposition: form-data; name=\"file\"; filename=\"Artboard@1x.png\"" + newLine
    body.append(file.data(using: stringEncoding)!)

    let type = "Content-Type: image/png" + twoNewLines
    body.append(type.data(using: stringEncoding)!)

    //new line
    body.append(newLine.data(using: stringEncoding)!)

    let img = #imageLiteral(resourceName: "Artboard@1x")

    if let fileContent = img.pngData() {

        body.append(fileContent)

    }

    //new line
    body.append(newLine.data(using: stringEncoding)!)
    body.append("--\(UUID().uuidString)--".data(using: stringEncoding)!)

    print(String(data: body, encoding: .utf16)!)

    return body
}

这是身体的样子,省略了图像数据:

----------------------------F2152BF1-CE54-4E86-B8D0-931FA36F7C36
Content-Disposition: form-data; name="table_name"

incident
----------------------------F2152BF1-CE54-4E86-B8D0-931FA36F7C36
Content-Disposition: form-data; name="table_sys_id"

ba931ddadbf93b00f7bbdd0b5e96193c
----------------------------F2152BF1-CE54-4E86-B8D0-931FA36F7C36
Content-Disposition: form-data; name="file"; filename="Artboard@1x.png"
Content-Type: image/png

.....

----------------------------F2152BF1-CE54-4E86-B8D0-931FA36F7C36

这是标题调用

func addAttachmentToIncident(){

let passwordString = "\(userNameTextField.text!):\(passwordTextField.text!)"
let passwordData = passwordString.data(using: String.Encoding.utf8)
let base64EncodedCredential = passwordData?.base64EncodedString(options: Data.Base64EncodingOptions.lineLength76Characters)

let boundary = generateBoundaryString()

let headers = [
    "authorization": "Basic " + base64EncodedCredential!,
    "cache-control": "no-cache",
    "Accept": "application/json",
    "content-type": "multipart/form-data; boundary=--\(boundary)"
]

guard let url = URL(string: "https://xxx.service-now.com/api/now/attachment/upload") else {
    return

}

var request = URLRequest(url: url)

request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let dataBody = createDataBody(boundary: boundary)
request.httpBody = dataBody

let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
    if let response = response {
        print(response)
    }

    if let data = data {
        do {
            let json = try JSONSerialization.jsonObject(with: data, options: [])
            print(json)
        } catch {
            print(error)
        }
    }
    }.resume()

} // addAttachmentToIncident

1 个答案:

答案 0 :(得分:1)

一些观察结果:

  1. 最终边界不正确。假设您已经创建了一个以--开头的边界,则应该附加\(boundary)--作为最终边界。现在,代码正在创建一个新的UUID(并省略了您在原始边界中添加的所有多余破折号),因此它将与其余边界不匹配。您也需要在该最终边界之后加上一个newLine序列。

    缺少最终边界可能会阻止它识别出身体的这一部分,因此“文件部分可能丢失”消息。

  2. boundary不应是局部变量。准备多部分请求时,必须在标头中指定边界(此处必须是同一边界,而不是另一个UUID()实例)。

    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
    

    通常,我会让调用者创建边界,在创建请求标头时使用边界,然后将边界作为参数传递给此方法。参见Upload image with parameters in Swift

    标题和正文中没有相同的边界值将阻止它识别正文的任何​​这些部分。

  3. 您已定义本地边界以包括newLine。显然,它根本不应该是local var,但结尾不能包含换行符,否则尝试附加/(boundary)--的最后一个边界将失败。

    很明显,如果您将此内容排除在边界之外,则请确保在构建主体时在需要的地方插入适当的换行符。最重要的是,确保您的身体看起来像下面的样子(带有最后的--):

    ----------------------------F2152BF1-CE54-4E86-B8D0-931FA36F7C36
    Content-Disposition: form-data; name="table_name"
    
    incident
    ----------------------------F2152BF1-CE54-4E86-B8D0-931FA36F7C36
    Content-Disposition: form-data; name="table_sys_id"
    
    ba931ddadbf93b00f7bbdd0b5e96193c
    ----------------------------F2152BF1-CE54-4E86-B8D0-931FA36F7C36
    Content-Disposition: form-data; name="file"; filename="Artboard@1x.png"
    Content-Type: image/png
    
    .....
    
    ----------------------------F2152BF1-CE54-4E86-B8D0-931FA36F7C36--
    
  4. curl的{​​{1}} example中,他们使用的字段名称为/now/attachment/upload,但是您正在使用uploadFile。您可能需要仔细检查您的字段名称,并匹配file和邮递员示例。

    curl

如果解决了上述问题后仍然无法解决问题,建议您使用CharlesWireshark并将成功的请求与您以编程方式生成的请求进行比较。

不用说,您可能想考虑使用Alamofire,这使您摆脱了创建格式正确的多部分请求的麻烦。