web-dev-qa-db-ja.com

iPhoneプログラミングでのHTTPサーバーへのファイルのアップロード

Iphone APIを使用してHTTPサーバーにファイルをアップロードするためのリンクまたは例を提供してください。

58
BP.

以下のコードでは、HTTP POSTを使用してNSDataをWebサーバーにポストします。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 はネットワークAPIの優れたラッパーであり、ファイルのアップロードを非常に簡単にします。以下に例を示します(ただし、これはiPhoneでも実行できます。画像を「ディスク」に保存し、後でアップロードします。

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

Jane Sales answer のようにASIHTTPRequestを使用しましたが、もう開発中ではなく、著者はAFNetworkingのような他のライブラリの使用を提案しています。

正直なところ、今が他の場所を探し始める時だと思う。

AFNetworking はうまく機能し、ブロックをたくさん操作できます(これは非常に安心です)。

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

この投稿を読んで、サーバー側でファイルを受信して​​ファイルシステムにファイルを保存する方法を見つけるのに苦労している初心者のために、この答えにサーバー側のPHPコードを追加すると思いました。

私はこの答えがOPの質問に直接答えないことを理解していますが、ブランドンの答えはアップロードのiOSデバイス側には十分であり、彼はPHPのある程度の知識が必要であると述べているので、私はこの答えでPHPのギャップを埋めると思いました。

これは、サンプルの使用コードと一緒に作成したクラスです。ファイルは、アップロードするユーザーに基づいてディレクトリに保存されることに注意してください。これはあなたの使用に当てはまるかもしれないし、当てはまらないかもしれませんが、万が一のためにそのままにしておくと思いました。

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

}


?>

アップロードクラスの使用方法を示すサンプルコードを次に示します...

// 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

これを試してください。非常に理解しやすい&実装...

ここからサンプルコードを直接ダウンロードできます 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

これは素晴らしいラッパーですが、asp.net Webページに投稿する場合、2つの追加の投稿値を設定する必要があります。

    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

これは代替ソリューションではありません。むしろ、ブランドンの一般的な回答の提案(その回答にコメントするのに十分な担当者がいないように見える)。大きなファイルをアップロードする場合;サーバーに投稿するためにファイルをメモリに読み込む必要があるため、おそらくmmap malloc例外が発生します。

以下を置き換えることで、ブランドンのコードを微調整できます。

[request setHTTPBody:postbody];

と:

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

メソッドに一般化された@Brandonの回答の更新

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

そのように呼び出して、文字列からデータを送信します。

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

github で利用可能なMobile-AppSalesアプリの軽量バックアップ方法を作成しました

私はそれについてここに書いた http://memention.com/blog/2009/11/22/Lightweight-backup.html

_ReportManager.m_で- (void)startUploadメソッドを探します

1
epatel