AFNetworking загружает UIImage как NSData -> файл перемещения PHP-скрипта

Я знаю, что этот вопрос был опубликован много раз. Я пытался заставить мой код работать, но не могу найти проблему. Помогите мне, пожалуйста. В моем приложении пользователь делает фотографию, и ее нужно загрузить на веб-сервер.

вот мой код iOS:

NSData *data = UIImageJPEGRepresentation([UIImage imageNamed:@"oeffnungszeiten.jpg"], 1.0);

NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:BaseURLString parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {


    [formData appendPartWithFileData:data name:@"uploadedfile" fileName:_imageString mimeType:@"image/jpeg"];
} error:nil];


AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSProgress *progress = nil;


NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        NSLog(@"%@ %@", response, responseObject);
    }
}];


[uploadTask resume];

и вот мой PHP-код:

<?php
error_log("\n-->".$FILES["uploadedfile"]['name'], 3, "log.txt");
$target_path = "/";

$target_path = $target_path . basename( $_FILES['uploadedfile']['name']); 

if (!$FILES["uploadedfile"]) {
    error_log("\nleer", 3, "log.txt");
}
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
    echo "The file ".  basename( $_FILES['uploadedfile']['name']). 
    " has been uploaded";
    error_log("The file ".  basename( $_FILES['uploadedfile']['name']). 
    " has been uploaded", 3, "log.txt");
} else{
    echo "There was an error uploading the file, please try again!";
    error_log("There was an error uploading the file, please try again!", 3, "log.txt");
}
?>

Вы можете найти мою ошибку? Изображение не отправляется на сервер!


person Raphael    schedule 15.03.2014    source источник
comment
И что именно происходит, когда вы запускаете это? Какой ответ вы получаете? Любые сообщения об ошибках?   -  person Rob    schedule 15.03.2014
comment
Я получаю следующее: <NSHTTPURLResponse: 0x10adaef80> { URL: http://www....URLTOMYSCRIPT... } { status code: 200, headers { Connection = close; "Content-Length" = 0; "Content-Type" = "text/html"; Date = "Sat, 15 Mar 2014 11:47:38 GMT"; Server = Apache; } } <> and $FILES[uploadedfile]['name'] пуст.   -  person Raphael    schedule 15.03.2014


Ответы (1)


Пара мыслей:

  1. Некоторые серверы не обрабатывают запросы chunked. (Запрос Transfer-encoding из chunked традиционно представляет собой способ потоковой передачи запросов на сервер, где Content-length заранее неизвестен.) Способ создания запроса chunked в NSURLSession заключается в использовании NSInputStream с запросом, а не NSData или файла.

    К сожалению, AFNetworking всегда использует метод NSInputStream (по крайней мере, для составных запросов), что означает, что все запросы передаются на сервер с использованием Transfer-encoding из chunked. Однако вы можете после отправки запроса создать NSData из NSInputStream, удалить HTTPBodyStream из запроса, а затем использовать метод фабрики задач загрузки, который использует NSData, а не потоковый запрос.

    NSMutableURLRequest *request = ... ; // create the request like you are now
    
    // create `NSData` from the `NSInputStream`
    
    NSMutableData *requestData = [NSMutableData data];
    u_int8_t buffer[1024];
    NSInteger length;
    
    [request.HTTPBodyStream open];
    
    do {
        length = [request.HTTPBodyStream read:buffer maxLength:sizeof(buffer)];
        if (length > 0)
            [requestData appendBytes:buffer length:length];
    } while (length > 0);
    
    [request.HTTPBodyStream close];
    
    // now that we have our `NSData`, we can remove `HTTPBodyStream` from request
    
    request.HTTPBodyStream = nil;
    request.HTTPBody = nil;
    
    // instead of uploadTaskWithStreamedRequest, actually specify the `NSData` for the body of the request
    
    NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromData:requestData progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error);
        } else {
            NSLog(@"%@ %@", response, responseObject);
        }
    }];
    
    [uploadTask resume];
    

    Это, по общему признанию, немного неэффективно (вы можете уменьшить объем памяти, передав это в файл, а не NSData, а затем используя это в своей задаче загрузки), но это единственный известный мне способ заставить AFNetworking не создавать потоковую передачу. , chunked запрос для составных запросов. Поэтому я бы посоветовал выполнить это упражнение только в том случае, если ваш сервер не принимает запросы chunked.

  2. Если ваш сервер выдает запрос на аутентификацию, в AFNetworking есть ошибка, которая может вызвать у вас проблемы. (Если вы не получаете никаких запросов на аутентификацию, эта ошибка не проявится для вас, и вам не нужно об этом беспокоиться.)

    Глядя на это в Charles, я замечаю, что AFNetworking правильно указывает границу в заголовке запроса, но когда граница используется в теле запроса, который повторно выдается после проверки подлинности, граница части тела становится nil. Это из-за ошибки в copyWithZone для AFHTTPBodyPart. Я отправил запрос на включение, который устраняет эту проблему.

    Если вы хотите исправить это в своей локальной копии, перейдя к AFSerialization.h и заменив copyWithZone на AFHTTPBodyPart:

    - (id)copyWithZone:(NSZone *)zone {
        AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];
    
        bodyPart.stringEncoding = self.stringEncoding;
        bodyPart.headers = self.headers;
        bodyPart.bodyContentLength = self.bodyContentLength;
        bodyPart.body = self.body;
    
        return bodyPart;
    }
    

    с

    - (id)copyWithZone:(NSZone *)zone {
        AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];
    
        bodyPart.stringEncoding = self.stringEncoding;
        bodyPart.headers = self.headers;
        bodyPart.bodyContentLength = self.bodyContentLength;
        bodyPart.body = self.body;
        bodyPart.boundary = self.boundary;
    
        return bodyPart;
    }
    

    Я просто добавил boundary в список свойств, которые необходимо копировать при копировании объекта.

  3. Не связанный с вашим первоначальным вопросом, у вас есть строка, в которой говорится:

    NSData *data = UIImageJPEGRepresentation([UIImage imageNamed:@"oeffnungszeiten.jpg"], 1.0);
    

    Этот процесс кругового обхода через UIImage не вернет тот же объект, что и исходный файл. Во-первых, потенциально он может быть намного больше исходного файла, учитывая, что вы используете коэффициент качества 1.0. Вы также удаляете любые метаданные (например, дату съемки, используемую камеру, настройки камеры и т. д.).

    Если это ваше намерение, все в порядке, но обычно вы просто отправляете исходный JPEG:

    NSURL *imageFileURL = [[NSBundle mainBundle] URLForResource:@"oeffnungszeiten" withExtension:@"jpg"];
    NSData *data = [NSData dataWithContentsOfURL:imageFileURL];
    
person Rob    schedule 15.03.2014
comment
Привет Роб. Прежде всего, я должен поблагодарить вас за ваше время и подробный ответ. Хорошо. Это была комбинация из пункта 1 и 3. Никаких проблем с аутентификацией. БЛАГОДАРНОСТЬ! - person Raphael; 16.03.2014