IOS NSURLSession Background Загрузка как загружать большие файлы в фоновом режиме?

Я хочу загружать большие файлы (видео) размером 2-3 ГБ на сервер в фоновом режиме со следующими требованиями

Первый метод

  1. Загрузка должна возобновиться, если подключение к Интернету потеряно и снова подключено

  2. Загрузка должна продолжаться, даже если приложение находится в фоновом режиме.

  3. Загрузка должна возобновиться, если пользователь убьет приложение и вернется

для вышеуказанных функций, которые я реализовал

  1. Пользователь выбирает файл

  2. Разделите файл на куски по 1 МБ и сохраните все куски на диске как файл.

  3. Создайте задачу загрузки для каждого файла фрагмента и добавьте файл в фоновый сеанс.

Вышеупомянутый метод работает, но в некоторых случаях не работает

  1. Если размер файла превышает 1 ГБ, создание фрагментов и запись фрагментов на диск вызывает исключение памяти

  2. Если я хочу загрузить файл размером 1 ГБ, мне нужно дополнительное пространство 1 ГБ для создания фрагментов.

Второй метод

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

Мой вопрос: как лучше всего загружать большие файлы в фоновом режиме, помня обо всех этих моментах?

Я знаю, что уже задано несколько вопросов такого типа, но ни один из них не отвечает на мой вопрос.

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

Обновлять

Я использую следующий код для создания фрагментов. Код находится в Xamarin.IOS, но я в порядке, если кто-то предоставит объяснение в Objective C или Swift.

public static void SplitFileInChunks( UploadFileInfo UploadFile )
{
        int i = -1;

        long chunkSize = UploadHelper.chunkSize;
        nuint dataLength = (System.nuint)chunkSize; 

        //var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        string directoryPath = UploadHelper.UploadsDirectory;

        int chunkCount = 0;
        NSFileHandle fileHandleRead = NSFileHandle.OpenRead(UploadFile.FilePath);

        fileHandleRead.ReadInBackground();
        //fileHandleRead.WaitForDataInBackground();

        if (fileHandleRead == null)
            return;

        do
        {
            i++;
            ulong index = (ulong)(i * chunkSize);

            var filePath = Path.Combine(directoryPath, UploadFile.ContentGuide + "" + i.ToString());
            //fileHandleRead.SeekToFileOffset(index);

            NSData data = fileHandleRead.ReadDataOfLength(dataLength );
            Console.WriteLine(UploadFile.FileStatus);

            if (data.Length <= 0)
                continue;

            NSFileManager.DefaultManager.CreateFile(filePath, data, attr: null);


            NSError error;
            //data.Save(filePath, true, out error);

            chunkCount++;

            Console.WriteLine("Data Lenght" + data.Length);
            data.Dispose();
            Console.WriteLine("Chunk " + i);
        }

        while ( i * chunkSize <= UploadFile.Size && UploadFile.FileStatus != UploadFileStatus.Aborted );

        fileHandleRead.CloseFile();
        fileHandleRead.Dispose();

        Console.WriteLine("All Files Written sucessuflly");
        UploadFile.TotalChunksCount = chunkCount;

    }

person Fahad Rehman    schedule 24.04.2017    source источник
comment
да, я использую фоновый сеанс, но для сохранения фрагментов на диске мне нужно дополнительное пространство, потому что NSURLsession в фоновом режиме работает только с NSURL.FromFileName, насколько я понимаю   -  person Fahad Rehman    schedule 24.04.2017
comment
так есть ли другой способ преодолеть это, например, загрузить исходный файл, а также поддержать возобновление   -  person Fahad Rehman    schedule 24.04.2017
comment
Я пытался добавить это в блок Auto ReleasePool, но безуспешно.   -  person Fahad Rehman    schedule 24.04.2017
comment
Давайте продолжим обсуждение в чате.   -  person Rob    schedule 24.04.2017


Ответы (1)


Это, безусловно, сработает, но если вы контролируете программное обеспечение на другом конце, вы можете сделать лучше:

  • На стороне сервера:

    • Provide an upload start endpoint (URL) that merely provides a unique ID.
    • Укажите конечную точку загрузки данных, которая принимает уникальный идентификатор, тело POST и необязательное начальное смещение в байтах и ​​записывает данные во временный файл на сервере.
    • Предоставьте конечную точку состояния загрузки, которая принимает уникальный идентификатор и возвращает объем данных, сохраненных на диске до сих пор.
    • Укажите завершенную конечную точку загрузки.
  • На стороне клиента:

    • Call the start endpoint and get an ID for the upload.
    • Вызовите конечную точку загрузки данных и начните отправку данных.
    • В случае сбоя вызовите конечную точку состояния загрузки, чтобы узнать, сколько данных на самом деле получил сервер.
    • Затем вызовите конечную точку данных и начните отправлять данные с этого смещения, сообщая серверу, с чего вы начинаете. (На сервере всегда начинайте запись с этого смещения в файл, даже если с тех пор длина увеличилась, просто на всякий случай.)
    • По завершении вызовите конечную точку завершения загрузки.

Эта архитектура также позволяет довольно легко отображать строку состояния.

person dgatwood    schedule 24.04.2017
comment
К сожалению, если ваши данные полностью не помещаются в ОЗУ, я не думаю, что можно избежать создания усеченной копии файла. Если вы предварительно разделите его на части, вы можете создать усеченную копию текущего фрагмента. В любом случае ваше приложение должно быть разбужено, обработать ошибку и перезапустить передачу. В какой-то момент вы начнете получать штрафы от NSURLSession, если ваше приложение слишком часто просыпается, поэтому фрагментация потенциально хуже, чем нефункция. - person dgatwood; 25.04.2017
comment
На самом деле, теперь, когда я думаю об этом, вы можете использовать NSURLSessionDownloadTask на основе потоковой загрузки, но это зависит от того, правильно ли обрабатывает это демон. Сначала попробуйте этот подход, но если он не сработает, вам придется создать усеченную копию файла. - person dgatwood; 25.04.2017
comment
Я не понял, что означает первый подход, во-вторых, я попытался сделать усеченную копию с помощью этого подхода stackoverflow.com/questions/28620522/, но, к сожалению, приложение вылетает, если размер файла составляет около 2-3 ГБ, и я загрузил только несколько байтов - person Fahad Rehman; 25.04.2017
comment
При создании задачи загрузки вы можете указать объект NSURLRequest, содержащий файл NSStream. Если вы используете getBoundStreamsWithBufferSize:inputStream:outputStream:, ваше приложение может контролировать предоставление данных тела. Затем считывайте данные из файла по несколько мегабайт за раз и записывайте их в выходной поток, а входной поток передайте при создании файла NSURLRequest. По сути, откройте поток для записи, прежде чем пытаться отправить данные, подождите, пока вы не получите событие свободного места, запишите некоторые данные и проверьте возвращаемое значение, чтобы определить, сколько данных было фактически отправлено. - person dgatwood; 25.04.2017
comment
Обязательно закройте поток после записи последнего фрагмента, иначе загрузка никогда не завершится. - person dgatwood; 25.04.2017