【问题标题】:Swift 4 - Get an Upload Image Progress using URLSessionSwift 4 - 使用 URLSession 获取上传图像进度
【发布时间】:2019-05-08 02:26:26
【问题描述】:

我下面有这样的代码

func upload(){
    let img = UIImage(named: "1")
    let imgData = UIImageJPEGRepresentation(img!, 1)
    let data = imgData?.base64EncodedString()

    var request = URLRequest(url: URL(string: url)!)
    request.httpMethod = "POST"
    request.setValue("\(auth)", forHTTPHeaderField: "Authorization")
    request.setValue("application/xml", forHTTPHeaderField: "Content-Type")

    var postString = "<uploadrequest>"
    postString += "<folderid>123</folderid>"
    postString += "<folder_name>images</folder_name>"
    postString += "<image_byte>\(data!)</image_byte>"
    postString += "</uploadrequest>"

    request.httpBody = postString.data(using: .utf8)

    let configuration = URLSessionConfiguration.default
    let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)

    let task = session.uploadTask(with: request, from: imgData!, completionHandler: {
        (data, response, error) -> Void in

        if let error = error {
            print(error)
            return
        }
        if let data = data {
            _ = self.parseJsonData(data: data)
        }
    })

    task.resume()
}

func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
    print("sent:", totalBytesSent, "from:", totalBytesExpectedToSend)

    let size = totalBytesSent / totalBytesExpectedToSend
    self.progressbar.progress = Float(size)
}

我的目标是使用 API 上传图片并获取其上传进度。图像被转换为​​ base64 并插入到 API 的 poststring 上的&lt;image_byte&gt;\(data!)&lt;/image_byte&gt;

然后我得到的是这样的:

发送:32768 来自:927220 发送:65536 来自:927220 发件人:131072 发件人:927220 发送:163840 来自:927220 发送:262144 来自:927220 发送:294912 来自:927220 发件人:360448 发件人:927220 发件人:393216 发件人:927220 发件人:425984 发件人:927220 发送:491520 来自:927220 发送:524288 来自:927220 发件人:557056 发件人:927220 发件人:589824 发件人:927220 发件人:622592 发件人:927220 发送:655360 来自:927220 发件人:688128 发件人:927220 发件人:720896 发件人:927220 发件人:786432 发件人:927220 发送:819200 来自:927220 发件人:851968 发件人:927220 发送:927220 来自:927220 错误域 = NSCocoaErrorDomain 代码 = 3840 “JSON 文本没有以数组或对象开头,并且允许未设置片段的选项。” UserInfo={NSDebugDescription=JSON 文本不是以数组开头或 允许未设置片段的对象和选项。}

但是,如果我使用类似这样的其他方法(没有 uploadTask),它可以正常工作,但没有上传进度。

let task = URLSession.shared.dataTask(with: request, completionHandler: {
        (data, response, error) -> Void in

        if let error = error {
            print(error)
            return
        }
        if let data = data {
            _ = self.parseJsonData(data: data)
        }
    })

我也试过AlamofireAlamofirerequest 函数成功,但仍然没有上传进度,Alamofireuploadfunction (multipartformData) 出现此错误:

responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(error: Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 3." UserInfo={NSDebugDescription=Invalid value around character 3.}))

我想知道我的代码是否有错误。你能告诉我我应该如何处理我的代码吗?或者问题出在我的 API 上,因为我的 API 只接受 base64 代码?

如果你想查看我的Alamofire 代码,它是there

请有人帮我解决这个问题。

谢谢! :)

编辑 1

这是我的请求函数的结果(没有上传函数)

{
    "media_preview" =     {
        "image_media" =         {
            fileid = 4928501;
            filename = "zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            folderpath = "product\\66861/images800\\";
            height = 533;
            src = "zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            width = 800;
        };
        "medium_media" =         {
            fileid = 4928503;
            filename = "zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            folderpath = "product\\66861/images500\\";
            height = 333;
            src = "zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            width = 500;
        };
        "small_media" =         {
            fileid = 4928503;
            filename = "zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            folderpath = "\product\\66861/images300\\";
            height = 200;
            src = "/zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            width = 300;
        };
        "source_media" =         {
            fileid = 4928499;
            filename = "zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            folderpath = "\\product\\66861images\\";
            height = 666;
            src = "/zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            width = 999;
        };
        "thumbnail_media" =         {
            fileid = 4928507;
            filename = "zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            folderpath = \\product\\66861/thumb\\";
            height = 150;
            src = "/zhzvoHBb1ogda9bps13R5IavYiadCm.jpg";
            width = 150;
        };
    };
    responsestatus =     {
        "access_token" = "<null>";
        code = 00;
        description = Success;
    };
}

