【问题标题】:iOS - How to upload a video with uploadTask?iOS - 如何使用 uploadTask 上传视频?
【发布时间】:2017-06-28 09:49:54
【问题描述】:

我需要在后台将 mp4 视频文件从 iPhone/iPad 上传到服务器,所以我读到了 URLSession.uploadTask(with: URLRequest, fromFile: URL)方法,但我不明白我之前如何准备请求。我需要创建一个 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 是如何工作的?也许它将文件的数据附加到请求正文然后自动添加边界?如果我使用此代码,上传不起作用,我需要更改什么?

更新:我已经更新了代码,现在使用uploadTask的completionHandler在前台上传,但是如果我创建一个后台会话并使用URLSessionDataDelegate而不是completionHandler(因为它没有't work in the back),传输速度也很慢,2 MB的文件,我该如何解决这个问题?

更新 2:在后台会话中,uploadTask 多次重新启动,但从未完成,永远不会。

【问题讨论】:

  • 使用 alamofire 。
  • 如果可能的话,我不会使用任何外部框架来做到这一点
  • 该示例显示 dataTask(它在后台不起作用)和 downloadTask,我需要工作 uploadTask 示例
  • 后台任务需要配置。它不依赖于上传任务或数据任务。

标签: ios swift3 nsurlsessionuploadtask


【解决方案1】:

经过一些尝试,我看到 URLSession.uploadTask(with: URLRequest, fromFile: URL) 方法将文件作为原始正文附加到请求中,所以问题在于正在解析的服务器对应方表单数据请求而不是原始正文请求。修复服务器端脚本后,上传在后台使用以下代码进行:

    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()

【讨论】:

  • 谢谢,这非常有帮助!您介意与服务器对应方分享您必须执行的操作的详细信息吗?
  • 谢谢,这帮助我意识到我没有将原始参数设置为 true
  • 如何将文件放入服务器端? @罗兰
【解决方案2】:

从 2020 年开始使用原生 URLSession 使用 uploadTaskmultipart/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 处理:我这样做的方式非常标准,您可以在许多在线教程中找到

希望有帮助

【讨论】:

  • 谢谢,这个链接真的很有帮助。您是如何从委派中获得服务器响应的?
  • 我只是使用didCompleteWithError检查我的请求是否完成,我没有得到服务器响应,您可能需要自己在google上搜索。
【解决方案3】:

有时服务器从表单数据中读取文件更容易。例如,flask 框架可以通过 request.files 轻松读取以 form-data 格式上传的文件。 Alamofire 提供了一种简单的方法来做到这一点

AF.upload(multipartFormData: { multipartFormData in
                multipartFormData.append(FilePath, withName: FilePath.lastPathComponent)
            }, to: url).responseJSON { response in
                    debugPrint(response)
            }

这样,文件就会以form-data的格式上传。

【讨论】:

    猜你喜欢
    • 2015-10-23
    • 2014-03-10
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-12
    相关资源
    最近更新 更多