NSURLSessionUploadTask не передает файл в php-скрипт

РЕДАКТИРОВАТЬ: Хорошо, я просто установил для заголовка типа содержимого значение multipart/form-data без разницы. Мой оригинальный вопрос ниже:


Это мой первый вопрос о переполнении стека, надеюсь, я делаю это правильно.

Я только изучаю Objective-C, недавно пройдя онлайн-версию Стэнфордского курса. Я практически ничего не знаю о php и html. PHP-скрипт и html, которые я использую, в основном скопированы из учебника. Obj-C имеет для меня больше смысла.

Проблема:

У меня есть php-скрипт. Он загружает файл изображения. Корректно работает при вызове из html файла в той же папке на сервере. Я пытаюсь заставить тот же скрипт работать, когда он вызывается из моего объекта obj-c. Вроде запускается, возвращает 200, obj-c вызывает php, но в онлайн-папке файл не появляется.

Кажется, об этом очень мало в сети, так как это было представлено только в ios7. Я не нашел примеров, связанных с загрузкой файлов, все они имеют дело с загрузкой и просто говорят, что загрузка аналогична. То, что я сделал, кажется, удовлетворяет любым учебникам, которые я нашел.

Я знаю следующее:

  • php работает, и файл загружается, когда он вызывается из html файла на сервере
  • obj-c определенно вызывает php-скрипт (я написал журнал (используя file_put_contents) в php, который подтверждает, что скрипт вызывается при запуске obj-c)
  • obj-c определенно загружает файл изображения (если я использую метод делегата в obj-c, он показывает ход загрузки)
  • но php-скрипт не получает файл (протокол, который я записал в php, показывает, что $_FILES не имеет значения при вызове из obj-c. При вызове из html он работает как положено)
  • Я только что отредактировал php, чтобы зарегистрировать заголовки, которые он получает, и он получает Content-Length файла изображения.

Things that may be important:

  • Я не добавляю никаких заголовков html, ни в одном учебнике, который я видел, не говорится, что я должен (с NSURLSessionUploadTask), я предполагаю, что NSURLSessionUploadTask разбирается с этим для вас? Или это моя проблема?
  • [описание ответа] возвращает 200, цитата: {URL: (URL-адрес сценария PHP)} {код состояния: 200, заголовки { Connection = "Keep-Alive"; "Тип контента" = "текст/html"; Дата = "Чт, 16 января 2014 г., 19:58:10 по Гринвичу"; "Keep-Alive" = "timeout=5, max=100"; Сервер = Апач; "Передача-Кодирование" = Личность; } }
  • html указывает enctype="multipart/form-data", возможно, это нужно где-то обработать в моем obj-c?
  • Я пока запускаю его только с симулятора.
  • любая помощь будет безмерно оценена! спасибо :)
  • редактировать, я только что отредактировал приведенный ниже код, чтобы показать [запрос setHTTPMethod:@"POST"] вместо [запрос setHTTPMethod:@"PUSH"], который у меня был изначально, но это не вносит изменений.

Вот цель C

- (void) uploadFile: (NSURL*) localURL toRemoteURL: (NSURL*) phpScriptURL
{
    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:phpScriptURL];
    [request setHTTPMethod:@"POST"];
    NSURLSessionUploadTask* uploadTask = [defaultSession uploadTaskWithRequest:request fromFile:localURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
        if (error == nil)
        {
            NSLog(@"NSURLresponse =%@",  [response description]);
            // do something !!!
        } else
        {
            //handle error
        }
        [defaultSession invalidateAndCancel];
    }];

    self.imageView.image = [UIImage imageWithContentsOfFile:localURL.path]; //to confirm localURL is correct

    [uploadTask resume];
}

а вот и скрипт PHP, который находится на сервере

