MYSQL and Swift - Upload image and FILE || Would it be better to use Alamofire?
If you don't want to get lost in the weeds of creating complex requests, a third party library like Alamofire would be smart. It's by the same author as AFNetworking, but it's a native Swift library.
So, an Alamofire implementation might look like:
func performRequest(urlString: String, id: String, uuid: String, text: String, title: String, blogAttributedText: NSAttributedString, image: UIImage) {
let parameters = [
"id" : id,
"uuid" : uuid,
"Text" : text,
"Title" : title
]
let imageData = UIImageJPEGRepresentation(image, 0.5)!
let blogData = NSKeyedArchiver.archivedData(withRootObject: blogAttributedText)
Alamofire.upload(
multipartFormData: { multipartFormData in
for (key, value) in parameters {
if let data = value.data(using: .utf8) {
multipartFormData.append(data, withName: key)
}
}
multipartFormData.append(imageData, withName: "image", fileName: "image.jpg", mimeType: "image/jpeg")
multipartFormData.append(blogData, withName: "blog", fileName: "blog.archive", mimeType: "application/octet-stream")
},
to: urlString,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload
.validate()
.responseJSON { response in
switch response.result {
case .success(let value):
print("responseObject: \(value)")
case .failure(let responseError):
print("responseError: \(responseError)")
}
}
case .failure(let encodingError):
print("encodingError: \(encodingError)")
}
})
}
If you're going to build this request yourself, I'd suggest a few things. First, since you're sending files of different types, you might want some nice type to encapsulate this:
struct FilePayload {
let fieldname: String
let filename: String
let mimetype: String
let payload: Data
}
I'm also not sure what to make of the image/txt
mime type. I'd probably use application/octet-stream
for the archive.
Anyway, the building of the request could be as follows:
/// Create request.
///
/// - Parameters:
/// - url: The URL to where the post will be sent.
/// - id: The identifier of the entry
/// - uuid: The UUID of the entry
/// - text: The text.
/// - title: The title.
/// - blogAttributedText: The attributed text of the blog entry.
/// - image: The `UIImage` of the image to be included.
///
/// - Returns: The `URLRequest` that was created
func createRequest(url: URL, id: String, uuid: String, text: String, title: String, blogAttributedText: NSAttributedString, image: UIImage) -> URLRequest {
let parameters = [
"id" : id,
"uuid" : uuid,
"Text" : text, // I find it curious to see uppercase field names (I'd use lowercase for consistency's sake, but use whatever your PHP is looking for)
"Title" : title
]
let boundary = "Boundary-\(NSUUID().uuidString)"
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept") // adjust if your response is not JSON
// use whatever field name your PHP is looking for the image; I used `image`
let imageData = UIImageJPEGRepresentation(image, 0.5)!
let imagePayload = FilePayload(fieldname: "image", filename: "image.jpg", mimetype: "image/jpeg", payload: imageData)
// again, use whatever field name your PHP is looking for the image; I used `blog`
let blogData = NSKeyedArchiver.archivedData(withRootObject: blogAttributedText)
let blogPayload = FilePayload(fieldname: "blog", filename: "blog.archive", mimetype: "application/octet-stream", payload: blogData)
request.httpBody = createBody(with: parameters, files: [imagePayload, blogPayload], boundary: boundary)
return request
}
/// Create body of the multipart/form-data request.
///
/// - Parameters:
/// - parameters: The optional dictionary containing keys and values to be passed to web service.
/// - files: The list of files to be included in the request.
/// - boundary: The `multipart/form-data` boundary
///
/// - Returns: The `Data` of the body of the request.
private func createBody(with parameters: [String: String]?, files: [FilePayload], boundary: String) -> Data {
var body = Data()
if parameters != nil {
for (key, value) in parameters! {
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.append("\(value)\r\n")
}
}
for file in files {
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(file.fieldname)\"; filename=\"\(file.filename)\"\r\n")
body.append("Content-Type: \(file.mimetype)\r\n\r\n")
body.append(file.payload)
body.append("\r\n")
}
body.append("--\(boundary)--\r\n")
return body
}
/// Create boundary string for multipart/form-data request
///
/// - returns: The boundary string that consists of "Boundary-" followed by a UUID string.
private func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().uuidString)"
}
Where
extension Data {
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to `Data`, and then add that data to the `Data`, this wraps it in a nice convenient little `Data` extension. This converts using UTF-8.
///
/// - parameter string: The string to be added to the mutable `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
append(data)
}
}
}
Clearly this was Swift 3 code, so I excised the NSMutableData
reference.
Swift 3 Alamofire 4 Upload array of images with parameters
So the error was from the server side, that had a very small size limit for images and that's why they weren't saving. I updated the compression for the JPEG images from
if let imageData = UIImageJPEGRepresentation(image, 1)
to
if let imageData = UIImageJPEGRepresentation(image, 0.6)
and now it's working fine.
Thank you @Sneak for your links.
Upload multiple images in swift using Alamofire
Swift 3
Just use "[]" with image upload param to make it array of images.
Alamofire.upload(multipartFormData: { multipartFormData in
// import image to request
for imageData in imagesData {
multipartFormData.append(imageData, withName: "\(imageParamName)[]", fileName: "\(Date().timeIntervalSince1970).jpeg", mimeType: "image/jpeg")
}
for (key, value) in parameters {
multipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key)
}
}, to: urlString,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
}
case .failure(let error):
print(error)
}
})
Alamofire: Upload Jpg to a webservice in OSX
Alamofire doesn't care at all what the bytes in the data parameter are -- it uploads them as is.
You could just as easily do something like: https://stackoverflow.com/a/17504245/3937
Note: this is not preserving the original format. An NSImage is uncompressed image data. And ... JPEG is lossy, so this will not be exactly what the image data is.
If you had an original JPEG (let's say in a file), you could create the NSData from that file instead and never create an NSImage object.
Alamorefire(Swift 3) : Ambiguous reference to member 'upload(..'
Try below code
Alamofire.upload(multipartFormData: { (multipartFormData) in
multipartFormData.append(UIImageJPEGRepresentation(self.photoImageView.image!, 0.5)!, withName: "photo_path", fileName: "swift_file.jpeg", mimeType: "image/jpeg")
for (key, value) in parameters {
multipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key)
}
}, to:"http://server1/upload_img.php")
{ (result) in
switch result {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (Progress) in
print("Upload Progress: \(Progress.fractionCompleted)")
})
upload.responseJSON { response in
//self.delegate?.showSuccessAlert()
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
// self.showSuccesAlert()
//self.removeImage("frame", fileExtension: "txt")
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
case .failure(let encodingError):
//self.delegate?.showFailAlert()
print(encodingError)
}
}
Related Topics
Disable Scroll on a Uiwebview Allowed
How to Remove All Navigationbar Back Button Title
How to Add Private Key to the Distribution Certificate
Using iOS Gamekit's "Bluetooth Bonjour" with Other Platforms
Why Does Uiviewcontroller Extend Under Uinavigationbar, While Uitableviewcontroller Doesn'T
Bringing a Subview to Be in Front of All Other Views
How to Get the Size of a Scaled Uiimage in Uiimageview
Set Background Gradient on Button in Swift
Xcode 8 Recommend Me to Change the Min iOS Deployment Target from 7.1 to 8.0
How to Create .Ipa File for Testing Using Runner.App
Apply Gradient Color to Arc Created with Uibezierpath
Uiscrollview in Storyboard Not Working with iOS 8 Size Classes and Autolayout
To Convert Image to Cartoon in iOS
Get Latitude/Longitude from Address
Module Was Not Compiled for Testing' When Using @Testable
iOS 8.1 Simulator Localization Broken (Nslocalizedstring)
Assertion Failure When Using Uisearchdisplaycontroller in Uitableviewcontroller