MYSQL and Swift - Upload image and FILE || Would it be better to use Alamofire?
Asked Answered
A

2

4

I am trying to upload an image, and a text file(uploading it as Data).

So far I can upload the image alone correctly, and also upload the text file data uploading it as a .txt successfully alone.

Now I need to upload both image and .txt file together...

I am not sure how to set the Paramaters up in my IOS app for this....

So far this is how I upload the .txt file (basically the same way I upload the image but I change the "filename" and "mimetype")

func createBodyWithParameters(parameters: [String : Any]?, filePathKey: String?,filePathKey1: String?, imageDataKey: NSData,imageDataKey1: NSData, boundary: String) -> NSData {

        let body = NSMutableData();

        if parameters != nil {
            for (key, value) in parameters! {
                body.appendString("--\(boundary)\r\n")
                body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
                body.appendString("\(value)\r\n")
            }
        }

        let filename = "post-\(uuid).txt"
        let mimetype = "image/txt"

        body.appendString("--\(boundary)\r\n")
        body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
        body.appendString("Content-Type: \(mimetype)\r\n\r\n")


        body.append(imageDataKey as Data)

        body.appendString("\r\n")
        body.appendString("--\(boundary)--\r\n")

        return body
    }

Now I am not sure how to save both image and .txt file with that paramater.

This however is the rest of my swift code for uploading it:

 let param = [
            "id" : id,
            "uuid" : uuid,
            "Text" : Text,
            "Title" : Title
           ] as [String : Any]

        let boundary = "Boundary-\(NSUUID().uuidString)"
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        let data: Data = NSKeyedArchiver.archivedData(withRootObject: blogattributedText)


        var imageData = NSData()
        let image = CoverImage
        let width = CGSize(width: self.view.frame.width, height: image.size.height * (self.view.frame.width / image.size.width))
        imageData = UIImageJPEGRepresentation(imageWithImage(image, scaledToSize: width), 0.5)! as NSData

   // ... body
    request.httpBody = createBodyWithParameters(parameters: param, filePathKey: "file",filePathKey1: "file1", imageDataKey: data as NSData,imageDataKey1: imageData as NSData, boundary: boundary) as Data

If anyone needs to see anymore of my code or doesn't understand my question please let me know!

Thanks in advance to anyone that can help!!

Aeniah answered 10/11, 2016 at 12:2 Comment(4)
Please provide SHOW CREATE TABLE.Superorder
This has nothing to do with PHP. Please fix your tags.Bodine
@Bodine done, sorry.Aeniah
@RickJames I can do but there's no problems with the tables, just with the parameters in swift for uploading the 2 files. I know how to handle the php and mysql side, just need to figure out the correct parameters for upload a **text document++ and ++image++Aeniah
V
1

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.

Volost answered 21/11, 2016 at 1:1 Comment(4)
Wow this deffienetly looks promising I shall give it a good ASAP..! Thank you.Aeniah
Fantastic, can't thank you enough!! I have decided to do it normaly without Alamofire as that's how I've been doing it and I would have to change alot of code. But if it does have several adavantages I think I might, what do you think?Aeniah
Best one Rob. Thanks.Brannon
@Jack - If you'll have to refactor a lot of code to integrate Alamofire, then perhaps you don't worry about it for now. But next time, consider Alamofire, as it's a pretty robust library and could save you time in the future.Volost
B
0

Alternatively, you can make JSON rest API's.

  • Convert your notepad file content in Base64 encoded string.
  • Convert your image file in Base64 encoded string.
  • Pass both Base64 encoded string in request parameters.
  • Once you get Base64 encoded string on API file in function parameter.
  • Again decode Base64 string and store it in content folder.

https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking

upload image using AFNetworking in swift ios

Uploading image with AFNetworking 2.0

Note - Show image on iOS app, you can make absolute URL and using image caching class you can display image.

Brannon answered 18/11, 2016 at 10:31 Comment(7)
Hi again! so upload both files as Base64 string and then in the php before uploading to the server convert them to normal files and store them with a link to the content folder? and then getting them back to the IOS app I just do the normal procedure downloading them from the link? upvoting for showing interest in the question though :)Aeniah
I guess you question code is like HTTP post on server location, right ?Brannon
yeh, if it makes it easier I can add all the post code from my app and php code?Aeniah
Better to swift your code on AFNetworking in swift and change way to uploading media material as Base64.Brannon
Hmm I'll probably change all my code to work with AFnetworking if I can't get anything done by tonight...But there must be a way with swift using normal NSurlsessionAeniah
First, using base64-encoded JSON rather than multipart makes the request 33% larger. I'm not sure I'd recommend that, but you certainly can. Second, if you're going to use a third-party utility (which is a good idea when doing something complicated), I'd suggest Alamofire rather than AFNetworking. They are both from the same author, and I'd suggest staying within Swift, rather than introducing an Objective-C library into the mix.Volost
@Volost - Good suggestion Alamofire.Brannon

© 2022 - 2024 — McMap. All rights reserved.