Upload Pdf, Docx and image file to server using Swift 4

25,681

Solution 1

One fundamental mistake is that you are using dataTask instead of an uploadTask on your URLSession instance, e.g. uploadTask(with:from:completionHandler:)

Construction of Body Data

Here's a generic example from my own code (as requested in the comments below) of how body data might be constructed:

// imagesURLS is an optional array of URLs, i.e. imageURLS:[URL]?

if let imgURLs = imagesURLS {
    for f in imgURLs {
        let filename = f.lastPathComponent  
        let splitName = filename.split(separator: ".")
        let name = String(describing: splitName.first)
        let filetype = String(describing: splitName.last)

        let imgBoundary = "\r\n--\(boundary)\r\nContent-Type: image/\(filetype)\r\nContent-Disposition: form-data; filename=\(filename); name=\(name)\r\n\r\n"

        if let d = imgBoundary.data(using: .utf8) {
            bodyData.append(d)
        }

        do {
            let imgData = try Data(contentsOf:f, options:[])
            bodyData.append(imgData)
        }
        catch {
            // can't load image data
        }

        }
    }
    let closingBoundary = "\r\n--\(boundary)--"
    if let d = closingBoundary.data(using: .utf8) {
            bodyData.append(d)
    }

The loop means that every item of data (in this case an image) is preceded by a boundary string and after the very last item of data the closing boundary string is added (i.e. the one ending in a double hyphen).

Solution 2

This works for me, in Swift4:

func uploadFiles(_ urlPath: [URL]){

if let url = URL(string: "YourURL"){
var request = URLRequest(url: url)
let boundary:String = "Boundary-\(UUID().uuidString)"

request.httpMethod = "POST"
request.timeoutInterval = 10
request.allHTTPHeaderFields = ["Content-Type": "multipart/form-data; boundary=----\(boundary)"]

    for path in urlPath{
        do{
            var data2: Data = Data()
            var data: Data = Data()
            data2 = try NSData.init(contentsOf: URL.init(fileURLWithPath: path.absoluteString, isDirectory: true)) as Data
            /* Use this if you have to send a JSON too.
             let dic:[String:Any] = [
             "Key":Value,
             "Key":Value
            ]


            for (key,value) in dic{
                data.append("------\(boundary)\r\n")
                data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
                data.append("\(value)\r\n")
            }
              */
            data.append("------\(boundary)\r\n")
            //Here you have to change the Content-Type
            data.append("Content-Disposition: form-data; name=\"file\"; filename=\"YourFileName\"\r\n")
            data.append("Content-Type: application/YourType\r\n\r\n")
            data.append(data2)
            data.append("\r\n")
            data.append("------\(boundary)--")

            request.httpBody = data
        }catch let e{
            //Your errors
        }
        DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).sync {
            let session = URLSession.shared
            let task = session.dataTask(with: request, completionHandler: { (dataS, aResponse, error) in
                if let erros = error{
                    //Your errors
                }else{
                    do{
                        let responseObj = try JSONSerialization.jsonObject(with: dataS!, options: JSONSerialization.ReadingOptions(rawValue:0)) as! [String:Any]

                    }catch let e{

                    }
                }
            }).resume()
        }
    }
}
}

extension Data{
mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
    if let data = string.data(using: encoding) {
        append(data)
    }
}
}
Share:
25,681
TheSwiftGuy77
Author by

TheSwiftGuy77

Loading...

Updated on April 11, 2020

