Al Ras Al Ras - 20 days ago 14
iOS Question

Upload multipart image to PHP server from iOS app

I'm trying to upload an image with my iOS app (Swift 2.0). The server gets the request and sends status 200, but when it comes to the file upload the image-data doesn't get uploaded. The

print_r($_FILES);
command gives me response:

Array
(
)
wrong format for image


This is the code:

func send()
{

let request = NSMutableURLRequest(URL: NSURL(string: "***URL to upload.php***")!)

request.HTTPMethod = "POST"
let postString = "username=\(globalUsr)&photo-name=\(globalImage.description)&photo-description=\(message.text)"

let myData : NSData! = postString.dataUsingEncoding(NSUTF8StringEncoding)

let imageData :NSData = UIImageJPEGRepresentation(globalImage, 1.0)!;

let boundary = "-----SwiftBoundary-----"
let contentType = "multipart/form-data; boundary=\(boundary)"
let body = NSMutableData();

let tempData = NSMutableData()
let fileName = "\(globalImage.description).jpg"
let parameterName = "contest-photo"

tempData.appendData(myData)
tempData.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
let fileNameContentDisposition = "name=\(parameterName)"
let contentDisposition = "Content-Disposition: form-data; name=\"\(fileName)\"; \(fileNameContentDisposition)\r\n"
tempData.appendData(contentDisposition.dataUsingEncoding(NSUTF8StringEncoding)!)
tempData.appendData("Content-Type: \(contentType)\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
tempData.appendData(imageData)
tempData.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)


body.appendData(tempData)

body.appendData("\r\n--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

request.setValue("\(body.length)", forHTTPHeaderField: "Content-Length")
request.HTTPBody = body


let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in

if error != nil {
print("error=\(error)")
return
}

let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString!)")
}
task.resume()


}


The PHP code is:

<?php

// Load Wordpress
define('WP_USE_THEMES', false);
require_once("../../../../../wp-load.php");

$username = $_POST["username"];

$m = contest_upload_photo('contest-upload-photo','contest_upload_photo',username_exists($username ));




function contest_upload_photo($atts, $content = null,$user_ID=null) {

//Important variables
if ($user_ID == null){
die(json_encode("There is no user."));
}

$html = '';//Inciate output string
$koncovky = array('jpg', 'jpeg', 'png', 'gif');

$number_images = get_user_meta($user_ID, 'contest_user_images', true);
if(empty($number_images)){$number_images=0;}


$error = array();
// Do some minor form validation to make sure there is content

$name = trim($_POST['photo-name']);
/*if (empty($_POST['photo-title'])){
$error['title'] = __('Please enter the photo title','photo-contest');
} else {
$title = trim($_POST['photo-title']);
}*/


//Check photo
if ($_FILES['contest-photo']['error'] == UPLOAD_ERR_NO_FILE){
$error['photo'] = __('Please select the image','photo-contest');
die("no photo selected");
} else {

//Control upload and extension
if ($_FILES['contest-photo']['error']) {
$error['upload_error'] = __('Error image upload.','photo-contest');
die("Image upload error");
}
elseif (!in_array(strtolower(pathinfo($_FILES['contest-photo']['name'], PATHINFO_EXTENSION)), $koncovky)) {
$error['extension_error'] = __('Image must be jpg, png or gif.','photo-contest');
print($koncovky);
print_r($_FILES);
die("wrong format for image");
}
elseif (!($imagesize = getimagesize($_FILES['contest-photo']['tmp_name'])) || $imagesize[2] > 3) {
$error['type_error'] = __('Image type must be jpg, png or gif.','photo-contest');
die("image too big");
}
else {

@$img=getimagesize($_FILES['contest-photo']['tmp_name']);

$minimum = array('width' => '400', 'height' => '400');
$width= $img[0];
$height =$img[1];
if ($width < $minimum['width'] ){
$error['type_error'] = __('Minimum image width is 400px.','photo-contest');
die("width is too small");
}
elseif ($height < $minimum['height']){
$error['type_error'] = __('Minimum image height is 400px.','photo-contest');
die("height is too small");
}
$photo_limit = get_option( 'pcplugin-photo-limit', true );
$size_maxi = $photo_limit;
$size = filesize($_FILES['contest-photo']['tmp_name']);
if($size>$size_maxi){
$error['size_error'] = __('File size is above allowed limitations!','photo-contest');
die("file is too big");
}
}


}


if(empty($error)){
//If no exist error - create attachment post
if(empty($_POST['photo-description'])){
$description = sanitize_text_field($_POST['photo-description']);
}else{
$description = '';
}

@$wp_filetype = wp_check_filetype(basename($_FILES['contest-photo']['name']), null );
@$wp_upload_dir = wp_upload_dir();
$attachment = array(
'guid' => $wp_upload_dir['url'] . '/' . basename( $_FILES['contest-photo']['name'] ),
'post_mime_type' => $wp_filetype['type'],
'post_title' => $name,
'post_content' => $description,
'post_status' => 'inherit'
);

require_once(ABSPATH . 'wp-admin/includes/image.php');
require_once(ABSPATH . "wp-admin" . '/includes/file.php');
require_once(ABSPATH . "wp-admin" . '/includes/media.php');
$attach_id = media_handle_upload( 'contest-photo', 0,$attachment );

$attach_data = wp_generate_attachment_metadata( $attach_id, $wp_upload_dir['url'] . '/' . basename( $_FILES['contest-photo']['name']) );

wp_update_attachment_metadata( $attach_id, $attach_data );

update_post_meta($attach_id,'contest-active',1);
update_post_meta($attach_id,'contest-photo-points',0);
update_post_meta($attach_id,'contest-photo-author',$user_ID);
update_post_meta($attach_id,'post_author',$user_ID);

$number_images = $number_images+1;
update_user_meta($user_ID, 'contest_user_images', $number_images);


$my_post = array(
'ID' => $attach_id,
'post_author' => $user_ID,
);

wp_update_post( $my_post );


$image = get_post( $attach_id );

if ($attach_id==""){
die("306");
}else{
echo($attach_id);
}
if ( ! $image || 'attachment' != $image->post_type || 'image/' != substr( $image->post_mime_type, 0, 6 ) )
die( json_encode( array( 'error' => sprintf( __( 'Failed resize: %s is an invalid image ID.', 'regenerate-thumbnails' ), esc_html( $attach_id ) ) ) ) );



$fullsizepath = get_attached_file( $image->ID );

if ( false === $fullsizepath || ! file_exists( $fullsizepath ) )

// @set_time_limit( 900 ); // 5 minutes per image should be PLENTY

$metadata = wp_generate_attachment_metadata( $image->ID, $fullsizepath );

if ( is_wp_error( $metadata ) )
if ( empty( $metadata ) )
wp_update_attachment_metadata( $image->ID, $metadata );
return $attach_id;


}


}
?>


