I am trying to upload a text file to a web server from an iOS application. I have written an objective C method to handle the upload request and a PHP file to handle the POST request at the server. I have also written a small HTML page to test the upload from a browser (which is working fine using the same PHP file). When the iOS App runs it should upload a text file (which is empty ie 0 bytes in size) to a specified folder on the web server, and although the debug window shows the response as success(200) the file is not uploaded.
Here is the Objective C method
+ (void)fileUpload{
NSString *localFilePath = [self GetTokenFilePath];
NSURL *localFile = [NSURL fileURLWithPath:localFilePath];
NSData *data = [[NSData alloc] initWithContentsOfFile:localFilePath];
NSString *filename = [self GetTokenFilename];
// Set up the body of the POST request.
// This boundary serves as a separator between one form field and the next.
// It must not appear anywhere within the actual data that you intend to
// upload.
NSString * boundary = @"---------------------------14737809831466499882746641449";
// Body of the POST method
NSMutableData * body = [NSMutableData data];
// The body must start with the boundary preceded by two hyphens, followed
// by a carriage return and newline pair.
//
// Notice that we prepend two additional hyphens to the boundary when
// we actually use it as part of the body data.
//
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// This is followed by a series of headers for the first field and then
// TWO CR-LF pairs. set tag-name to submit upload
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@.txt\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
// Next is the actual data for that field (called "tag_name") followed by
// a CR-LF pair, a boundary, and another CR-LF pair.
[body appendData:[NSData dataWithData:data]];
// Close the request body with one last boundary with two
// additional hyphens prepended **and** two additional hyphens appended.
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Data uploading task.
NSURL * url = [NSURL URLWithString:@"https://MY_URL.co.uk/Upload.php"];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
NSString * contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];
request.HTTPMethod = @"POST";
request.HTTPBody = body;
/*------------------*/
NSURLSession *session = [NSURLSession sharedSession];
// Create a NSURLSessionUploadTask object to Upload data for a jpg image.
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromFile:localFile completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
// Upload success.
NSLog(@"upload file:%@",localFile);
NSLog(@"upload success:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
NSLog(@"upload response:%@",response);
// Update user defaults to show success on token upload.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:TRUE forKey:@"TOKEN_UPLOAD_SUCCESS"];
} else {
// Upload fail.
NSLog(@"upload error:%@",error);
NSLog(@"upload response:%@",response);
// Update user defaults to show success on token upload.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:FALSE forKey:@"TOKEN_UPLOAD_SUCCESS"];
}
}];
[task resume];
}
Here is the PHP file I use at the server end
<?php
print_r($_POST);
echo "<br>";
//phpinfo();
// file properties
$file = $_FILES['file'];
$file_name = $_FILES['file']['name'];
$file_tmp = $_FILES['file']['tmp_name'];
$file_size = $_FILES['file']['size'];
$file_error = $_FILES['file']['error'];
$file_type = $_FILES['file']['type'];
echo "File name = ".$file_name."<br>";
echo "File size = ".$file_size."<br>";
echo "File error = ".$file_error."<br>";
//print_r($file);
//work out the file extension
$file_ext = explode('.' , $file_name );
$file_actual_ext = strtolower(end($file_ext));
echo "File Extension = ".$file_actual_ext."<br>";
//echo $file_actual_ext;
$allowed = array('txt', 'csv');
print_r($allowed);
echo "<br>";
if(in_array($file_actual_ext,$allowed)) {
echo "File type is allowed"."<br>";
if($file_error === 0) {
if($file_size <= 1048576) {
$file_destination = "TokensV11/".$file_name;
echo "File Destination = ".$file_destination."<br>";
if(move_uploaded_file($file_tmp, $file_destination)) {
echo "The file has been uploaded sucessfully"."<br>";
}else{
echo "The file was not moved to destination"."<br>";
}
}else{
echo "The file size is too large"."<br>";
}
}else{
echo "There was an error uploading your file"."<br>";
}
}else {
echo "You cannot upload files of this type or no file was specified"."<br>";
}
?>
Here is the output sample from the debug window in Xcode
2023-07-25 12:39:26.855874+0100 BikerCafe[3036:86717] upload file:file:///Users/stephencox/Library/Developer/CoreSimulator/Devices/5246C453-B5BF-4F6E-B4BD-E5121A382E87/data/Containers/Data/Application/MYFILE.txt
2023-07-25 12:39:28.238222+0100 BikerCafe[3036:86717] upload success:The PHP script has started<br>File name = <br>File size = <br>File error = <br>File Extension = <br>Array
(
[0] => txt
[1] => csv
[2] => png
)
<br>File Destination = TokensV11/<br>
2023-07-25 12:39:30.723756+0100 BikerCafe[3036:86717] upload response:<NSHTTPURLResponse: 0x6000010e7f60> { URL: https://tokens.ukbikercafes.co.uk/TokenUpload.php } { Status Code: 200, Headers {
"Content-Encoding" = (
gzip
);
"Content-Type" = (
"text/html; charset=UTF-8"
);
Date = (
"Tue, 25 Jul 2023 11:39:23 GMT"
);
Server = (
Apache
);
"x-powered-by" = (
"PHP/7.4.33"
);
} }
I have managed to figure out my issue using the guidance of the example from this stackoverflow post. Upload multipart image to PHP server from iOS app . I did have to convert the example from Swift to Objective C with a few minor changes to suit my needs but it worked well.