<?php

        $file = 'log.txt';
        $current = file_get_contents($file);
        $current .= $_FILES["file"]["name"]." is being uploaded. ";  //should write the name of the file to log.txt 
        file_put_contents($file, $current);


    ini_set('display_errors',1);
    error_reporting(E_ALL);

   $allowedExts = array("gif", "jpeg", "jpg", "png");
   $temp = explode(".", $_FILES["file"]["name"]);
   $extension = end($temp);   

    if ((($_FILES["file"]["type"] == "image/gif")
    || ($_FILES["file"]["type"] == "image/jpeg")
    || ($_FILES["file"]["type"] == "image/jpg")
    || ($_FILES["file"]["type"] == "image/pjpeg")
    || ($_FILES["file"]["type"] == "image/x-png")
    || ($_FILES["file"]["type"] == "image/png"))
    //&& ($_FILES["file"]["size"] < 100000) //commented out for error checking
    && in_array($extension, $allowedExts))
      {
      if ($_FILES["file"]["error"] > 0)
        {
        echo "Return Code: " . $_FILES["file"]["error"] . "<br>";
        }
      else
        {
            echo "Upload: " . $_FILES["file"]["name"] . "<br>";
            echo "Type: " . $_FILES["file"]["type"] . "<br>";
            echo "Size: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
            echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br>";

            if (file_exists("upload/" . $_FILES["file"]["name"]))
              {
              echo $_FILES["file"]["name"] . " already exists. ";
              }
            else
              {
              if (move_uploaded_file($_FILES["file"]["tmp_name"],
              "upload/" . $_FILES["file"]["name"]))
              {
                echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
              }
              else
              {
                echo "Error saving to: " . "upload/" . $_FILES["file"]["name"];
              }

            }
        }
      }
    else
      {
      echo "Invalid file";
      }

?>

а вот HTML-файл, который работает, как и ожидалось, при вызове того же скрипта

<html>
<body>

    <form action="ios_upload.php" method="post"
    enctype="multipart/form-data">
    <label for="file">Filename:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="Submit">
    </form>

</body>


person narco    schedule 16.01.2014    source источник
comment
Вы проверили php.net/move_uploaded_file? Получение файла довольно просто с php, комбинируя суперглобальный $_FILES и move_uploaded_file (вместо файлового потока, который вы делаете). А также, где вы указываете, что входное имя файла в вашей цели c?   -  person Martin Sommervold    schedule 17.01.2014
comment
Спасибо за ваш ответ :) Да, я посмотрел на это. Я также пробовал это с теми же результатами (извините, я не уверен, как форматировать код в ответе): $file = $_FILES[file][tmp_name]; $ новый файл = загрузить/ . $_FILES[файл][имя]; if (!copy($file, $newfile)) { echo не удалось скопировать $file...\n; } И я не уверен, что вы имеете в виду под своим вторым вопросом?   -  person narco    schedule 17.01.2014
comment
@Martin Sommervold Я просто снова просматривал ваш комментарий, так как большую часть недели пытался решить эту проблему (и никто больше не ответил). Возможно, я неправильно понял ваш комментарий. Вы говорите, что мой php неправильный? Мой объект не загружается в $_FILES? заранее спасибо   -  person narco    schedule 23.01.2014
comment
@Martin Sommervold ах, больше недели спустя, и я, наконец, понял комментарий к файлу. Спасибо. В итоге я написал код, чтобы сделать все по-другому, поэтому я не могу быть уверен, что ваше решение полностью решило проблемы, поэтому я не выбрал его в качестве правильного ответа. Но спасибо   -  person narco    schedule 24.01.2014
comment
Вы опередили меня на 37 секунд :) нет, суперглобальный $_FILES всегда будет получать загрузку, так что это правильное место для поиска. Однако мой вопрос был о том, где в вашем obj-c вы указываете свой ввод. Например, вы можете ввести ‹input typefile name=holybatman› и получить этот файл как $_FILES['holybatman']. Я просто не мог обнаружить «святого бэтмена» в вашем obj-c, вот и все, и мне было интересно, не пытались ли вы просто захватить неправильный ключ :) do a var_dump($_FILES); чтобы увидеть, как выглядят ваши сообщения о файлах :) рад, что вы решили это!   -  person Martin Sommervold    schedule 24.01.2014
comment
@narco У меня похожие проблемы. Не могли бы вы дать краткое описание вашего решения этой проблемы? Спасибо!   -  person gberginc    schedule 19.02.2014
comment
Мое решение было в основном основано на двух частях. Мартин Соммерволдс ответил, что мой php неверен, и тогда мне пришлось научиться правильно создавать http POST, о чем я узнал в основном из этой статьи: faqs.org/rfcs/rfc1867.html Вам необходимо установить границу (описанную в этой статье), а затем построчно собрать запрос POST. Я обнаружил, что все должно быть идеально, чтобы работать, поэтому может потребоваться небольшая настройка. Я настроил PHP для создания массива результатов JSON и вернул ему несколько шагов, чтобы выяснить, что происходит. Надеюсь, это поможет.   -  person narco    schedule 22.02.2014
comment
Я только что узнал, что эта статья не подходит для загрузки нескольких файлов. На самом деле это делается с использованием только одной границы. Но статья верна для одиночных файлов.   -  person narco    schedule 15.03.2014
comment
У меня аналогичная проблема в том, что NSURLSessionUploadTask, похоже, неправильно передает данные на сервер, поэтому $_FILES отображается пустым. Конечно, полное построение тела POST по частям (предполагается) становится излишним из-за существования [NSURLSessionUploadTask uploadTaskWithRequest:fromFile:completionHandler:]?   -  person GTF    schedule 30.09.2014
comment
Кто-нибудь решил??? Я сталкиваюсь с этой проблемой и пытаюсь решить ее с прошлой недели. Пожалуйста, помогите мне, опубликуйте здесь рабочий код. Спасибо   -  person va05    schedule 10.12.2014