UPDATE:

If I try it without:

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


I get:

data =<22546865 72652069 73206e6f 20757365 722e22>
response = <NSHTTPURLResponse: 0x7fcd7e184980> { URL: http://www.clip2gether.com/mobile/app/v1/iOS/upload/upload.php } { status code: 200, headers {
"Cache-Control" = "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
Connection = close;
"Content-Type" = "text/html; charset=UTF-8";
Date = "Sun, 04 Oct 2015 20:39:26 GMT";
Expires = "Thu, 19 Nov 1981 08:52:00 GMT";
Pragma = "no-cache";
Server = "Apache/2.4.10";
"Set-Cookie" = "qtrans_cookie_test=1; path=/; domain=www.clip2gether.com";
"Transfer-Encoding" = Identity;
"X-Powered-By" = "PHP/5.6.12";
} }


And if I try it with the line:

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

data =<41727261 79417272 61790a28 0a290a77 726f6e67 20666f72 6d617420 666f7220 696d6167 65>
response = <NSHTTPURLResponse: 0x7fdd22f3e660> { URL: http://www.clip2gether.com/mobile/app/v1/iOS/upload/upload.php } { status code: 200, headers {
"Cache-Control" = "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
Connection = close;
"Content-Type" = "text/html; charset=UTF-8";
Date = "Sun, 04 Oct 2015 20:46:19 GMT";
Expires = "Thu, 19 Nov 1981 08:52:00 GMT";
Pragma = "no-cache";
Server = "Apache/2.4.10";
"Set-Cookie" = "qtrans_cookie_test=1; path=/; domain=www.clip2gether.com";
"Transfer-Encoding" = Identity;
"X-Powered-By" = "PHP/5.6.12";
} }


So basically I'm getting the same response with setting the value contentType, as hex-code:

There is no user


and without setting it

ArrayArray
(
)
wrong format for image

Answer

Your body data is wrong formatted for a multipart/form-data enctype. Assuming that you want to POST the following fields:

  • username a text field
  • photo-name a text field
  • photo-description a text field
  • contest-photo a file field(will contain image's binary data)

Your body should look like:

--SwiftBoundary
Content-Disposition: form-data; name="username"

my username value
--SwiftBoundary
Content-Disposition: form-data; name="photo-name"

my photo-name value
--SwiftBoundary
Content-Disposition: form-data; name="photo-description"

my photo-description value
--SwiftBoundary
Content-Disposition: form-data; name="contest-photo"; filename="myfile.jpg"
Content-Type: image/jpeg

...my image binary data...
--SwiftBoundary--

You also will have to set the following headers on request:

  • Content-Length to the length of body
  • and Content-Type: multipart/form-data; boundary=SwiftBoundary, here is important that the boundary string matches the one used in body

If you manage to update your request to look like above, you will find your image data under $_FILES['contest-photo']

Your send function should look something like this:

func send()
{
    let request = NSMutableURLRequest(URL: NSURL(string: "***URL to upload.php***")!)

    request.HTTPMethod = "POST"

    let imageData :NSData = UIImageJPEGRepresentation(globalImage, 1.0)!;

    let boundary = "SwiftBoundary"
    let contentType = "multipart/form-data; boundary=\(boundary)"
    let fileName = "\(globalImage.description).jpg"
    let parameterName = "contest-photo"

    let body = NSMutableData()

    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Disposition: form-data; name=\"username\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("\(globalUsr)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Disposition: form-data; name=\"photo-name\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("\(globalImage.description)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Disposition: form-data; name=\"photo-description\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("\(message.text)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Disposition: form-data; name=\"\(parameterName)\"; filename=\"\(fileName)\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Type: image/jpeg\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData(imageData)
    body.appendData("\r\n--\(boundary)--".dataUsingEncoding(NSUTF8StringEncoding)!)

    request.setValue(contentType, forHTTPHeaderField: "Content-Type")
    request.setValue("\(body.length)", forHTTPHeaderField: "Content-Length")
    request.HTTPBody = body


    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
        data, response, error in

        if error != nil {
            print("error=\(error)")
            return
        }

        let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
        print("responseString = \(responseString!)")
    }
    task.resume()
}
Comments