从ios app发送zip文件到php服务器

时间:2013-11-20 13:24:53

标签: php ios objective-c xampp nsdata

我正在构建一个客户端 - 服务器应用程序,其中应用程序正在发送包含大量图像的zip文件,并且php服务器应检查文件是否已收到并返回zip文件中的图像数量发送。

这是应用程序中的代码:

        NSData *zipData = [[NSData alloc] initWithContentsOfFile:zipFile]; // zipFile contains the zip file path

    NSString *postLength = [NSString stringWithFormat:@"%d", [zipData length]];

    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
    [request setURL:[NSURL URLWithString:@"http://localhost/test.php"]];
    [request setHTTPMethod:@"POST"];
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:zipData];

    NSHTTPURLResponse* urlResponse = nil;
    NSError *error = [[NSError alloc] init];
    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];
    NSString *result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    NSLog(@"Response Code: %d", [urlResponse statusCode]);

    if ([urlResponse statusCode] >= 200 && [urlResponse statusCode] < 300)
    {
        NSLog(@"Response: %@", result);
    }

这是我的php文件,使用xampp在本地主机上运行:

<?php
print_r($_POST);

print_r($_FILES);
?>

使用sethttpbody将zip文件附加到请求中,我正在尝试查看php中的文件,如上所示,但它始终为空,请帮助我,而且是通过以下方式查看发送的zip文件的权利打印$ _POST和$ _FILES如果没有正确的方法,zip文件转换为NSDATA,我也检查了连接已建立,但我真的很困惑。

2 个答案:

答案 0 :(得分:1)

您的Objective-C代码存在问题,您将Content-Type标头定义为application/x-www-form-urlencoded,但请求正文与Content-Type不一致。从理论上讲,您可以编写处理构造请求的PHP代码(读取正文的原始数据),但最好更改请求以符合既定标准。

如果您自己编写代码来构造这样的请求,那就有点难看了(请参阅本答案的结尾),所以我建议使用AFNetworking,这极大地简化了这些请求的创建: / p>

NSData *zipData = [NSData dataWithContentsOfFile:zipFile];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:urlString parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    [formData appendPartWithFileData:zipData name:@"file" fileName:[zipFile lastPathComponent] mimeType:@"application/zip"];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
    BOOL success = [responseObject[@"success"] boolValue];
    NSString *errorMessage = responseObject[@"error"];
    NSLog(@"Success: %d; Error message: %@", success, errorMessage);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

注意,这需要一个JSON响应,因此接收它的相应PHP代码,将其保存到“upload”子目录中,并返回JSON响应可能如下所示:

<?php

    header("Content-Type: application/json");

    $allowedExts = array("zip", "gz");
    $extension = end(explode(".", $_FILES["file"]["name"]));
    if (in_array($extension, $allowedExts))
    {
        if ($_FILES["file"]["error"] > 0)
        {
            echo json_encode(array("success" => false, "error" => $_FILES["file"]["error"]));
        }
        else
        {
            if (file_exists("upload/" . $_FILES["file"]["name"]))
            {
                echo json_encode(array("success" => false, "error" => $_FILES["file"]["name"] . " already exists"));
            }
            else
            {
                move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
                echo json_encode(array("success" => true));
            }
        }
    }
    else
    {
        echo json_encode(array("success" => false, "error" => "Invalid file type"));
    }

?>

虽然我真的鼓励你使用AFNetworking,但如果你想构建自己的请求,你可以这样做:

[self uploadFileAtPath:zipFile forField:@"file" URL:[NSURL URLWithString:urlString] parameters:nil completion:^(BOOL success, NSString *errorMessage) {
    NSLog(@"success = %d; errorMessage = %@", success, errorMessage);
}];

其中,uploadFileAtPath和一些支持方法的定义如下:

