2

I am trying to upload a file (in this case I'm trying with an image, but I would need to be able to upload any kind of file, especially a video file) to my server.

This is my PHP code, and it works fine on a server-side:

<?php include '_config.php';

if ($_FILES["file"]["error"] > 0) {
    echo "Error: " .$_FILES["file"]["error"]. "<br>";

} else {
    // Check file size
    if ($_FILES["file"]["size"] > 20485760) { // 20 MB
        echo "ERROR: Your file is larger than 20 MB. Please upload a smaller one.";    
    } else { uploadImage(); }

}// ./ If


// UPLOAD IMAGE ------------------------------------------
function uploadImage() {
    // generate a unique random string
    $randomStr = generateRandomString();
    $filePath = "uploads/".$randomStr;

    // upload image into the 'uploads' folder
    move_uploaded_file($_FILES['file']['tmp_name'], $filePath);

    // echo the link of the uploaded image
    echo $filePath;
}

// GENERATE A RANDOM STRING ---------------------------------------
function generateRandomString() {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i<20; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString."_".$_POST['fileName'];
}
?>

This is my Swift 5 function:

func uploadFile(_ aImage:UIImage, maxWidth:CGFloat, completion: @escaping (_ fileURL:String?) -> Void) {
        showHUD()
        let image = scaleImageToMaxWidth(image: aImage, newWidth: maxWidth)

        // Generate a random filename
        var filename = ""
        for _ in 0..<20 {
            let randomChar = Int(arc4random() % UInt32(charsForRand.count))
            filename += charsForRand[randomChar]
        }
        filename += "__image.jpg"
        print("FILENAME: \(filename)")

        let boundary = UUID().uuidString
        let fieldName = "reqtype"
        let fieldValue = "fileupload"
        let fieldName2 = "userhash"
        let fieldValue2 = "caa3dce4fcb36cfdf9258ad9c"

        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)
        var urlRequest = URLRequest(url: URL(string: DATABASE_PATH + "upload-file.php")!)
        urlRequest.httpMethod = "POST"
        urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        var data = Data()
        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
        data.append("Content-Disposition: form-data; name=\"\(fieldName)\"\r\n\r\n".data(using: .utf8)!)
        data.append("\(fieldValue)".data(using: .utf8)!)
        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
        data.append("Content-Disposition: form-data; name=\"\(fieldName2)\"\r\n\r\n".data(using: .utf8)!)
        data.append("\(fieldValue2)".data(using: .utf8)!)
        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
        data.append("Content-Disposition: form-data; name=\"fileToUpload\"; fileName=\"\(filename)\"\r\n".data(using: .utf8)!)
        data.append("Content-Type: image/png\r\n\r\n".data(using: .utf8)!)

        data.append(image.jpegData(compressionQuality: 1.0)!)

        data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

        // Send a POST request to the URL, with the data we created earlier
        session.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in
            if error != nil { print("\(error!.localizedDescription)") }

            guard let responseData = responseData else {
                DispatchQueue.main.async {
                    self.simpleAlert("Something went wrong while uploading, try again.")
                }
                completion(nil)
                return
            }

            if let response = String(data: responseData, encoding: .utf8) {
                completion("\(DATABASE_PATH)\(response)")
                print("UPLOAD URL: \(DATABASE_PATH)\(response)")
            }
        }).resume()
    }

I call that function in my ViewController as follows:

uploadFile(UIImage(named: "default_avatar")!, maxWidth: 300) { (fileURL) in
    if fileURL != nil {
         print("FILE URL: \(fileURL!)")
     }
}

But this is what I get in the Xcode console:

FILE URL: https://example.com/uploads/8iWQOrwr0wgNDor8XNhX_
UPLOAD URL: https://example.com/uploads/8iWQOrwr0wgNDor8XNhX_

This means that my function doesn't append "__image.jpg" string to the filename variable, and it also doesn't upload my image to the uploads folder on my server.

What am I doing wrong? If I call my PHP script from a form with an input of type file, it works like a charm., so my PHP script is fine, so I'm surely doing something wrong in the Swift function.

2
  • I'm trying to figure out this whole thing, and I'm hoping that your final answer will lead me to my final answer. Obviously the filename is adding the __image.jpg, as it prints out OK. Somewhere in the transfer does it delete it because it has an _ character? Just a thought, but try changing the __image.jpg to xximage.jpg and see if you get the same result. Commented Feb 11, 2020 at 3:44
  • Please check my answer, I could figure it out. Commented Feb 11, 2020 at 7:37

1 Answer 1

6

I've found a solution, here's my edited Swift 5 function, which now can also accept mp4 video files, not just jpg or png images:

func uploadFile(fileData:Data, fileName:String , completion: @escaping (_ fileURL:String?, _ error:String?) -> Void) {
        print("FILENAME: \(fileName)")

        let boundary: String = "------VohpleBoundary4QuqLuM1cE5lMwCy"
        let contentType: String = "multipart/form-data; boundary=\(boundary)"
        let request = NSMutableURLRequest()
        request.url = URL(string: DATABASE_PATH + "upload-file.php")
        request.httpShouldHandleCookies = false
        request.timeoutInterval = 60
        request.httpMethod = "POST"
        request.setValue(contentType, forHTTPHeaderField: "Content-Type")
        let body = NSMutableData()
        body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
        body.append("Content-Disposition: form-data; name=\"fileName\"\r\n\r\n".data(using: String.Encoding.utf8)!)
        body.append("\(fileName)\r\n".data(using: String.Encoding.utf8)!)

        body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
        body.append("Content-Disposition: form-data; name=\"file\"; filename=\"file\"\r\n".data(using: String.Encoding.utf8)!)

        // File is an image
        if fileName.hasSuffix(".jpg") {
            body.append("Content-Type:image/png\r\n\r\n".data(using: String.Encoding.utf8)!)
        // File is a video
        } else if fileName.hasSuffix(".mp4") {
            body.append("Content-Type:video/mp4\r\n\r\n".data(using: String.Encoding.utf8)!)
        }

        body.append(fileData)
        body.append("\r\n".data(using: String.Encoding.utf8)!)


        body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
        request.httpBody = body as Data
        let session = URLSession.shared
        let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
            guard let _:Data = data as Data?, let _:URLResponse = response, error == nil else {
                DispatchQueue.main.async { completion(nil, error!.localizedDescription) }
                return
            }
            if let response = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) {
                print("XSUploadFile -> RESPONSE: " + DATABASE_PATH + response)
                DispatchQueue.main.async { completion(DATABASE_PATH + response, nil) }

            // NO response
            } else { DispatchQueue.main.async { completion(nil, E_401) } }// ./ If response
        }; task.resume()
    }

Here's how I use that function:

let imageData = UIImage(named: "my_img")!.jpegData(compressionQuality: 1)
uploadFile(fileData: imageData!, fileName: "image.jpg") { (fileURL, e) in
   if e == nil {
       print("FILE URL: " + fileURL!)
}}

This works 100%.

Sign up to request clarification or add additional context in comments.

1 Comment

This is the perfect solution if you want to select a file from the "Files" and upload to a server using php. <3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.