web-dev-qa-db-fra.com

Téléchargement de fichiers sur le serveur HTTP dans la programmation iphone

Quelqu'un peut-il me fournir des liens ou des exemples pour télécharger des fichiers sur le serveur HTTP à l'aide des API iphone.

58
BP.

Le code ci-dessous utilise HTTP POST pour publier NSData sur un serveur Web. Vous avez également besoin d'une connaissance mineure de 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);
74

ASIHTTPRequest est un excellent wrapper autour des API réseau et facilite le téléchargement d'un fichier. Voici leur exemple (mais vous pouvez aussi le faire sur l'iPhone - nous enregistrons les images sur "disque" et les téléchargeons plus tard.

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"];
32
Jane Sales

J'ai utilisé ASIHTTPRequest un peu comme réponse Jane Sales mais il n'est plus en cours de développement et l'auteur suggère d'utiliser d'autres bibliothèques comme AFNetworking.

Honnêtement, je pense que c'est le moment de commencer à chercher ailleurs.

AFNetworking fonctionne très bien, et vous laisse beaucoup travailler avec des blocs (ce qui est un grand soulagement).

Voici un exemple de téléchargement d'image de leur page de documentation sur 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];
7
Tieme

Je pensais que j'ajouterais du code PHP côté serveur à cette réponse pour tous les débutants qui liront ce post et qui ont du mal à comprendre comment recevoir le fichier côté serveur et enregistrer le fichier dans le système de fichiers.

Je me rends compte que cette réponse ne répond pas directement à la question de l'OP, mais comme la réponse de Brandon est suffisante pour le côté appareil iOS du téléchargement et qu'il mentionne qu'une certaine connaissance de php est nécessaire, j'ai pensé que je comblerais l'écart php avec cette réponse.

Voici une classe que j'ai mise en place avec un exemple de code d'utilisation. Notez que les fichiers sont stockés dans des répertoires en fonction de l'utilisateur qui les télécharge. Cela peut ou non s'appliquer à votre utilisation, mais j'ai pensé que je le laisserais en place au cas où.

<?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.');
}

}


?>

Voici quelques exemples de code montrant comment vous pourriez utiliser la classe de téléchargement ...

// 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);
}
2
digitalHound

Essayez ceci .. très facile à comprendre & implémentation ...

Vous pouvez télécharger un exemple de code directement ici 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);
}
2
Rajesh Loganathan

C'est un excellent emballage, mais lors de la publication sur une page Web asp.net, deux valeurs de publication supplémentaires doivent être définies:

    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
Steve The Sultan

Ce n'est pas une solution alternative; plutôt une suggestion pour la réponse populaire de Brandon (vu que je n'ai pas assez de représentant pour commenter cette réponse). Si vous téléchargez des fichiers volumineux; vous obtiendrez probablement une exception mmap malloc car vous devrez lire le fichier en mémoire pour le publier sur votre serveur.

Vous pouvez modifier le code de Brandon en remplaçant:

[request setHTTPBody:postbody];

Avec:

NSInputStream *stream = [[NSInputStream alloc] initWithData:postbody];
[request setHTTPBodyStream:stream];
2
Reece

Une mise à jour de la réponse de @ Brandon, généralisée à une méthode

- (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;
    }
}

Appelez comme cela, en envoyant des données à partir d'une chaîne:

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

J'ai mis à disposition une méthode de sauvegarde légère pour l'application Mobile-AppSales sur github

J'ai écrit à ce sujet ici http://memention.com/blog/2009/11/22/Lightweight-backup.html

Recherchez la méthode - (void)startUpload dans ReportManager.m

1
epatel