简体   繁体   English

Swift:使用 URLSession 进行后台上传

[英]Swift: Background upload using URLSession

I'm trying to upload files to the s3 bucket using URLSession.我正在尝试使用 URLSession 将文件上传到 s3 存储桶。 I understood that to upload files in the background, I need to use uploadTask(with:fromFile:) method as mentioned here .我知道要在后台上传文件,我需要使用这里提到的uploadTask(with:fromFile:)方法。 So I am performing below steps to upload the file.所以我正在执行以下步骤来上传文件。

  1. Create a background URLSession创建后台 URLSession
lazy var session: URLSession = {
                let bundleIdentifier = Bundle.main.bundleIdentifier!
                let config = URLSessionConfiguration.background(withIdentifier: bundleIdentifier + ".background")
                config.sharedContainerIdentifier = bundleIdentifier
                config.sessionSendsLaunchEvents = true
                return URLSession(configuration: config, delegate: self, delegateQueue: nil)
            }()
  1. Generate request with multipart data使用 多部分数据生成请求

Data Model数据 Model

struct UploadRequest {
    let destinationUrl: URL
    let sourceURL: URL
    let params: [String: String]
    let fileName: String
    let mimeType: String
}
private func requestAndPath(for
                                uploadParam: UploadRequest) -> (request: URLRequest,
                                                                filePath: URL)? {

        // Create an empty file and append header, file content and footer to it
        let uuid = UUID().uuidString
        let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
        let fileURL = directoryURL.appendingPathComponent(uuid)
        let filePath = fileURL.path
        FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil)
        let file = FileHandle(forWritingAtPath: filePath)!

        let boundary = UUID().uuidString
        let newline = "\r\n"

        do {
            let partName = "file"
            let data = try Data(contentsOf: uploadParam.sourceURL)
            // Write boundary header
            var header = ""
            header += "--\(boundary)" + newline
            header += "Content-Disposition: form-data; name=\"\(partName)\"; filename=\"\(uploadParam.fileName)\"" + newline
            for (key, value) in uploadParam.params {
                header += "Content-Disposition: form-data; name=\"\(key)" + newline
                header += newline
                header += value + newline
            }
            
            header += "Content-Type: \(uploadParam.mimeType)" + newline
            header += newline

            let headerData = header.data(using: .utf8, allowLossyConversion: false)
            // Write data
            file.write(headerData!)
            file.write(data)

            // Write boundary footer
            var footer = ""
            footer += newline
            footer += "--\(boundary)--" + newline
            footer += newline

            let footerData = footer.data(using: .utf8, allowLossyConversion: false)
            file.write(footerData!)
            file.closeFile()

            let contentType = "multipart/form-data; boundary=\(boundary)"
            var urlRequest = URLRequest(url: uploadParam.destinationUrl)
            urlRequest.httpMethod = "POST"
            urlRequest.setValue(contentType, forHTTPHeaderField: "Content-Type")
            return (urlRequest, fileURL)
        } catch {
            debugPrint("Error generating url request")
        }
        return nil
    }
  1. Upload file上传文件
func uploadFile(request: UploadRequest) {
       if let reqPath = requestAndPath(for: uploadRequest) {
            let task = session.uploadTask(with: reqPath.request,
                                          fromFile: reqPath.filePath)
            task.resume()
        }
}

When I call the uploadFile method, the delegate didSendBodyData is called once and the control goes to didCompleteWithError with error as nil.当我调用uploadFile方法时,委托didSendBodyData被调用一次,并且控件转到didCompleteWithError ,错误为nil。 But the file is not uploaded to the s3 bucket.但是文件没有上传到 s3 存储桶。 What could be the issue?可能是什么问题?

I am able to upload the file using Alamofire but since Alamofire doesn't support background upload , I would like to fallback to URLSession我可以使用 Alamofire 上传文件,但由于 Alamofire 不支持后台上传,我想回退到 URLSession

Upload using Alamofire (default)使用 Alamofire 上传(默认)

