How to cancel Alamofire.upload

2020-07-06 00:30发布

I am uploading images on server via Alamofire.upload as multipart data. Unlike Alamofire.request it's not returning Request object, which I usually use to cancel requests.

But it's very reasonable to be able to cancel such a consuming requests like uploading. What are the options for this in Alamofire?

4条回答
狗以群分
2楼-- · 2020-07-06 00:37

I'm afraid you can't, according to the Alamofire source code the upload function returns an Request type in all of its overloads except in these that support MultipartFormData see the following code:

// MARK: MultipartFormData

/**
Creates an upload request using the shared manager instance for the specified method and URL string.

- parameter method:                  The HTTP method.
- parameter URLString:               The URL string.
- parameter headers: The HTTP headers. `nil` by default.
- parameter multipartFormData:       The closure used to append body parts to the `MultipartFormData`.
- parameter encodingMemoryThreshold: The encoding memory threshold in bytes. `MultipartFormDataEncodingMemoryThreshold` 
                                by default.
- parameter encodingCompletion:      The closure called when the `MultipartFormData` encoding is complete.
*/
public func upload(
  method: Method,
  URLString: URLStringConvertible,
  headers: [String: String]? = nil,
  multipartFormData: MultipartFormData -> Void,
  encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold,
  encodingCompletion: (Manager.MultipartFormDataEncodingResult -> Void)?)
{
   return Manager.sharedInstance.upload(
      method,
      URLString,
      headers: headers,
      multipartFormData: multipartFormData,
      encodingMemoryThreshold: encodingMemoryThreshold,
      encodingCompletion: encodingCompletion
   )
}

/**
Creates an upload request using the shared manager instance for the specified method and URL string.

- parameter URLRequest:              The URL request.
- parameter multipartFormData:       The closure used to append body parts to the `MultipartFormData`.
- parameter encodingMemoryThreshold: The encoding memory threshold in bytes. `MultipartFormDataEncodingMemoryThreshold`
                                by default.
- parameter encodingCompletion:      The closure called when the `MultipartFormData` encoding is complete.
*/
public func upload(
  URLRequest: URLRequestConvertible,
  multipartFormData: MultipartFormData -> Void,
  encodingMemoryThreshold: UInt64 =    Manager.MultipartFormDataEncodingMemoryThreshold,
  encodingCompletion: (Manager.MultipartFormDataEncodingResult -> Void)?)
{
   return Manager.sharedInstance.upload(
      URLRequest,
      multipartFormData: multipartFormData,
      encodingMemoryThreshold: encodingMemoryThreshold,
      encodingCompletion: encodingCompletion
   )
}

It's recommended when you are writing client-side code, use multipart/form-data when your form includes any <input type="file"> elements.

So if you want to just upload an image to the server you can use the another upload function overloads that returns an Request object and you can cancel it like in this ways proposed in the Alamofire documentation:

let fileURL = NSBundle.mainBundle().URLForResource("Default", withExtension: "png")
ler request =Alamofire.upload(.POST, "https://httpbin.org/post", file: fileURL)
// request.cancel()

I hope this help you.

查看更多
贼婆χ
3楼-- · 2020-07-06 00:51

Using the Uploading MultiPartFormData example from the Alamofire README:

Alamofire.upload(
    .POST,
    "https://httpbin.org/post",
    multipartFormData: { multipartFormData in
        multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
        multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow")
    },
    encodingCompletion: { encodingResult in
        switch encodingResult {
        case .Success(let upload, _, _):
            upload.responseJSON { response in
                debugPrint(response)
            }
        case .Failure(let encodingError):
            print(encodingError)
        }
    }
)

Here, upload.responseJSON returns a Request, which should allow you to assign it to something for cancellation later. For example:

let request = upload.responseJSON {  ...

...

request.cancel()
查看更多
Deceive 欺骗
4楼-- · 2020-07-06 00:52

It's possible to prepare a closure, and transfer a request out of "encodingCompletion"

class NetworkManager {
    private var closure: ((Request)->())?

    func startUpload() {
        Alamofire.upload(
            .POST,
            "https://httpbin.org/post",
            multipartFormData: { multipartFormData in
                multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
                multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow")
            },
            encodingCompletion: { encodingResult in
                switch encodingResult {
                case .Success(let upload, _, _):
                    self.closure?(upload)

                    upload.responseJSON { response in
                        debugPrint(response)
                        uploadRequest = nil
                    }
                case .Failure(let encodingError):
                    print(encodingError)
                }
            }
        )
    }
}
查看更多
等我变得足够好
5楼-- · 2020-07-06 00:56

In my case I created "sessionManager" instance in my API class and assigned Alamofire's session manager with configuration to it.

    var sessionManager: SessionManager!

    // Setup request manager
    let configuration = URLSessionConfiguration.default
    configuration.timeoutIntervalForResource = TimeInterval(15.0)
    configuration.timeoutIntervalForRequest = TimeInterval(15.0)
    sessionManager = Alamofire.SessionManager(configuration: configuration)


    sessionManager.upload(multipartFormData: { multipartFormData in

        // AppendMultipart parts
        multipartFormData.append(metadataBodyPart, withName: PartName.Metadata, mimeType: MimeType.MultiPart)
        multipartFormData.append(imageDataBodyPart, withName: PartName.Photo, mimeType: MimeType.ImageJPG)

    } // ... rest of the code

Then I could create a method to cancel any current request type. Note that here "(_, uploadTasks, _)" you can also have "dataTasks" & "downloadTasks" which you can also cancel if you want "(dataTasks, uploadTasks, downloadTasks)"

func cancelUploadRequest() {
    sessionManager.session.getTasksWithCompletionHandler { (_, uploadTasks, _) in
        uploadTasks.forEach { $0.cancel() }
    }
}

You can also have smth like this:

func cancel(request: CancelRequestType) {
    sessionManager.session.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) in
        switch request {
            case .DataTask:
                dataTasks.forEach { $0.cancel() }
                print("- - - Data task was canceled!")
            case .UploadTask:
                uploadTasks.forEach { $0.cancel() }
                print("- - - Upload task was canceled!")
            case .DownloadTask:
                downloadTasks.forEach { $0.cancel() }
                print("- - - Download task was canceled!")
            case .ZeroTask:
                print("- - - Zero tasks was found!")
        }
    }
}

Where "CancelRequestType" is enum. So you can call the method like API.cancel(request: .UploadTask)

enum CancelRequestType: String {
    case DownloadTask = "DownloadTask"
    case DataTask = "DataTask"
    case UploadTask = "UploadTask"
    case ZeroTask = "Zero"
}
查看更多
登录 后发表回答