编辑 2 这是parseJsonData(data: Data)的代码

func parseJsonData(data: Data) {
    do {
        let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
        print(jsonResult!)
    } catch {
        print(error)
    }
}

【问题讨论】:

  • 错误似乎在那里:_ = self.parseJsonData(data: data)。您能否通过以下方式检查:let responseString = String(data: data, encoding: .utf8),打印并检查它是否真的是 JSON?您假设它是 JSON,但似乎并非如此(或者格式错误)。为什么我猜测:“Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed” ResponseSerialization,这是一个,而且非 Alamofire,它似乎是您使用 JSON 的唯一地方。顺便说一句,有在线工具可以检查 JSON 是否有效。
  • 问题很可能在于您的parseJsonData 函数,或者更确切地说是后端没有发送有效的 JSON 作为响应。请edit您的问题包括收到的JSON响应(您可以使用if let data = data { print(String(data: data, encoding: .utf8)!)打印它)和parseJsonData函数的代码。
  • 我已经用 json 结果编辑了我的问题 :) @DávidPásztor
  • “这是我的请求函数的结果(没有上传函数)”,你的意思是_ = self.parseJsonData(data: data)中的_?你当时正确地解析了它。还是String(data: data, encoding: .utf8)
  • 这是一个“或”的问题。哪一个?

标签: ios swift alamofire nsurlsession


【解决方案1】:

对于上传到服务器的多个参数的图像。试试这个代码 Alamofire。它会工作。

Alamofire.upload(multipartFormData: { (multipartFormData : MultipartFormData) in
            let count = imageToUpload.count
            for (key, value) in parameters {
                multipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
            }
            for i in 0..<count{
                let image:UIImage = self.loadImage(imageToUpload[i])
                if let imageData = image.jpegData(compressionQuality: 0.5) {
                    let imageName  = "image[\(i)]"
                    multipartFormData.append(imageData as Data, withName:imageName , fileName: imageToUpload[i], mimeType: "image/jpeg")
                }
            }
        }, usingThreshold: UInt64.init(), to: serviceUrl, method: .post, headers: headers)  { (result) in
            switch result {
            case .success(let upload, _ , _):
                upload.uploadProgress(closure: { (progress) in
                    print("uploding: \(progress.fractionCompleted)")
                })
                upload.responseJSON { response in
                    if response.result.isFailure {
                        if let err = response.error{
                            print(err)
                        }
                        return
                    }

                    guard let jsonData = response.result.value else {                           
                        return
                    }
                    //

                    do{
                        let json = try JSONSerialization.jsonObject(with: response.data as! Data, options: [])
                        print("josnResposne:\(json)")

                    } catch let parsingError {
                        print("Error", parsingError)
                    }

                }
            case .failure(let encodingError):
                print("failed")
                print(encodingError)                                        
            }
        }

【讨论】:

  • 如果我将图像作为base64字符串放在参数上怎么办?我还需要 multipartFormData.append(imageData as Data, withName:imageName , fileName: imageToUpload[i], mimeType: "image/jpeg") 吗?
  • @cathrinnatalia 最初我尝试将图像作为base64字符串放在参数上,但这不起作用,因此我尝试了另一种方式,就像我在上面的代码中所做的那样。
【解决方案2】:

获取进度使用以下 URLSession 的委托方法,您可以在didReceiveData 方法中获取进度

func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {

        //here you can get full lenth of your content
        expectedContentLength = Int(response.expectedContentLength)
        println(expectedContentLength)
        completionHandler(NSURLSessionResponseDisposition.Allow)
    }

    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {


        buffer.appendData(data)

        let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength)
        progress.progress =  percentageDownloaded
    }

    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        //use buffer here.Download is done
        progress.progress = 1.0   // download 100% complete
    }

【讨论】:

  • 我做到了。但我遇到了一个错误,就像我之前解释的那样
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-09
  • 2017-01-30
  • 2017-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-13
相关资源
最近更新 更多