AF.upload(multipartFormData: { multipartFormData in
            for (key, value) in uploadRequest.params {
                if let data = value.data(using: String.Encoding.utf8, allowLossyConversion: false) {
                    multipartFormData.append(data, withName: key)
                }
            }
            multipartFormData.append(
                uploadRequest.sourceURL,
                withName: "File",
                fileName: uploadRequest.fileName,
                mimeType: uploadRequest.mimeType
            )
        }, with: urlRequest).responseData { response in
            if let responseData = response.data {
                let strData = String(decoding: responseData, as: UTF8.self)
                debugPrint("Response data \(strData)")
            } else {
                debugPrint("Error is \(response.error)")
            }
        }.uploadProgress { progress in
            debugPrint("Progress \(progress)")
        }

I made changes in the request body and wrote the data to the file and used uploadTask(with:fromFile:) method using the background session.我在请求正文中进行了更改并将数据写入文件并使用后台 session 的uploadTask(with:fromFile:)方法。 urlSessionDidFinishEvents(forBackgroundURLSession:) will be called once the upload is completed when the app is in the background. urlSessionDidFinishEvents(forBackgroundURLSession:)将在应用处于后台时在上传完成后调用。

private func requestAndPath(for
                                uploadParam: UploadRequest) -> (request: URLRequest,
                                                                filePath: URL)? {
        
        let uuid = UUID().uuidString
        let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
        let fileURL = directoryURL.appendingPathComponent(uuid)
        let filePath = fileURL.path
        FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil)
        let file = FileHandle(forWritingAtPath: filePath)!
        
        let boundary = generateBoundary()
        let lineBreak = "\r\n"
        var body = Data()

        for (key, value) in uploadParam.params {
            body.append("--\(boundary + lineBreak)")
            body.append("Content-Disposition: form-data; name=\"\(key)\"\(lineBreak + lineBreak)")
            body.append("\(value + lineBreak)")
        }
        
        do {
            let data = try Data(contentsOf: uploadParam.sourceURL)
            body.append("--\(boundary + lineBreak)")
            body.append("Content-Disposition: form-data; name=\"File\"; filename=\"\(uploadParam.fileName)\"\(lineBreak)")
            body.append("Content-Type: \(uploadParam.mimeType + lineBreak + lineBreak)")
            body.append(data)
            body.append(lineBreak)
            body.append("--\(boundary)--\(lineBreak)")
            file.write(body)
            file.closeFile()
            
            var urlRequest = URLRequest(url: uploadParam.destinationUrl)
            urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
            urlRequest.httpMethod = "POST"
            return (urlRequest, fileURL)
        } catch {
            debugPrint("Error getting request")
        }
        return nil
    } 


func generateBoundary() -> String {
        return UUID().uuidString
    }


extension Data {
    mutating func append(_ string: String) {
    if let data = string.data(using: .utf8) {
      self.append(data)
    }
  }
}

Reference: https://stackoverflow.com/a/58246456/696465参考: https://stackoverflow.com/a/58246456/696465

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Swift 4-使用URLSession获取上传图像进度 - Swift 4 - Get an Upload Image Progress using URLSession 如何使用 URLSession 在后台上传 zip 文件? - How to upload zip file in the background using URLSession? Swift:无法使用 URLSession 上传多张图片 - Swift: Unable to do multiple images upload using URLSession 使用URLSession将视频上传到Vimeo - Upload video to Vimeo using URLSession URLSession后台上传任务不断重置 - URLSession background upload task keeps resetting 迅速的3 +,URLsession,在后台显然失败了 - swift 3+, URLsession, in the background fails apparently at random 如何在 Swift 中使用 Codable 和 URLSession.shared.uploadTask (multipart/form-data) 上传图像文件? - How to upload image file using Codable and URLSession.shared.uploadTask (multipart/form-data) in Swift? 如何在Swift中使用URLSession添加UIActivityIndi​​catorView? - How to add UIActivityIndicatorView by using URLSession in Swift? 尝试在 swift 中使用 URLSession 解析 JSON 时出错 - Error trying to parse JSON using URLSession in swift 如何在后台下载后将属性传递给Swift 3 urlsession didFinishDownloadingTo委托? - how to pass properties to swift 3 urlsession didFinishDownloadingTo delegate after background download?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM