【发布时间】:2010-10-30 12:39:28
【问题描述】:
任何人都可以提供一些链接或示例以使用将文件上传到 HTTP 服务器 iphone API。
【问题讨论】:
-
鉴于您无权访问文件系统...您将上传哪些文件?
-
@mmc 文件可能是您自己创建的?您确实可以访问沙箱中的文件系统。
标签: iphone http file-upload
任何人都可以提供一些链接或示例以使用将文件上传到 HTTP 服务器 iphone API。
【问题讨论】:
标签: iphone http file-upload
下面的代码使用 HTTP POST 将 NSData 发布到网络服务器。您还需要对 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);
【讨论】:
14737809831466499882746641449 是一个随机边界。你可以使用任何东西,但每个人总是使用14737809831466499882746641449。我完全不知道为什么,也许它被用于苹果的一些示例代码中。有人有什么想法吗?
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"];
【讨论】:
我使用 ASIHTTPRequest 很像 Jane Sales answer,但它不再处于开发阶段,作者建议使用其他库,如 AFNetworking。
Honestly, I think now is the time to start looking elsewhere.
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];
【讨论】:
这是一个很棒的包装器,但是当发布到 asp.net 网页时,需要设置两个额外的发布值:
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];
【讨论】:
试试这个..很容易理解和实现...
您可以直接在这里下载示例代码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);
}
【讨论】:
这不是替代解决方案;而是对布兰登流行答案的建议(好像我没有足够的代表来评论该答案)。如果您要上传大文件;由于必须将文件读入内存才能将其发布到服务器,您可能会遇到 mmap malloc 异常。
您可以通过替换来调整 Brandon 的代码:
[request setHTTPBody:postbody];
与:
NSInputStream *stream = [[NSInputStream alloc] initWithData:postbody];
[request setHTTPBodyStream:stream];
【讨论】:
我想我会在这个答案中添加一些服务器端 php 代码,以供阅读这篇文章并正在努力弄清楚如何在服务器端接收文件并将文件保存到文件系统的初学者。
我意识到这个答案并没有直接回答 OP 的问题,但是由于 Brandon 的答案对于 iOS 设备端的上传来说已经足够了,而且他提到了一些 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);
}
【讨论】:
我已经为 Mobile-AppSales 应用程序制作了一种轻量级备份方法,网址为github
我在这里写了http://memention.com/blog/2009/11/22/Lightweight-backup.html
在ReportManager.m中寻找- (void)startUpload方法
【讨论】:
@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#>"];
【讨论】: