58

Can anyone provide me some links or examples to upload files to the HTTP server using iphone APIs.

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
BP.
  • 621
  • 2
  • 7
  • 4
  • Given that you don't have access to the file system... what files would you be uploading? – mmc Jun 01 '09 at 22:48
  • 3
    @mmc files you created yourself perhaps? you do have access to the filesystem within your sandbox. – Rog Jun 02 '09 at 05:53

9 Answers9

74

The code below uses HTTP POST to post NSData to a webserver. You also need minor knowledge of PHP.

NSString *urlString = @"http://yourserver.com/upload.php";
NSString *filename = @"filename";
request= [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:@"POST"];
NSString *boundary = @"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request addValue:contentType forHTTPHeaderField: @"Content-Type"];
NSMutableData *postbody = [NSMutableData data];
[postbody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"userfile\"; filename=\"%@.jpg\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithString:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[NSData dataWithData:YOUR_NSDATA_HERE]];
[postbody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postbody];

NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSLog(@"%@", returnString);
Brandon Schlenker
  • 5,078
  • 1
  • 36
  • 58
  • 1
    Brandon, Thanks for your response in the above code i have some questions, 1) Where we are passing upload file path 2) what i need tp pass here YOUR_NSDATA_HERE actually i am having file name called test.txt in this path /Users/abc/Desktop/test.txt can you tell me where should i pass this info using above code ,i executed above code it gives NSInvalidArgumentException. please help pleas.. --BP – BP. Jun 02 '09 at 20:54
  • 1
    You need to convert your text file to NSData. NSData *data = [[NSData alloc] initWithContentsOfFile:path]; "path" is obviously the path to your text file such as NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"image.jpg"]; – Brandon Schlenker Jun 02 '09 at 23:44
  • Thanks Brandon, i have one question , i have 1 browse button if i choose it should open iphone or ipod files all txt or imgs do we have any kind of API's to open file system . and that files pth i need to add to NSData. i am sorry if i am wrong but please help me please. Thanks in advance. – BP. Jun 03 '09 at 06:15
  • There isnt any sort of file picker. I dont think you can access files outside of files that your app has created. – Brandon Schlenker Jun 04 '09 at 02:33
  • 1
    I tried this out on my own and I keep getting the errors from my webserver about the format of the post message (System.InvalidOperationException: Request format is invalid: multipart/form-data; boundary=---------------------------14737809831466499882746641449) . Does anyone get errors? – Abel Jan 14 '10 at 18:56
  • Can u plz modify it to send multiple files at a time ? – Biranchi Mar 17 '10 at 04:53
  • 3
    This looks nice. I don't see any PHP though. I think you meant HTTP. – ma11hew28 Aug 06 '10 at 14:07
  • I'd like to add here that some webservers (lighttpd in my case) do not like the omission of the Content-Type header and will not accept the upload without it. – Promit Jan 19 '11 at 03:20
  • 1
    What does the '14737809831466499882746641449' stand for? Seems quite specific. – Marc Apr 23 '12 at 11:19
  • Simple PHP: Create an 'upload.php' on your server and make an 'uploads' directory underneath it. Then use this code: – mpemburn Oct 10 '12 at 11:13
  • 4
    @Marc `14737809831466499882746641449` is a random boundary. You can use anything but everybody is always using `14737809831466499882746641449`. I have absolutely no idea why, maybe it is used in some of apple's sample code. Anyone has any ideas? – Tieme Dec 10 '12 at 09:40
  • 1
    @tieme, it's from my blog. I published it from when Apple still had the NDA on showing code. http://zcentric.com/2008/08/29/post-a-uiimage-to-the-web/ – Mike Jan 30 '13 at 16:54
  • @Tieme Here's Boundary Zero, a random boundary sniffed via wireshark, as per the comment: http://zcentric.com/2008/08/29/post-a-uiimage-to-the-web/ – bobthecow Jan 30 '13 at 16:55
  • @Mike Okay, so everybody is actually using some sample boundary originally used by apple? – Tieme Feb 01 '13 at 05:54
  • @Tieme no that boundary was my wireshark dump of a image post I did to my server. – Mike Feb 01 '13 at 12:40
  • I have tried. But i'm not getting any error. Image is not loading server. Is it possible to send PDF also same way? – Yalamandarao Jan 08 '15 at 14:09
  • we are using PHP code in server side: – Yalamandarao Jan 08 '15 at 14:19
  • But image is not uploading – Yalamandarao Jan 08 '15 at 14:20
  • @mpemburn @Singapore My question to you is shouldn't I use my remote directory here, where I want to upload my files? `$file_path = "uploads/";` I use `$file_path = "$uploaddir = 'http://xxxxx.com/UploadImageInServer/';";`, but it is not working at all. – Tulon Sep 12 '17 at 18:09
32

ASIHTTPRequest is a great wrapper around the network APIs and makes it very easy to upload a file. Here's their example (but you can do this on the iPhone too - we save images to "disk" and later upload them.

ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setPostValue:@"Ben" forKey:@"first_name"];
[request setPostValue:@"Copsey" forKey:@"last_name"];
[request setFile:@"/Users/ben/Desktop/ben.jpg" forKey:@"photo"];
Jane Sales
  • 13,526
  • 3
  • 52
  • 57
7

I used ASIHTTPRequest a lot like Jane Sales answer but it is not under development anymore and the author suggests using other libraries like AFNetworking.

Honestly, I think now is the time to start looking elsewhere.

AFNetworking works great, and let you work with blocks a lot (which is a great relief).

Here's an image upload example from their documentation page on github:

NSURL *url = [NSURL URLWithString:@"http://api-base-url.com"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:@"avatar.jpg"], 0.5);
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:@"POST" path:@"/upload" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
    [formData appendPartWithFileData:imageData name:@"avatar" fileName:@"avatar.jpg" mimeType:@"image/jpeg"];
}];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
    NSLog(@"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[httpClient enqueueHTTPRequestOperation:operation];
Community
  • 1
  • 1
Tieme
  • 62,602
  • 20
  • 102
  • 156
2

Try this.. very easy to understand & implementation...

You can download sample code directly here https://github.com/Tech-Dev-Mobile/Json-Sample

- (void)simpleJsonParsingPostMetod
{

#warning set webservice url and parse POST method in JSON
    //-- Temp Initialized variables
    NSString *first_name;
    NSString *image_name;
    NSData *imageData;

    //-- Convert string into URL
    NSString *urlString = [NSString stringWithFormat:@"demo.com/your_server_db_name/service/link"];
    NSMutableURLRequest *request =[[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:@"POST"];

    NSString *boundary = @"14737809831466499882746641449";
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
    [request addValue:contentType forHTTPHeaderField: @"Content-Type"];

    //-- Append data into posr url using following method
    NSMutableData *body = [NSMutableData data];


    //-- For Sending text

        //-- "firstname" is keyword form service
        //-- "first_name" is the text which we have to send
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",@"firstname"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"%@",first_name] dataUsingEncoding:NSUTF8StringEncoding]];


    //-- For sending image into service if needed (send image as imagedata)

        //-- "image_name" is file name of the image (we can set custom name)
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    [body appendData:[[NSString stringWithFormat:@"Content-Disposition:form-data; name=\"file\"; filename=\"%@\"\r\n",image_name] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[NSData dataWithData:imageData]];
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];


    //-- Sending data into server through URL
    [request setHTTPBody:body];

    //-- Getting response form server
    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

    //-- JSON Parsing with response data
    NSDictionary *result = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
    NSLog(@"Result = %@",result);
}
Rajesh Loganathan
  • 11,129
  • 4
  • 78
  • 90
2

This isn't an alternative solution; rather a suggestion for Brandon's popular answer (seeing as though I don't have enough rep to comment on that answer). If you're uploading large files; you're probably going to get a mmap malloc exception on account of having to read the file into memory to post it to your server.

You can tweak Brandon's code by replacing:

[request setHTTPBody:postbody];

With:

NSInputStream *stream = [[NSInputStream alloc] initWithData:postbody];
[request setHTTPBodyStream:stream];
Reece
  • 641
  • 7
  • 18
2

This is a great wrapper, but when posting to a asp.net web page, two additional post values need to be set:

    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    //ADD THESE, BECAUSE ASP.NET is Expecting them for validation
    //Even if they are empty you will be able to post the file
    [request setPostValue:@"" forKey:@"__VIEWSTATE"];
    [request setPostValue:@"" forKey:@"__EVENTVALIDATION"]; 
    ///

    [request setFile:FIleName forKey:@"fileupload_control_Name"];
    [request startSynchronous];
2

I thought I would add some server side php code to this answer for any beginners that read this post and are struggling to figure out how to receive the file on the server side and save the file to the filesystem.

I realize that this answer does not directly answer the OP's question, but since Brandon's answer is sufficient for the iOS device side of uploading and he mentions that some knowledge of php is necessary, I thought I would fill in the php gap with this answer.

Here is a class I put together with some sample usage code. Note that the files are stored in directories based on which user is uploading them. This may or may not be applicable to your use, but I thought I'd leave it in place just in case.

<?php


class upload
{
    protected $user;

    protected $isImage;
    protected $isMovie;

    protected $file;
    protected $uploadFilename;
    protected $uploadDirectory;
    protected $fileSize;
    protected $fileTmpName;
    protected $fileType;
    protected $fileExtension;

    protected $saveFilePath;

    protected $allowedExtensions;

function __construct($file, $userPointer)
{
    // set the file we're uploading
    $this->file = $file;

    // if this is tied to a user, link the user account here
    $this->user = $userPointer;

    // set default bool values to false since we don't know what file type is being uploaded yet
    $this->isImage   = FALSE;
    $this->isMovie   = FALSE;

    // setup file properties
    if (isset($this->file) && !empty($this->file))
    {   
        $this->uploadFilename   = $this->file['file']['name'];
        $this->fileSize         = $this->file['file']['size'];
        $this->fileTmpName      = $this->file['file']['tmp_name'];
        $this->fileType         = $this->file['file']['type'];
    }
    else
    {
        throw new Exception('Received empty data. No file found to upload.');
    }

    // get the file extension of the file we're trying to upload
    $tmp = explode('.', $this->uploadFilename);
    $this->fileExtension        = strtolower(end($tmp));

}



public function image($postParams)
{
    // set default error alert (or whatever you want to return if error)
    $retVal = array('alert' => '115');

    // set our bool
    $this->isImage = TRUE;

    // set our type limits
    $this->allowedExtensions    = array("png");

    // setup destination directory path (without filename yet)
    $this->uploadDirectory      = DIR_IMG_UPLOADS.$this->user->uid."/photos/";

    // if user is not subscribed they are allowed only one image, clear their folder here
    if ($this->user->isSubscribed() == FALSE)
    {
        $this->clearFolder($this->uploadDirectory);
    }

    // try to upload the file
    $success = $this->startUpload();

    if ($success === TRUE)
    {
        // return the image name (NOTE: this wipes the error alert set above)
        $retVal = array(
                        'imageName' =>  $this->uploadFilename,
                        );
    }

    return $retVal;
}



public function movie($data)
{
    // update php settings to handle larger uploads
    set_time_limit(300);

    // you may need to increase allowed filesize as well if your server is not set with a high enough limit

    // set default return value (error code for upload failed)
    $retVal = array('alert' => '92');

    // set our bool
    $this->isMovie = TRUE;

    // set our allowed movie types
    $this->allowedExtensions = array("mov", "mp4", "mpv", "3gp");

    // setup destination path
    $this->uploadDirectory = DIR_IMG_UPLOADS.$this->user->uid."/movies/";

    // only upload the movie if the user is a subscriber
    if ($this->user->isSubscribed())
    {
        // try to upload the file
        $success = $this->startUpload();

        if ($success === TRUE)
        {
            // file uploaded so set the new retval
            $retVal = array('movieName' => $this->uploadFilename);
        }
    }
    else
    {
        // return an error code so user knows this is a limited access feature
        $retVal = array('alert' => '13');
    }

    return $retVal;
}




//-------------------------------------------------------------------------------
//                          Upload Process Methods
//-------------------------------------------------------------------------------

private function startUpload()
{
    // see if there are any errors
    $this->checkForUploadErrors();

    // validate the type received is correct
    $this->checkFileExtension();

    // check the filesize
    $this->checkFileSize();

    // create the directory for the user if it does not exist
    $this->createUserDirectoryIfNotExists();

    // generate a local file name
    $this->createLocalFileName();

    // verify that the file is an uploaded file
    $this->verifyIsUploadedFile();

    // save the image to the appropriate folder
    $success = $this->saveFileToDisk();

    // return TRUE/FALSE
    return $success;
}

private function checkForUploadErrors()
{
    if ($this->file['file']['error'] != 0)
    {
        throw new Exception($this->file['file']['error']);
    }
}

private function checkFileExtension()
{
    if ($this->isImage)
    {
        // check if we are in fact uploading a png image, if not return error
        if (!(in_array($this->fileExtension, $this->allowedExtensions)) || $this->fileType != 'image/png' || exif_imagetype($this->fileTmpName) != IMAGETYPE_PNG)
        {
            throw new Exception('Unsupported image type. The image must be of type png.');
        }
    }
    else if ($this->isMovie)
    {
        // check if we are in fact uploading an accepted movie type
        if (!(in_array($this->fileExtension, $this->allowedExtensions)) || $this->fileType != 'video/mov')
        {
            throw new Exception('Unsupported movie type. Accepted movie types are .mov, .mp4, .mpv, or .3gp');
        }
    }   
}

private function checkFileSize()
{
    if ($this->isImage)
    {
        if($this->fileSize > TenMB)
        {
            throw new Exception('The image filesize must be under 10MB.');
        }
    }
    else if ($this->isMovie)
    {
        if($this->fileSize > TwentyFiveMB) 
        {
            throw new Exception('The movie filesize must be under 25MB.');
        }
    }
}

private function createUserDirectoryIfNotExists()
{
    if (!file_exists($this->uploadDirectory)) 
    {
        mkdir($this->uploadDirectory, 0755, true);
    }
    else
    {
        if ($this->isMovie)
        {
            // clear any prior uploads from the directory (only one movie file per user)
            $this->clearFolder($this->uploadDirectory);
        }
    }
}

private function createLocalFileName()
{
    $now = time();

    // try to create a unique filename for this users file
    while(file_exists($this->uploadFilename = $now.'-'.$this->uid.'.'.$this->fileExtension))
    {
        $now++;
    }

    // create our full file save path
    $this->saveFilePath = $this->uploadDirectory.$this->uploadFilename;
}

private function clearFolder($path)
{
    if(is_file($path))
    {
        // if there's already a file with this name clear it first
        return @unlink($path);
    }
    elseif(is_dir($path))
    {
        // if it's a directory, clear it's contents
        $scan = glob(rtrim($path,'/').'/*');
        foreach($scan as $index=>$npath)
        {
            $this->clearFolder($npath);
            @rmdir($npath);
        }
    }
}

private function verifyIsUploadedFile()
{
    if (! is_uploaded_file($this->file['file']['tmp_name']))
    {
        throw new Exception('The file failed to upload.');
    }
}


private function saveFileToDisk()
{
    if (move_uploaded_file($this->file['file']['tmp_name'], $this->saveFilePath))
    {
        return TRUE;     
    }

    throw new Exception('File failed to upload. Please retry.');
}

}


?>

Here's some sample code demonstrating how you might use the upload class...

// get a reference to your user object if applicable
$myUser = $this->someMethodThatFetchesUserWithId($myUserId);

// get reference to file to upload
$myFile = isset($_FILES) ? $_FILES : NULL;

// use try catch to return an error for any exceptions thrown in the upload script
try 
{
    // create and setup upload class
    $upload = new upload($myFile, $myUser);

    // trigger file upload
    $data   = $upload->image();     // if uploading an image
    $data  = $upload->movie();      // if uploading movie

    // return any status messages as json string
    echo json_encode($data);
} 
catch (Exception $exception) 
{
    $retData = array(
            'status'    => 'FALSE',
            'payload'   => array(
                            'errorMsg' => $exception->getMessage()
                            ),
                );

    echo json_encode($retData);
}
digitalHound
  • 4,384
  • 27
  • 27
1

I have made a lightweight backup method for the Mobile-AppSales app available at github

I wrote about it here http://memention.com/blog/2009/11/22/Lightweight-backup.html

Look for the - (void)startUpload method in ReportManager.m

epatel
  • 45,805
  • 17
  • 110
  • 144
1

An update to @Brandon's answer, generalized to a method

- (NSString*) postToUrl:(NSString*)urlString data:(NSData*)dataToSend withFilename:(NSString*)filename
{
    NSMutableURLRequest *request= [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:@"POST"];
    NSString *boundary = @"---------------------------14737809831466499882746641449";
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
    [request addValue:contentType forHTTPHeaderField: @"Content-Type"];
    NSMutableData *postbody = [NSMutableData data];
    [postbody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [postbody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"userfile\"; filename=\"%@\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
    [postbody appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [postbody appendData:[NSData dataWithData:dataToSend]];
    [postbody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [request setHTTPBody:postbody];

    NSError* error;
    NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
    if (returnData) {
        return [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
    }
    else {
        return nil;
    }
}

Invoke like so, sending data from a string:

[self postToUrl:@"<#Your url string#>"
           data:[@"<#Your string to send#>" dataUsingEncoding:NSUTF8StringEncoding]
   withFilename:@"<#Filename to post with#>"];
Stan James
  • 2,535
  • 1
  • 28
  • 35