iOS - 如何使用uploadTask上传视频?
iOS - How to upload a video with uploadTask?
我需要将 mp4 视频文件从 iPhone/iPad 上传到服务器,也在后台,所以我读到 URLSession.uploadTask 可以做到这一点(with: URLRequest, fromFile: URL) 方法,但是我不明白如何准备请求 before.I 需要创建一个 multipart/form-data 请求,因为我想要附加其他字符串参数。
func requestBodyFor(video: URL) -> Data? {
let url = URL(string: "url_of_upload_handler.php")!
let parameters = ["type":"video", "user":"112"]
do {
let kBoundary = "Boundary-\(UUID().uuidString)"
let kStartTag = "--%@\r\n"
let kEndTag = "\r\n"
let kContent = "Content-Disposition: form-data; name=\"%@\"\r\n\r\n"
var body = Data()
let videoData = try Data(contentsOf: video)
// parameters
for (key,value) in parameters {
body.append(String(format: kStartTag, kBoundary).data(using: String.Encoding.utf8)!)
body.append(String(format: kContent, key).data(using: String.Encoding.utf8)!)
body.append(value.data(using: String.Encoding.utf8)!)
body.append(String(format: kEndTag).data(using: String.Encoding.utf8)!)
}
//Video data
body.append(String(format: kStartTag, boundary).data(using: String.Encoding.utf8)!)
body.append(String(format: "Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", "file", video.lastPathComponent).data(using: String.Encoding.utf8)!)
body.append("Content-Type: video/mp4\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append(videoData)
body.append(String(format: kEndTag).data(using: String.Encoding.utf8)!)
// close form
body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
return body
} catch let error {
print(error)
return nil
}
}
if let body = requestBodyFor(video: fileUrl) {
let contentType = "multipart/form-data; boundary=\(kBoundary)"
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.uploadTask(with: request, from: body) { data, response, error in
guard error == nil && data != nil else {
return
}
if let data = String(data: data!, encoding: String.Encoding.utf8) {
print(data)
}
}
task.resume()
}
uploadTask 是如何工作的?也许它将文件的数据附加到请求体,然后自动添加边界?如果我使用此代码,上传不起作用,我必须更改什么?
UPDATE:我更新了代码,现在使用 uploadTask 的 completionHandler 在前台进行上传,但是如果我创建后台会话并使用 URLSessionDataDelegate 而不是 completionHandler(因为它在后台不工作),对于 2 MB 的文件,传输速率也很慢,我该如何解决这个问题?
更新 2:在后台会话中,uploadTask 重新启动多次但没有完成,永远不会。
经过一些尝试,我看到 URLSession.uploadTask(with: URLRequest, fromFile: URL) 方法将文件附加为请求的原始正文,所以问题出在解析表单数据请求而不是原始正文的服务器对应 requests.After 我修复了服务器端脚本,上传在后台使用此代码进行:
var request = URLRequest(url: "my_url")
request.httpMethod = "POST"
request.setValue(file.lastPathComponent, forHTTPHeaderField: "filename")
let sessionConfig = URLSessionConfiguration.background(withIdentifier: "it.example.upload")
sessionConfig.isDiscretionary = false
sessionConfig.networkServiceType = .video
let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: OperationQueue.main)
let task = session.uploadTask(with: request, fromFile: file)
task.resume()
有时服务器更容易从表单数据中读取文件。比如flask框架可以很方便的读取form-data格式上传的文件request.files。
Alamofire 提供了一种简单的方法来做到这一点
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(FilePath, withName: FilePath.lastPathComponent)
}, to: url).responseJSON { response in
debugPrint(response)
}
这样,文件将以form-data格式上传。
截至 2020 年的解决方案,原生 URLSession
到 运行 后台上传 uploadTask
和 multipart/form-data
:
- 我按照 this tutorial 设置了对
multipart/form-data
的请求,因为它需要我的 NodeJS 服务器
- 然后我在设置部分做了一些改动
URLSession
:
let config = URLSessionConfiguration.background(withIdentifier: "uniqueID")
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
// This line is important: here we use withStreamedRequest
let task = session.uploadTask(withStreamedRequest: request)
task.resume()
关于我的服务器端的一些信息:
- 它是用 NodeJS 和 Express 编写的
- 文件上传由Multer处理:我做的很标准,网上很多教程都可以找到
希望对您有所帮助
我需要将 mp4 视频文件从 iPhone/iPad 上传到服务器,也在后台,所以我读到 URLSession.uploadTask 可以做到这一点(with: URLRequest, fromFile: URL) 方法,但是我不明白如何准备请求 before.I 需要创建一个 multipart/form-data 请求,因为我想要附加其他字符串参数。
func requestBodyFor(video: URL) -> Data? {
let url = URL(string: "url_of_upload_handler.php")!
let parameters = ["type":"video", "user":"112"]
do {
let kBoundary = "Boundary-\(UUID().uuidString)"
let kStartTag = "--%@\r\n"
let kEndTag = "\r\n"
let kContent = "Content-Disposition: form-data; name=\"%@\"\r\n\r\n"
var body = Data()
let videoData = try Data(contentsOf: video)
// parameters
for (key,value) in parameters {
body.append(String(format: kStartTag, kBoundary).data(using: String.Encoding.utf8)!)
body.append(String(format: kContent, key).data(using: String.Encoding.utf8)!)
body.append(value.data(using: String.Encoding.utf8)!)
body.append(String(format: kEndTag).data(using: String.Encoding.utf8)!)
}
//Video data
body.append(String(format: kStartTag, boundary).data(using: String.Encoding.utf8)!)
body.append(String(format: "Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", "file", video.lastPathComponent).data(using: String.Encoding.utf8)!)
body.append("Content-Type: video/mp4\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append(videoData)
body.append(String(format: kEndTag).data(using: String.Encoding.utf8)!)
// close form
body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
return body
} catch let error {
print(error)
return nil
}
}
if let body = requestBodyFor(video: fileUrl) {
let contentType = "multipart/form-data; boundary=\(kBoundary)"
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.uploadTask(with: request, from: body) { data, response, error in
guard error == nil && data != nil else {
return
}
if let data = String(data: data!, encoding: String.Encoding.utf8) {
print(data)
}
}
task.resume()
}
uploadTask 是如何工作的?也许它将文件的数据附加到请求体,然后自动添加边界?如果我使用此代码,上传不起作用,我必须更改什么?
UPDATE:我更新了代码,现在使用 uploadTask 的 completionHandler 在前台进行上传,但是如果我创建后台会话并使用 URLSessionDataDelegate 而不是 completionHandler(因为它在后台不工作),对于 2 MB 的文件,传输速率也很慢,我该如何解决这个问题?
更新 2:在后台会话中,uploadTask 重新启动多次但没有完成,永远不会。
经过一些尝试,我看到 URLSession.uploadTask(with: URLRequest, fromFile: URL) 方法将文件附加为请求的原始正文,所以问题出在解析表单数据请求而不是原始正文的服务器对应 requests.After 我修复了服务器端脚本,上传在后台使用此代码进行:
var request = URLRequest(url: "my_url")
request.httpMethod = "POST"
request.setValue(file.lastPathComponent, forHTTPHeaderField: "filename")
let sessionConfig = URLSessionConfiguration.background(withIdentifier: "it.example.upload")
sessionConfig.isDiscretionary = false
sessionConfig.networkServiceType = .video
let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: OperationQueue.main)
let task = session.uploadTask(with: request, fromFile: file)
task.resume()
有时服务器更容易从表单数据中读取文件。比如flask框架可以很方便的读取form-data格式上传的文件request.files。 Alamofire 提供了一种简单的方法来做到这一点
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(FilePath, withName: FilePath.lastPathComponent)
}, to: url).responseJSON { response in
debugPrint(response)
}
这样,文件将以form-data格式上传。
截至 2020 年的解决方案,原生 URLSession
到 运行 后台上传 uploadTask
和 multipart/form-data
:
- 我按照 this tutorial 设置了对
multipart/form-data
的请求,因为它需要我的 NodeJS 服务器 - 然后我在设置部分做了一些改动
URLSession
:
let config = URLSessionConfiguration.background(withIdentifier: "uniqueID")
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
// This line is important: here we use withStreamedRequest
let task = session.uploadTask(withStreamedRequest: request)
task.resume()
关于我的服务器端的一些信息:
- 它是用 NodeJS 和 Express 编写的
- 文件上传由Multer处理:我做的很标准,网上很多教程都可以找到
希望对您有所帮助