1

I have an iOS App which should upload image data to my php server on Nginx with a POST request. I can upload the data but when I download it after, the uploaded file is corrupted and smaller and I can't open it.

I have changed the values for post_max_size, max_input_vars and upload_max_filesize high enough and have all permissions set correctly.

Here is my code:

App:

DispatchQueue.global().async {
    let url = "url to upload.php"
    
    var urlComponents = URLComponents(string: url!)
    urlComponents.queryItems = [
        URLQueryItem(name: "filetype", value: "img")
    ]

    var request = URLRequest(url: urlComponents.url!)
    request.httpMethod = "POST"

    let boundary = UUID().uuidString
    let contentType = "multipart/form-data; boundary=\(boundary)"

    var body = Data()

    body.append("--\(boundary)\r\n".data(using: .utf8)!)
    body.append("Content-Disposition: form-data; name=\"username\"\r\n\r\n".data(using: .utf8)!)
    body.append("\(username)\r\n".data(using: .utf8)!)
            
    body.append("--\(boundary)\r\n".data(using: .utf8)!)
    body.append("Content-Disposition: form-data; name=\"image-upload\"; filename=\"img.jpg\"".data(using: .utf8)!)
    body.append("Content-Type: image/jpeg".data(using: .utf8)!)
    body.append(imageData) // 2735963 bytes

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

    request.setValue(contentType, forHTTPHeaderField: "Content-Type")

    request.httpBody = body

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
                
            if error != nil {
                print(error!.localizedDescription)
                return
            }
                
                
            let responseString = String(data: data!, encoding: .utf8)
            print(responseString!) // final data stored on server: 2734537 bytes

                
    }
    task.resume()
}

PHP:

<?php

$user = $_POST['username'];///I need this for the path
$username = str_replace(array("\r", "\n", "'"), '', $user);
$type = $_GET['filetype'];

if ($type == "img")
{
    $path = "path to save img.jpg";
    $image = $_FILES['image-upload']['tmp_name'];

    if (move_uploaded_file($image, $path)) {
        echo "Success";
    }
    else
    {
        echo "Failed";
    }
}

?>

Is anything wrong with it?

  • Open the image in a text edit and inspect the contents. My guess is that it is an error message. Do you have PHP error reporting turned on on your server? – Chris Haas Jan 24 '22 at 13:09
  • @ChrisHaas The text file of the image is just wired looking text elements, no error message and the PHP error reporting doesn't show any errors. The file size decreases only about 500 to 1000 KB. – koproductions Jan 24 '22 at 13:34
  • @Rob I compare it to the number of bytes of `imageData`. I prepare the Data using UIImage(named: "")!.pngData()!. The number of bytes of this data is for example `2735963` bytes and on the server it is only `2734537` bytes. And yes it is reporting Success. I also tried not setting `Content-Length` but it doesn't change anything. – koproductions Jan 24 '22 at 15:43
  • Oh sorry, no I am creating the jpeg Data with `UIImage(named: "")!.jpeg(compressionQuality: 1.0)!"` – koproductions Jan 24 '22 at 16:28
  • 1
    @Rob Thank you, the code in the article you linked worked for me! – koproductions Jan 24 '22 at 17:46

1 Answers1

0

You’re missing the \r\n\r\n after Content-Type: image/jpeg.

E.g.:

body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)

And you probably need it after the final boundary, too:

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

Also, we generally do not set Content-Length. Let URLSession do that for you.

See https://stackoverflow.com/a/26163136/1271826 or consider using a library like Alamofire which gets you out of the weeds of preparing a multipart request.

Rob
  • 415,655
  • 72
  • 787
  • 1,044