- (void)uploadFileAtPath:(NSString *)path
                forField:(NSString *)fieldName
                     URL:(NSURL*)url
              parameters:(NSDictionary *)parameters
              completion:(void (^)(BOOL success, NSString *errorMessage))completion
{
    NSString *filename = [path lastPathComponent];
    NSData *data = [NSData dataWithContentsOfFile:path];

    NSMutableData *httpBody = [NSMutableData data];
    NSString *boundary = [self generateBoundaryString];
    NSString *mimetype = [self mimeTypeForPath:path];

    // configure the request

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
    [request setHTTPShouldHandleCookies:NO];
    [request setTimeoutInterval:30];
    [request setHTTPMethod:@"POST"];

    // set content type

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

    // add params (all params are strings)

    [parameters enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) {
        [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", parameterKey] dataUsingEncoding:NSUTF8StringEncoding]];
        [httpBody appendData:[[NSString stringWithFormat:@"%@\r\n", parameterValue] dataUsingEncoding:NSUTF8StringEncoding]];
    }];

    // add image data

    if (data) {
        [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName, filename] dataUsingEncoding:NSUTF8StringEncoding]];
        [httpBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimetype] dataUsingEncoding:NSUTF8StringEncoding]];
        [httpBody appendData:data];
        [httpBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    }

    [httpBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    // setting the body of the post to the reqeust

    [request setHTTPBody:httpBody];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        if (connectionError)
        {
            if (completion)
                completion(FALSE, [NSString stringWithFormat:@"%s: sendAsynchronousRequest error: %@", __FUNCTION__, connectionError]);
            return;
        }

        NSError *error = nil;
        NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
        if (!responseObject)
        {
            if (completion)
                completion(FALSE, [NSString stringWithFormat:@"%s: JSONObjectWithData error=%@", __FUNCTION__, error]);
            return;
        }

        BOOL success = [responseObject[@"success"] boolValue];
        NSString *errorMessage = responseObject[@"error"];
        if (completion)
            completion(success, errorMessage);
    }];
}

- (NSString *)generateBoundaryString
{
    // generate boundary string
    //
    // adapted from http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections
    //
    // Note in iOS 6 and later, you can just:
    //
    //    return [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];

    CFUUIDRef  uuid;
    NSString  *uuidStr;

    uuid = CFUUIDCreate(NULL);
    assert(uuid != NULL);

    uuidStr = CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
    assert(uuidStr != NULL);

    CFRelease(uuid);

    return [NSString stringWithFormat:@"Boundary-%@", uuidStr];
}

- (NSString *)mimeTypeForPath:(NSString *)path
{
    // get a mime type for an extension using MobileCoreServices.framework

    CFStringRef extension = (__bridge CFStringRef)[path pathExtension];
    CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extension, NULL);
    assert(UTI != NULL);

    NSString *mimetype = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType));
    assert(mimetype != NULL);

    CFRelease(UTI);

    return mimetype;
}

答案 1 :(得分:1)

虽然我不推荐它(我希望看到格式正确的multipart/form-dataapplication/JSON请求),但您可以像这样创建您的请求:

NSData *zipData = [NSData dataWithContentsOfFile:zipFile]; // note, autorelease object

NSString *postLength = [NSString stringWithFormat:@"%d", [zipData length]];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/zip" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:zipData];

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    NSString *result = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];

    NSInteger statusCode = -1;
    if ([response isKindOfClass:[NSHTTPURLResponse class]])
        statusCode = [(NSHTTPURLResponse *)response statusCode];

    NSLog(@"Response Code: %d", statusCode);

    if (statusCode >= 200 && statusCode < 300)
    {
        NSLog(@"Response: %@", result);
    }
}];

然后您可以使用PHP代码读取此二进制数据并将其保存到文件中:

<?php

$handle = fopen("php://input", "rb");
$http_raw_post_data = '';
while (!feof($handle)) {
    $http_raw_post_data .= fread($handle, 8192);
}
fclose($handle);

$handle = fopen('upload/myzip.zip', 'wb');

if ($handle == false) {
    echo 'unable to open file';
    exit (0);
}

$bytes = fwrite($handle, $http_raw_post_data);

if ($bytes == false)
    echo 'write failed';
else
    echo 'write succeeded';

fclose($handle);

?>