代码之家  ›  专栏  ›  技术社区  ›  Bartender1382

如何在Swift中找到创建多部分/表单请求失败的地方?

  •  0
  • Bartender1382  · 技术社区  · 3 年前

    我正在尝试用Swift上传一个文件,但我认为我无法构建正确的httpBody,我似乎无法找到它的位置。

    我在iPhone模拟器上获取一个文件,将其内容加载到一个数据变量中,然后构建一个URLRequest,它应该发送 用户名 , 暗语 ,以及 文件名 以及它的内容。

    响应是200,我的服务器记录如何调用正确的Perl脚本,但非常简单的脚本(在命令行中测试)看不到头。而且,我在URLSession中没有发现错误。

    我已经检查了请求变量,据我所知,它似乎是正确的。

    如果有人能发现我哪里出了问题,或者我应该在哪里寻找错误。

    以下是: URLRequest正文和我的示例应用程序的完整代码

    请求主体 如调试器中所示

    http://example.com/cgi-bin/swift.pl
    url:可选
    一些: http://example.com/cgi-bin/swift.pl
    _网址: http://example.com/cgi-bin/swift.pl
    缓存策略:0
    timeoutInterval:60.0
    main文档URL:nil

    networkServiceType:u C.NSURLRequestNetworkServiceType

    AllowCellularAccess:正确
    httpMethod:可选
    一些:“帖子”
    所有HttpHeaderFields:可选<字典<弦,弦>>
    一些:1个元素
    0:2个元素
    关键字:“ContentType”
    值:“多部分/formdata;边界=边界E8B0434637F341F0962215B412B61FAA”
    httpBody: 可选择的
    部分:27075字节
    计数:27075
    指针:0x00007f9d15c00000
    pointerValue:140312651497472
    httpBodyStream:无
    httpShouldHandleCookies:没错

    HttpShouldUsePipeline:错误

    示例应用程序

    import UIKit
    import MobileCoreServices
    
    typealias Params = [String: String]
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let myButton = UIButton(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 100, height: 50)))
            myButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
            myButton.backgroundColor = .systemGray
            view.addSubview(myButton)
        }
    
       @objc func buttonAction(sender: UIButton!) {
            var params = [String: String]()
            
            params = ["username" : "John", "userid" : "13"]
            let fManager = FileManager.default
            guard let mainurl = fManager.urls(for: .documentDirectory, in: .userDomainMask).first else { print ("oh no");return;}
    
            print ("\(mainurl.path)")
            let myFile = mainurl.path + "/inv.xlsx"
            let upFile = XFile(filename: myFile, key: "filename")
    
            var components = URLComponents()
            components.scheme = "http"
            components.host = "example.com"
            components.path = "/cgi-bin/swift.pl"
        
            let url = components.url
            if url == nil { return }
            var request = URLRequest(url: url!)
            request.httpMethod = "POST"
            
            let boundary = generateBoundary()
            
            request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
            
            
            let dataBody = createDataBody(params: params, file: upFile, boundary: boundary)
            request.httpBody = dataBody
            
            let session = URLSession.shared
            session.dataTask(with: request)
    //        session.uploadTask(with: request, from: request.httpBody)
            { (data, response, error) in
                if let response = response {
                    print(response)
                }
            
            if let error = error {
                print(error)
            }
                            
                if let data = data {
                    do {
                        let json = try JSONSerialization.jsonObject(with: data, options: [])
                        print(json)
                    } catch {
                        print(error)
                    }
                }
                }.resume()
            
        }
        
        func generateBoundary() -> String {
            return "Boundary-\(NSUUID().uuidString)"
        }
        
        func createDataBody(params: Params?, file: XFile?, boundary: String) -> Data {
            let lineBreak = "\r\n"
            var body = Data()
            
            if let parameters = params {
                for (key, value) in parameters {
                    body.append("--\(boundary + lineBreak)")
                    body.append("Content Disposition: form-data; name=\"\(key)\"\(lineBreak + lineBreak)")
                    body.append("\(value + lineBreak)")
                }
            }
            
            if let xFile = file {
                body.append("--\(boundary + lineBreak)")
                body.append("Content Disposition: form-data; name=\"\(xFile.key)\"; filename=\"\(xFile.filename)\"\(lineBreak)")
                body.append("Content-Type: \(xFile.mimeType! + lineBreak + lineBreak)")
                body.append(xFile.data)
                body.append(lineBreak)
            }
            
            body.append("--\(boundary)--\(lineBreak)")
            
            return body
        }
    }
    
    extension Data {
        mutating func append(_ string: String) {
            if let data = string.data(using: .utf8) {
                append(data)
            }
        }
    }
    

    保存文件上传的结构

    import UIKit
    import MobileCoreServices
    
    struct XFile {
        let key: String
        let filename: String
        let data: Data
        var mimeType: String?
        
        init?(filename: String, key: String) {
            let fileUrl = URL.init(fileURLWithPath: filename)
            
            
            self.key = key
            self.filename = "\(arc4random()).\(URL(fileURLWithPath: filename).pathExtension)"
    
            do {
                self.data = try Data(contentsOf: fileUrl)
            } catch { return nil}
    
            self.mimeType = mimeType(path: filename)
        }
    
        func mimeType(path: String) -> String {
                let pathExtension = URL(fileURLWithPath: path).pathExtension as NSString
                guard
                    let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, nil)?.takeRetainedValue(),
                    let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue()
                else {
                    return "application/octet-stream"
                }
     
                return mimetype as String
            }
    }
    
    0 回复  |  直到 3 年前