|
|
@@ -156,6 +156,150 @@ extension Manager {
|
|
|
public func upload(method: Method, _ URLString: URLStringConvertible, stream: NSInputStream) -> Request {
|
|
|
return upload(URLRequest(method, URLString), stream: stream)
|
|
|
}
|
|
|
+
|
|
|
+ // MARK: MultipartFormData
|
|
|
+
|
|
|
+ /// Default memory threshold used when encoding `MultipartFormData`.
|
|
|
+ public static let MultipartFormDataEncodingMemoryThreshold: UInt64 = 10 * 1024 * 1024
|
|
|
+
|
|
|
+ /**
|
|
|
+ Defines whether the `MultipartFormData` encoding was successful and contains result of the encoding as
|
|
|
+ associated values.
|
|
|
+
|
|
|
+ - Success: Represents a successful `MultipartFormData` encoding and contains the new `Request` along with
|
|
|
+ streaming information.
|
|
|
+ - Failure: Used to represent a failure in the `MultipartFormData` encoding and also contains the encoding
|
|
|
+ error.
|
|
|
+ */
|
|
|
+ public enum MultipartFormDataEncodingResult {
|
|
|
+ case Success(request: Request, streamingFromDisk: Bool, streamFileURL: NSURL?)
|
|
|
+ case Failure(NSError)
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Encodes the `MultipartFormData` and creates a request to upload the result to the specified URL request.
|
|
|
+
|
|
|
+ It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
|
|
+ payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
|
|
+ efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
|
|
+ be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
|
|
+ footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
|
|
+ used for larger payloads such as video content.
|
|
|
+
|
|
|
+ The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
|
|
+ or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
|
|
+ encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
|
|
+ during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
|
|
+ technique was used.
|
|
|
+
|
|
|
+ If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
|
|
+
|
|
|
+ :param: method The HTTP method.
|
|
|
+ :param: URLString The URL string.
|
|
|
+ :param: multipartFormData The closure used to append body parts to the `MultipartFormData`.
|
|
|
+ :param: encodingMemoryThreshold The encoding memory threshold in bytes. `MultipartFormDataEncodingMemoryThreshold`
|
|
|
+ by default.
|
|
|
+ :param: encodingCompletion The closure called when the `MultipartFormData` encoding is complete.
|
|
|
+ */
|
|
|
+ public func upload(
|
|
|
+ method: Method,
|
|
|
+ _ URLString: URLStringConvertible,
|
|
|
+ multipartFormData: MultipartFormData -> Void,
|
|
|
+ encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold,
|
|
|
+ encodingCompletion: (MultipartFormDataEncodingResult -> Void)?)
|
|
|
+ {
|
|
|
+ let urlRequest = URLRequest(method, URLString)
|
|
|
+
|
|
|
+ return upload(
|
|
|
+ urlRequest,
|
|
|
+ multipartFormData: multipartFormData,
|
|
|
+ encodingMemoryThreshold: encodingMemoryThreshold,
|
|
|
+ encodingCompletion: encodingCompletion
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Encodes the `MultipartFormData` and creates a request to upload the result to the specified URL request.
|
|
|
+
|
|
|
+ It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
|
|
+ payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
|
|
+ efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
|
|
+ be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
|
|
+ footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
|
|
+ used for larger payloads such as video content.
|
|
|
+
|
|
|
+ The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
|
|
+ or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
|
|
+ encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
|
|
+ during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
|
|
+ technique was used.
|
|
|
+
|
|
|
+ If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
|
|
+
|
|
|
+ :param: URLRequest The URL request.
|
|
|
+ :param: multipartFormData The closure used to append body parts to the `MultipartFormData`.
|
|
|
+ :param: encodingMemoryThreshold The encoding memory threshold in bytes. `MultipartFormDataEncodingMemoryThreshold`
|
|
|
+ by default.
|
|
|
+ :param: encodingCompletion The closure called when the `MultipartFormData` encoding is complete.
|
|
|
+ */
|
|
|
+ public func upload(
|
|
|
+ URLRequest: URLRequestConvertible,
|
|
|
+ multipartFormData: MultipartFormData -> Void,
|
|
|
+ encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold,
|
|
|
+ encodingCompletion: (MultipartFormDataEncodingResult -> Void)?)
|
|
|
+ {
|
|
|
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
|
|
|
+ let formData = MultipartFormData()
|
|
|
+ multipartFormData(formData)
|
|
|
+
|
|
|
+ if formData.contentLength < encodingMemoryThreshold {
|
|
|
+ let encodingResult = formData.encode()
|
|
|
+
|
|
|
+ dispatch_async(dispatch_get_main_queue()) {
|
|
|
+ switch encodingResult {
|
|
|
+ case .Success(let data):
|
|
|
+ let encodingResult = MultipartFormDataEncodingResult.Success(
|
|
|
+ request: self.upload(URLRequest, data: data),
|
|
|
+ streamingFromDisk: false,
|
|
|
+ streamFileURL: nil
|
|
|
+ )
|
|
|
+ encodingCompletion?(encodingResult)
|
|
|
+ case .Failure(let error):
|
|
|
+ encodingCompletion?(.Failure(error))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ let fileManager = NSFileManager.defaultManager()
|
|
|
+ let tempDirectoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory())!
|
|
|
+ let directoryURL = tempDirectoryURL.URLByAppendingPathComponent("com.alamofire.manager/multipart.form.data")
|
|
|
+ let fileName = NSUUID().UUIDString
|
|
|
+ let fileURL = directoryURL.URLByAppendingPathComponent(fileName)
|
|
|
+
|
|
|
+ var error: NSError?
|
|
|
+
|
|
|
+ if fileManager.createDirectoryAtURL(directoryURL, withIntermediateDirectories: true, attributes: nil, error: &error) {
|
|
|
+ formData.writeEncodedDataToDisk(fileURL) { error in
|
|
|
+ dispatch_async(dispatch_get_main_queue()) {
|
|
|
+ if let error = error {
|
|
|
+ encodingCompletion?(.Failure(error))
|
|
|
+ } else {
|
|
|
+ let encodingResult = MultipartFormDataEncodingResult.Success(
|
|
|
+ request: self.upload(URLRequest, file: fileURL),
|
|
|
+ streamingFromDisk: true,
|
|
|
+ streamFileURL: fileURL
|
|
|
+ )
|
|
|
+ encodingCompletion?(encodingResult)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dispatch_async(dispatch_get_main_queue()) {
|
|
|
+ encodingCompletion?(.Failure(error!))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// MARK: -
|