Comments

  • TheSwiftGuy77
    TheSwiftGuy77 about 4 years

    I am new to swift, I have been trying to upload pdf, docx and image file from local storage of a Iphone. I have written a code but it doesn't work and I keep getting Status Code: 415 from the response. Here is my code:

    func uploadfileToServer(completed: @escaping () -> ()){
    
        let theTitle = labelTitle.text
    
    
        guard let url = URL(string: "http://www.--------.com/assignment/post") else {return}
        var request = URLRequest.init(url: url)
        request.httpMethod = "POST"
    
        request.addValue("cf7ab8c9d4efae82b575eabd6bec76cbb86c6108391e036387f3dd5356a582171519367747000", forHTTPHeaderField: "api_key")
    
    
    
    
        let boundary = generateBoundaryString()
    
    
        // Set Content-Type in HTTP header.
        let boundaryConstant = boundary // This should be auto-generated.
        let contentType = "multipart/form-data; boundary=" + boundaryConstant
    
        let directory = NSTemporaryDirectory()
        let fileName = NSUUID().uuidString
    
        // This returns a URL? even though it is an NSURL class method
        let fullURL = NSURL.fileURL(withPathComponents: [directory, fileName])
    
    
    
        let fileNamee = fullURL?.path
        let mimeType = "text/csv"
        let fieldName = "uploadFile"
    
        request.setValue(contentType, forHTTPHeaderField: "Content-Type")
    
    
    
        var dataString = "--\(boundaryConstant)\r\n"
    
    
        dataString += "\r\n"
        dataString += "--\(boundaryConstant)--\r\n"
    
        var theBody = Data()
    
    
    
        let sectionID : String?
        sectionID = nil
        let str = "user_id=\(savedsesuid!)" + "&school_id=" + SCHOOL_ID + "&class_id=" + classID + "&section_id=\(sectionID)" + "&subject_id=\(id)"
    
    
    
        if let b = str.data(using: .utf8) {
            theBody.append(b)
        }
    
        let str1 = "&atitle=" + theTitle! + "&class_id=" + classID
    
        if let c = str1.data(using: .utf8){
            theBody.append(c)
        }
    
    
        let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        var filePath = documentDirectory.appendingFormat("/")
        filePath     = filePath.appendingFormat("/Users/prashanna/Desktop/ebusiness_topic_1_handout.pdf")
        let pdfData  = NSData(contentsOfFile: filePath)
    
        let file = "&afile=" + "\(pdfData)"
    
        if let d = file.data(using: .utf8){
            theBody.append(d)
        }
    
        print(theBody)
    
    
    
        request.httpBody = theBody
    
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            print(response)
            if let httpResponse = response as? HTTPURLResponse {
                let statuscode = httpResponse.statusCode
                if statuscode == 401{
                    self.displayMessage(userMessage: "Sending Failed")
    
    
                }else if statuscode == 200{
                    if error == nil{
                        do{
                            self.displayMessage(userMessage: "File Successfully Uploaded!")
                            DispatchQueue.main.async {
                                completed()
    
                            }
    
                        }
    
                    }
                }
            }
        }.resume()
    
    
    
    }
    
    func generateBoundaryString() -> String {
        return "Boundary-\(NSUUID().uuidString)"
    }
    

    Some solutions tell me to convert the file into Data and then send it to server while some say to directly add the file path to your body. Need Help!

  • TheSwiftGuy77
    TheSwiftGuy77 about 6 years
    Sorry dude i dont use Alamofire!
  • sketchyTech
    sketchyTech about 6 years
    Your use of boundary string doesn't look right either, you have a dataString instance that you concatenate but then don't use, when the actual data should be between the opening and closing boundary strings. And if you do have multiple items then a boundary string needs to open each of these before you terminate the body data with the closing boundary string. Aside from that it's a case of adhering the server's expectations.
  • sketchyTech
    sketchyTech about 6 years
    I've added a generic example from my own code, because without knowing the requirements of your server it would take me a long time to unpack what you need. Hope this helps.
  • TheSwiftGuy77
    TheSwiftGuy77 about 6 years
    how do i get the imagesURLS?
  • sketchyTech
    sketchyTech about 6 years
    I placed a comment at the beginning of the code to explain what imageURLS was, the reason being that this goes further back into irrelevant code where the user selects a folder and the code constructs a list of image urls in that folder. You'll need to adapt the code for your own needs, using the data you have and the boundary requirements of your server.
  • TheSwiftGuy77
    TheSwiftGuy77 about 6 years