Ответы (1)


Я только что ответил на этот же вопрос здесь: https://stackoverflow.com/a/28269901/4518324

По сути, файл загружается на сервер в теле запроса как двоичный файл.

Чтобы сохранить этот файл в PHP, вы можете просто получить тело запроса и сохранить его в файл.

Ваш код должен выглядеть примерно так:

Код Objective-C:

- (void) uploadFile: (NSURL*) localURL toRemoteURL: (NSURL*) phpScriptURL
{
    // Create the Request
     NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:phpScriptURL];
    [request setHTTPMethod:@"POST"];

    // Configure the NSURL Session
     NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.upload"];
    [sessionConfig setHTTPMaximumConnectionsPerHost: 1];

     NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:nil];

     NSURLSessionUploadTask* uploadTask = [defaultSession uploadTaskWithRequest:request fromFile:localURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
         if (error == nil)
         {
              NSLog(@"NSURLresponse =%@",  [response description]);
              // do something !!!
         } else
         {
             //handle error
         }
         [defaultSession invalidateAndCancel];
     }];

      self.imageView.image = [UIImage imageWithContentsOfFile:localURL.path]; //to confirm localURL is correct

     [uploadTask resume];
}

Код PHP:

<?php
    // Get the Request body
    $request_body = @file_get_contents('php://input');

    // Get some information on the file
    $file_info = new finfo(FILEINFO_MIME);

    // Extract the mime type
    $mime_type = $file_info->buffer($request_body);

    // Logic to deal with the type returned
    switch($mime_type) 
    {
        case "image/gif; charset=binary":
            // Create filepath
             $file = "upload/image.gif";

            // Write the request body to file
            file_put_contents($file, $request_body);

            break;

        case "image/png; charset=binary":
            // Create filepath
             $file = "upload/image.png";

            // Write the request body to file
            file_put_contents($file, $request_body);

            break;

        default:
            // Handle wrong file type here
            echo $mime_type;
    }
?>

Я написал пример кода для записи аудио и его загрузки на сервер здесь: https://github.com/gingofthesouth/Audio-Recording-Playback-and-Upload

Он показывает код от сохранения на устройстве iOS до загрузки и сохранения на сервер.

Надеюсь, это поможет.

person Ernest Cunningham    schedule 02.02.2015
comment
У меня возникли проблемы с uploadTaskWithRequest:fromFile:completion: ваш пример кода фактически выдает исключение. блок завершения не поддерживается, если вы используете фоновый сеанс NSURLSession. Я работал над тем, чтобы uploadTaskWithRequest работал строго на переднем плане - и заголовки выходят, но не файл. Начинаю подозревать, что метод с блоком завершения просто сломан - person Todd; 10.12.2015