我正在尝试用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
}
}