Uploading multiple image files with Swift
Asked Answered
C

3

1

Im using the following code to upload a single image to a server (with parameters), However I've been trying to upload multiple images, but could not get it to work

Creating NSData objects (of images saved in temp directory). Using netdata class here

    func uploadData(){
    //create image data objects
    let filemanager:NSFileManager = NSFileManager()
    let files = filemanager.enumeratorAtPath(tempPicPath)
    var counter:Int = Int()
    while let file: AnyObject = files?.nextObject() {
        imagePathCollection.append(tempPicPath + "/" + (file as NSString))
    }

    //convert to data objects ()
    for path in imagePathCollection{
        var image: UIImage? = UIImage(contentsOfFile: path)
        if image != nil {
            var sizeOfImage = image?.size
            var resizedImage = RBSquareImageTo(image!, sizeOfImage!)
            var imageData = UIImageJPEGRepresentation(resizedImage, 75)
            imageDataCollection.append(NetData(data: imageData!, mimeType:MimeType(rawValue: "image/jpeg")!, filename: "myImage.jpeg"))
        }
    }

    if(imageDataCollection.count > 1){
        for (index, element) in enumerate(imageDataCollection) {
            multipleImageUpload(imageDataCollection[index])
        }
    }else{
        singleImageUpload(imageDataCollection[0])
    }
}

Using Alamofire to upload

        func extrasImageUpoload(urlRequest:(URLRequestConvertible, NSData))->Request{
    let request = Alamofire.upload(urlRequest.0, urlRequest.1)
        .progress { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
            println("progress : \(totalBytesWritten) / \(totalBytesExpectedToWrite)")
    }
    return request
}

Generating the Alamofire request

    func urlRequestWithComponents(parameters:NSDictionary, uploadType:String) -> (URLRequestConvertible, NSData) {

    var mutableURLRequest:NSMutableURLRequest = NSMutableURLRequest()

    //set url type
    if(uploadType == "extra"){
        mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: baseURL + "ticket/extra")!)
    }else if(uploadType == "checkList"){
        //uploadUrl = "ticket/mark"
        mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: baseURL + "ticket/mark")!)
    }

    // create url request to send
    //var mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: baseURL + "ticket/extra")!)
    mutableURLRequest.HTTPMethod = Alamofire.Method.POST.rawValue
    //let boundaryConstant = "myRandomBoundary12345"
    let boundaryConstant = "NET-POST-boundary-\(arc4random())-\(arc4random())"
    let contentType = "multipart/form-data;boundary="+boundaryConstant
    //let pgToken = "c9049df83e8bfd7a3dfaef279cdb74478330ff2a"
    mutableURLRequest.setValue(contentType, forHTTPHeaderField: "Content-Type")
    mutableURLRequest.setValue(self.token, forHTTPHeaderField: "PG-Auth-Token")

    // create upload data to send
    let uploadData = NSMutableData()

    // add parameters
    for (key, value) in parameters {

        uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

        if value is NetData {
            // add image
            var postData = value as NetData
            //uploadData.appendData("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(postData.filename)\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!
            var filenameClause = " filename=\"\(postData.filename)\""
            let contentDispositionString = "Content-Disposition: form-data; name=\"\(key)\";\(filenameClause)\r\n"
            let contentDispositionData = contentDispositionString.dataUsingEncoding(NSUTF8StringEncoding)
            uploadData.appendData(contentDispositionData!)


            // append content type
            //uploadData.appendData("Content-Type: image/png\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
            let contentTypeString = "Content-Type: \(postData.mimeType.getString())\r\n\r\n"
            let contentTypeData = contentTypeString.dataUsingEncoding(NSUTF8StringEncoding)
            uploadData.appendData(contentTypeData!)
            uploadData.appendData(postData.data)

        }else{
            uploadData.appendData("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n\(value)".dataUsingEncoding(NSUTF8StringEncoding)!)
        }
    }
    uploadData.appendData("\r\n--\(boundaryConstant)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    // return URLRequestConvertible and NSData
    return (Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: nil).0, uploadData)
}

The parameters array along with the NSData collection as below

 var params = ["pic":imageData,"ticketid":ticketID, "appversion":Utilities().getAPPVersion(), "description":"bla bla bla", "cost":50]

"imageData" is a collection of NSData objects of the images. Code works fine but the NSData array of the images received as an empty array. However rest of the parameters including "appversion" and "description" will be received fine

Chipboard answered 11/2, 2015 at 7:32 Comment(0)
E
3
import MobileCoreServices


func createRequest(userid: String, image: [UIImage]) throws -> NSMutableURLRequest {

   var parameters: [String : AnyObject] = [
       "userid": userid,
       "Image1": image[0],  // You need to set the UIImage Type image parameters 
       "Image2": image[1],   
       "Image3": image[2],
       "Image4": image[3]
   ]

    print(parameters)

    let boundary = generateBoundaryString()

    let url = URL(string: Your URL String)!
    let request = NSMutableURLRequest(url: url)
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

    request.httpBody = try createBody(with: parameters as [String : AnyObject], boundary: boundary)

    return request
}

Here is Code of Upload Image and Parameters in multipart/form-data

func createBody(with parameters: [String: AnyObject], boundary: String) throws -> Data {

    var body = Data()

    for (key, value) in parameters {

        if(value is String || value is NSString) {
            body.append("--\(boundary)\r\n")
            body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
            body.append("\(value)\r\n")
        }else if let image = value as? UIImage {
            let r = arc4random()
            let filename = "image\(r).jpg"
            let data = UIImageJPEGRepresentation(image,1);
            let mimetype = mimeTypeForPath(path: filename)

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

    }

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

    return body

}

func mimeTypeForPath(path: String) -> String {
    let pathExtension = path.pathExtension
    var stringMimeType = "application/octet-stream";
    if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as! CFString, nil)?.takeRetainedValue() {
        if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
            stringMimeType = mimetype as NSString as String
        }
    }
    return stringMimeType;
}

extension String {
   var ns: NSString {
       return self as NSString
   }
   var pathExtension: String? {
       return ns.pathExtension
   }
   var lastPathComponent: String? {
       return ns.lastPathComponent
   }
}
Eckart answered 27/3, 2017 at 7:23 Comment(0)
R
1

The only thing that looks wrong to me is that you're not creating the initial boundary properly. You should NOT have a CRLF at the beginning of the first boundary. I would try the following instead.

let uploadData = NSMutableData()

for (index, (key, value)) in enumerate(parameters) {
    if index == 0 {
        uploadData.appendData("--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    } else {
        uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    }

    if let postData as? NetData {
        var filenameClause = " filename=\"\(postData.filename)\""
        let contentDispositionString = "Content-Disposition: form-data; name=\"\(key)\";\(filenameClause)\r\n"
        let contentDispositionData = contentDispositionString.dataUsingEncoding(NSUTF8StringEncoding)
        uploadData.appendData(contentDispositionData!)

        let contentTypeString = "Content-Type: \(postData.mimeType.getString())\r\n\r\n"
        let contentTypeData = contentTypeString.dataUsingEncoding(NSUTF8StringEncoding)
        uploadData.appendData(contentTypeData!)
        uploadData.appendData(postData.data)
    }
}

uploadData.appendData("\r\n--\(boundaryConstant)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
Rattly answered 12/2, 2015 at 6:32 Comment(5)
i am getting this when i try to do the multi-upload "Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates." ".NetData' does not implement methodSignatureForSelector: -- trouble ahead"Chipboard
Your snapshotting error is a very different problem that has nothing to do with the changes I suggested. I would suggest filing a different question and looking into the drawViewHierarchyInRect: method. As for the .NetData problem, you haven't posted enough information to go on. Again, I don't think that's an issue with the code I posted.Rattly
Hi @Yrol, did you end up figuring this out? If my answer helped you along the way, could you mark it as such to be a good community user? Cheers.Rattly
@Rattly Can you please check this question of mine - https://mcmap.net/q/752158/-upload-array-having-base64string-encoded-multiple-images-using-alamofire/5395919Chauffeur
Hi @Rattly sorry to bother you, but I find your answer might be able to help my question. I understand if you're not interested but if you could spare sometime to check if out I would be very grateful. #40527640 Plus there's 50 rep bounty ;)Demanding
S
0

In Swift Using Alamofire Just append "[]" with image upload Param Name to make it array of images. Following is the demo code where I am using "[]" with images upload's param name and creating a loop to same param name. ;-)

func uploadImages(withImagesData imagesData: [Foundation.Data], parameters: [String: String]) {
    Alamofire.upload(multipartFormData: { multipartFormData in
                // import image to request
                for imageData in imagesData {
                    multipartFormData.append(imageData, withName: "myImageParamName[]", 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
                            print("upload Success")
                        }
                    case .failure(let error):
                        print("upload Failed With Error: ", error)
                    }

            })
}
Sentence answered 3/8, 2018 at 12:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.