|
|
@@ -109,6 +109,7 @@ public class MultipartFormData {
|
|
|
public let boundary: String
|
|
|
|
|
|
private var bodyParts: [BodyPart]
|
|
|
+ private var bodyPartError: NSError?
|
|
|
private let streamBufferSize: Int
|
|
|
|
|
|
// MARK: - Lifecycle
|
|
|
@@ -133,6 +134,71 @@ public class MultipartFormData {
|
|
|
|
|
|
// MARK: - Body Parts
|
|
|
|
|
|
+ /**
|
|
|
+ Creates a body part from the data and appends it to the multipart form data object.
|
|
|
+
|
|
|
+ The body part data will be encoded using the following format:
|
|
|
+
|
|
|
+ - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
|
|
|
+ - Encoded data
|
|
|
+ - Multipart form boundary
|
|
|
+
|
|
|
+ :param: data The data to encode into the multipart form data.
|
|
|
+ :param: name The name to associate with the data in the `Content-Disposition` HTTP header.
|
|
|
+ */
|
|
|
+ public func appendBodyPart(#data: NSData, name: String) {
|
|
|
+ let headers = contentHeaders(name: name)
|
|
|
+ let stream = NSInputStream(data: data)
|
|
|
+ let length = UInt64(data.length)
|
|
|
+
|
|
|
+ appendBodyPart(stream: stream, length: length, headers: headers)
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Creates a body part from the data and appends it to the multipart form data object.
|
|
|
+
|
|
|
+ The body part data will be encoded using the following format:
|
|
|
+
|
|
|
+ - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
|
|
|
+ - `Content-Type: #{generated mimeType}` (HTTP Header)
|
|
|
+ - Encoded data
|
|
|
+ - Multipart form boundary
|
|
|
+
|
|
|
+ :param: data The data to encode into the multipart form data.
|
|
|
+ :param: name The name to associate with the data in the `Content-Disposition` HTTP header.
|
|
|
+ :param: mimeType The MIME type to associate with the data content type in the `Content-Type` HTTP header.
|
|
|
+ */
|
|
|
+ public func appendBodyPart(#data: NSData, name: String, mimeType: String) {
|
|
|
+ let headers = contentHeaders(name: name, mimeType: mimeType)
|
|
|
+ let stream = NSInputStream(data: data)
|
|
|
+ let length = UInt64(data.length)
|
|
|
+
|
|
|
+ appendBodyPart(stream: stream, length: length, headers: headers)
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Creates a body part from the data and appends it to the multipart form data object.
|
|
|
+
|
|
|
+ The body part data will be encoded using the following format:
|
|
|
+
|
|
|
+ - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
|
|
+ - `Content-Type: #{mimeType}` (HTTP Header)
|
|
|
+ - Encoded file data
|
|
|
+ - Multipart form boundary
|
|
|
+
|
|
|
+ :param: data The data to encode into the multipart form data.
|
|
|
+ :param: name The name to associate with the data in the `Content-Disposition` HTTP header.
|
|
|
+ :param: fileName The filename to associate with the data in the `Content-Disposition` HTTP header.
|
|
|
+ :param: mimeType The MIME type to associate with the data in the `Content-Type` HTTP header.
|
|
|
+ */
|
|
|
+ public func appendBodyPart(#data: NSData, name: String, fileName: String, mimeType: String) {
|
|
|
+ let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType)
|
|
|
+ let stream = NSInputStream(data: data)
|
|
|
+ let length = UInt64(data.length)
|
|
|
+
|
|
|
+ appendBodyPart(stream: stream, length: length, headers: headers)
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
Creates a body part from the file and appends it to the multipart form data object.
|
|
|
|
|
|
@@ -147,24 +213,23 @@ public class MultipartFormData {
|
|
|
`fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the
|
|
|
system associated MIME type.
|
|
|
|
|
|
- :param: URL The URL of the file whose content will be encoded into the multipart form data.
|
|
|
- :param: name The name to associate with the file content in the `Content-Disposition` HTTP header.
|
|
|
-
|
|
|
- :returns: An `NSError` if an error occurred, `nil` otherwise.
|
|
|
+ :param: fileURL The URL of the file whose content will be encoded into the multipart form data.
|
|
|
+ :param: name The name to associate with the file content in the `Content-Disposition` HTTP header.
|
|
|
*/
|
|
|
- public func appendBodyPart(fileURL URL: NSURL, name: String) -> NSError? {
|
|
|
+ public func appendBodyPart(#fileURL: NSURL, name: String) {
|
|
|
if let
|
|
|
- fileName = URL.lastPathComponent,
|
|
|
- pathExtension = URL.pathExtension
|
|
|
+ fileName = fileURL.lastPathComponent,
|
|
|
+ pathExtension = fileURL.pathExtension
|
|
|
{
|
|
|
let mimeType = mimeTypeForPathExtension(pathExtension)
|
|
|
- return appendBodyPart(fileURL: URL, name: name, fileName: fileName, mimeType: mimeType)
|
|
|
- }
|
|
|
-
|
|
|
- let failureReason = "Failed to extract the fileName of the provided URL: \(URL)"
|
|
|
- let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
|
|
|
+ appendBodyPart(fileURL: fileURL, name: name, fileName: fileName, mimeType: mimeType)
|
|
|
+ } else {
|
|
|
+ let failureReason = "Failed to extract the fileName of the provided URL: \(fileURL)"
|
|
|
+ let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
|
|
|
+ let error = NSError(domain: AlamofireErrorDomain, code: NSURLErrorBadURL, userInfo: userInfo)
|
|
|
|
|
|
- return NSError(domain: AlamofireErrorDomain, code: NSURLErrorBadURL, userInfo: userInfo)
|
|
|
+ setBodyPartError(error)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -177,117 +242,96 @@ public class MultipartFormData {
|
|
|
- Encoded file data
|
|
|
- Multipart form boundary
|
|
|
|
|
|
- :param: URL The URL of the file whose content will be encoded into the multipart form data.
|
|
|
+ :param: fileURL The URL of the file whose content will be encoded into the multipart form data.
|
|
|
:param: name The name to associate with the file content in the `Content-Disposition` HTTP header.
|
|
|
:param: fileName The filename to associate with the file content in the `Content-Disposition` HTTP header.
|
|
|
:param: mimeType The MIME type to associate with the file content in the `Content-Type` HTTP header.
|
|
|
-
|
|
|
- :returns: An `NSError` if an error occurred, `nil` otherwise.
|
|
|
*/
|
|
|
- public func appendBodyPart(fileURL URL: NSURL, name: String, fileName: String, mimeType: String) -> NSError? {
|
|
|
+ public func appendBodyPart(#fileURL: NSURL, name: String, fileName: String, mimeType: String) {
|
|
|
let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType)
|
|
|
var isDirectory: ObjCBool = false
|
|
|
+ var error: NSError?
|
|
|
|
|
|
- if !URL.fileURL {
|
|
|
- return errorWithCode(NSURLErrorBadURL, failureReason: "The URL does not point to a file URL: \(URL)")
|
|
|
- } else if !URL.checkResourceIsReachableAndReturnError(nil) {
|
|
|
- return errorWithCode(NSURLErrorBadURL, failureReason: "The URL is not reachable: \(URL)")
|
|
|
- } else if NSFileManager.defaultManager().fileExistsAtPath(URL.path!, isDirectory: &isDirectory) && isDirectory {
|
|
|
- return errorWithCode(NSURLErrorBadURL, failureReason: "The URL is a directory, not a file: \(URL)")
|
|
|
+ if !fileURL.fileURL {
|
|
|
+ error = errorWithCode(NSURLErrorBadURL, failureReason: "The URL does not point to a file URL: \(fileURL)")
|
|
|
+ } else if !fileURL.checkResourceIsReachableAndReturnError(nil) {
|
|
|
+ error = errorWithCode(NSURLErrorBadURL, failureReason: "The URL is not reachable: \(fileURL)")
|
|
|
+ } else if NSFileManager.defaultManager().fileExistsAtPath(fileURL.path!, isDirectory: &isDirectory) && isDirectory {
|
|
|
+ error = errorWithCode(NSURLErrorBadURL, failureReason: "The URL is a directory, not a file: \(fileURL)")
|
|
|
}
|
|
|
|
|
|
- let bodyContentLength: UInt64
|
|
|
- var fileAttributesError: NSError?
|
|
|
+ if let error = error {
|
|
|
+ setBodyPartError(error)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let length: UInt64
|
|
|
|
|
|
if let
|
|
|
- path = URL.path,
|
|
|
- attributes = NSFileManager.defaultManager().attributesOfItemAtPath(path, error: &fileAttributesError),
|
|
|
+ path = fileURL.path,
|
|
|
+ attributes = NSFileManager.defaultManager().attributesOfItemAtPath(path, error: &error),
|
|
|
fileSize = (attributes[NSFileSize] as? NSNumber)?.unsignedLongLongValue
|
|
|
{
|
|
|
- bodyContentLength = fileSize
|
|
|
+ length = fileSize
|
|
|
} else {
|
|
|
- return errorWithCode(NSURLErrorBadURL, failureReason: "Could not fetch attributes from the URL: \(URL)")
|
|
|
+ let failureReason = "Could not fetch attributes from the URL: \(fileURL)"
|
|
|
+ let error = errorWithCode(NSURLErrorBadURL, failureReason: failureReason)
|
|
|
+
|
|
|
+ setBodyPartError(error)
|
|
|
+
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
- if let bodyStream = NSInputStream(URL: URL) {
|
|
|
- let bodyPart = BodyPart(headers: headers, bodyStream: bodyStream, bodyContentLength: bodyContentLength)
|
|
|
- self.bodyParts.append(bodyPart)
|
|
|
+ if let stream = NSInputStream(URL: fileURL) {
|
|
|
+ appendBodyPart(stream: stream, length: length, headers: headers)
|
|
|
} else {
|
|
|
- let failureReason = "Failed to create an input stream from the URL: \(URL)"
|
|
|
- return errorWithCode(NSURLErrorCannotOpenFile, failureReason: failureReason)
|
|
|
- }
|
|
|
+ let failureReason = "Failed to create an input stream from the URL: \(fileURL)"
|
|
|
+ let error = errorWithCode(NSURLErrorCannotOpenFile, failureReason: failureReason)
|
|
|
|
|
|
- return nil
|
|
|
+ setBodyPartError(error)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Creates a body part from the data and appends it to the multipart form data object.
|
|
|
+ Creates a body part from the stream and appends it to the multipart form data object.
|
|
|
|
|
|
The body part data will be encoded using the following format:
|
|
|
|
|
|
- `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
|
|
- `Content-Type: #{mimeType}` (HTTP Header)
|
|
|
- - Encoded file data
|
|
|
+ - Encoded stream data
|
|
|
- Multipart form boundary
|
|
|
|
|
|
- :param: data The data to encode into the multipart form data.
|
|
|
- :param: name The name to associate with the data in the `Content-Disposition` HTTP header.
|
|
|
- :param: fileName The filename to associate with the data in the `Content-Disposition` HTTP header.
|
|
|
- :param: mimeType The MIME type to associate with the data in the `Content-Type` HTTP header.
|
|
|
+ :param: stream The input stream to encode in the multipart form data.
|
|
|
+ :param: length The content length of the stream.
|
|
|
+ :param: name The name to associate with the stream content in the `Content-Disposition` HTTP header.
|
|
|
+ :param: fileName The filename to associate with the stream content in the `Content-Disposition` HTTP header.
|
|
|
+ :param: mimeType The MIME type to associate with the stream content in the `Content-Type` HTTP header.
|
|
|
*/
|
|
|
- public func appendBodyPart(fileData data: NSData, name: String, fileName: String, mimeType: String) {
|
|
|
+ public func appendBodyPart(#stream: NSInputStream, length: UInt64, name: String, fileName: String, mimeType: String) {
|
|
|
let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType)
|
|
|
- let bodyStream = NSInputStream(data: data)
|
|
|
- let bodyContentLength = UInt64(data.length)
|
|
|
- let bodyPart = BodyPart(headers: headers, bodyStream: bodyStream, bodyContentLength: bodyContentLength)
|
|
|
-
|
|
|
- self.bodyParts.append(bodyPart)
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- Creates a body part from the data and appends it to the multipart form data object.
|
|
|
-
|
|
|
- The body part data will be encoded using the following format:
|
|
|
-
|
|
|
- - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
|
|
|
- - Encoded file data
|
|
|
- - Multipart form boundary
|
|
|
-
|
|
|
- :param: data The data to encode into the multipart form data.
|
|
|
- :param: name The name to associate with the data in the `Content-Disposition` HTTP header.
|
|
|
- */
|
|
|
- public func appendBodyPart(#data: NSData, name: String) {
|
|
|
- let headers = contentHeaders(name: name)
|
|
|
- let bodyStream = NSInputStream(data: data)
|
|
|
- let bodyContentLength = UInt64(data.length)
|
|
|
- let bodyPart = BodyPart(headers: headers, bodyStream: bodyStream, bodyContentLength: bodyContentLength)
|
|
|
-
|
|
|
- self.bodyParts.append(bodyPart)
|
|
|
+ appendBodyPart(stream: stream, length: length, headers: headers)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Creates a body part from the stream and appends it to the multipart form data object.
|
|
|
+ Creates a body part with the headers, stream and length and appends it to the multipart form data object.
|
|
|
|
|
|
The body part data will be encoded using the following format:
|
|
|
|
|
|
- - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
|
|
- - `Content-Type: #{mimeType}` (HTTP Header)
|
|
|
- - Encoded file data
|
|
|
+ - HTTP headers
|
|
|
+ - Encoded stream data
|
|
|
- Multipart form boundary
|
|
|
|
|
|
- :param: stream The input stream to encode in the multipart form data.
|
|
|
- :param: name The name to associate with the stream content in the `Content-Disposition` HTTP header.
|
|
|
- :param: fileName The filename to associate with the stream content in the `Content-Disposition` HTTP header.
|
|
|
- :param: mimeType The MIME type to associate with the stream content in the `Content-Type` HTTP header.
|
|
|
+ :param: stream The input stream to encode in the multipart form data.
|
|
|
+ :param: length The content length of the stream.
|
|
|
+ :param: headers The HTTP headers for the body part.
|
|
|
*/
|
|
|
- public func appendBodyPart(#stream: NSInputStream, name: String, fileName: String, length: UInt64, mimeType: String) {
|
|
|
- let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType)
|
|
|
+ public func appendBodyPart(#stream: NSInputStream, length: UInt64, headers: [String: String]) {
|
|
|
let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
|
|
|
-
|
|
|
self.bodyParts.append(bodyPart)
|
|
|
}
|
|
|
|
|
|
- // MARK: - Data Extraction
|
|
|
+ // MARK: - Data Encoding
|
|
|
|
|
|
/**
|
|
|
Encodes all the appended body parts into a single `NSData` object.
|
|
|
@@ -299,6 +343,10 @@ public class MultipartFormData {
|
|
|
:returns: EncodingResult containing an `NSData` object if the encoding succeeded, an `NSError` otherwise.
|
|
|
*/
|
|
|
public func encode() -> EncodingResult {
|
|
|
+ if let bodyPartError = self.bodyPartError {
|
|
|
+ return .Failure(bodyPartError)
|
|
|
+ }
|
|
|
+
|
|
|
var encoded = NSMutableData()
|
|
|
|
|
|
self.bodyParts.first?.hasInitialBoundary = true
|
|
|
@@ -329,6 +377,11 @@ public class MultipartFormData {
|
|
|
:param: completionHandler A closure to be executed when writing is finished.
|
|
|
*/
|
|
|
public func writeEncodedDataToDisk(fileURL: NSURL, completionHandler: (NSError?) -> Void) {
|
|
|
+ if let bodyPartError = self.bodyPartError {
|
|
|
+ completionHandler(bodyPartError)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
var error: NSError?
|
|
|
|
|
|
if let path = fileURL.path where NSFileManager.defaultManager().fileExistsAtPath(path) {
|
|
|
@@ -598,6 +651,13 @@ public class MultipartFormData {
|
|
|
return ["Content-Disposition": "form-data; name=\"\(name)\""]
|
|
|
}
|
|
|
|
|
|
+ private func contentHeaders(#name: String, mimeType: String) -> [String: String] {
|
|
|
+ return [
|
|
|
+ "Content-Disposition": "form-data; name=\"\(name)\"",
|
|
|
+ "Content-Type": "\(mimeType)"
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
private func contentHeaders(#name: String, fileName: String, mimeType: String) -> [String: String] {
|
|
|
return [
|
|
|
"Content-Disposition": "form-data; name=\"\(name)\"; filename=\"\(fileName)\"",
|
|
|
@@ -621,6 +681,12 @@ public class MultipartFormData {
|
|
|
|
|
|
// MARK: - Private - Errors
|
|
|
|
|
|
+ private func setBodyPartError(error: NSError) {
|
|
|
+ if self.bodyPartError == nil {
|
|
|
+ self.bodyPartError = error
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private func errorWithCode(code: Int, failureReason: String) -> NSError {
|
|
|
let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
|
|
|
return NSError(domain: AlamofireErrorDomain, code: code, userInfo: userInfo)
|