Скачать файл по частям (Windows Phone)

В моем приложении я могу загрузить некоторые медиафайлы из Интернета. Обычно я использовал метод WebClient.OpenReadCompleted для загрузки, расшифровки и сохранения файла в изолированном хранилище. Он работал хорошо и выглядел так:

 private void downloadedSong_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e, SomeOtherValues someOtherValues) // delegate, uses additional values
        {
            // Some preparations

                try
                {
                   if (e.Result != null)
                   {
                    using (isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        // working with the gained stream, decryption
                        // saving the decrypted file to isolatedStorage
                        isolatedStorageFileStream = new IsolatedStorageFileStream("SomeFileNameHere", FileMode.OpenOrCreate, isolatedStorageFile);
                        // and use it for MediaElement
                        mediaElement.SetSource(isolatedStorageFileStream);
                        mediaElement.Position = new TimeSpan(0);
                        mediaElement.MediaOpened += new RoutedEventHandler(mediaFile_MediaOpened);

                        // and some other work
                     }
                    }
                 }
                 catch(Exception ex) 
                 {
                  // try/catch stuff
                 }
           }

Но после некоторого расследования я обнаружил, что с большими файлами (для меня это более 100 МБ) я получаю исключение OutOfMemory при загрузке этого файла. Я полагаю, это потому, что WebClient.OpenReadCompleted загружает весь поток в ОЗУ и подавляется ... И мне понадобится больше памяти для расшифровки этого потока.

После еще одного расследования я нашел, как разделить большой файл на куски после события OpenReadCompleted при сохранении этого файла в изолированном хранилище (или расшифровке с последующим сохранением в моем случае), но это помогло бы только с частью проблемы ... проблема в том, как предотвратить блокировку телефона во время загрузки. Есть ли способ загружать большие файлы по частям? Тогда я мог бы использовать найденное решение для прохождения процесса дешифрования. (и все же мне нужно было бы найти способ загрузить такой большой файл в mediaElement, но это был бы другой вопрос)


Отвечать:

 private WebHeaderCollection headers;
 private int iterator = 0;
 private int delta = 1048576;
 private string savedFile = "testFile.mp3";

 // some preparations
 // Start downloading first piece


using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        if (isolatedStorageFile.FileExists(savedFile))
                            isolatedStorageFile.DeleteFile(savedFile);
                    }

                    headers = new WebHeaderCollection();
                    headers[HttpRequestHeader.Range] = "bytes=" + iterator.ToString() + '-' + (iterator + delta).ToString();
                    webClientReadCompleted = new WebClient();
                    webClientReadCompleted.Headers = headers;
                    webClientReadCompleted.OpenReadCompleted += downloadedSong_OpenReadCompleted;
                    webClientReadCompleted.OpenReadAsync(new Uri(song.Link));
                    // song.Link was given earlier

private void downloadedSong_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            try
            {
                if (e.Cancelled == false)
                {
                    if (e.Result != null)
                    {
                        ((WebClient)sender).OpenReadCompleted -= downloadedSong_OpenReadCompleted;

                        using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
                        {
                            using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(savedFile, FileMode.Append, FileAccess.Write, myIsolatedStorage))
                            {
                                int mediaFileLength = (int)e.Result.Length;
                                byte[] byteFile = new byte[mediaFileLength];
                                e.Result.Read(byteFile, 0, byteFile.Length);
                                fileStream.Write(byteFile, 0, byteFile.Length); 

                                // If there's something left, download it recursively
                                if (byteFile.Length > delta)
                                {
                                    iterator = iterator + delta + 1;

                                    headers = new WebHeaderCollection();
                                    headers[HttpRequestHeader.Range] = "bytes=" + iterator.ToString() + '-' + (iterator + delta).ToString();
                                    webClientReadCompleted.Headers = headers;
                                    webClientReadCompleted.OpenReadCompleted += downloadedSong_OpenReadCompleted;
                                    webClientReadCompleted.OpenReadAsync(new Uri(song.Link));
                                }
                            }
                        }
                    }
                }
            }

person Olter    schedule 30.01.2013    source источник
comment
ты задавал этот вопрос? stackoverflow.com/questions/14600426/ Посмотрите мой ответ   -  person Hermit Dave    schedule 30.01.2013
comment
@Hermit, нет, я даже не заметил этого, потому что у меня нет windows-phone-8 в моих любимых тегах. К сожалению, ваш ответ мне не помогает, теоретически я знаю, что мне делать :) Вопрос как?   -  person Olter    schedule 30.01.2013
comment
возможно, это поможет stackoverflow.com/questions/5659189/   -  person Hermit Dave    schedule 30.01.2013
comment
Нет, это система передачи файлов, я не могу управлять передачей файлов таким образом. Это должны быть какие-то методы из родных классов WP7, такие как WebClient или httpwebrequest или что-то еще.   -  person Olter    schedule 30.01.2013
comment
ни один стандартный класс / метод не делает то, что вы хотите. если вы хотите использовать патроны, вам придется поискать решение.   -  person Hermit Dave    schedule 30.01.2013


Ответы (2)


Чтобы загрузить файл по частям, вам нужно будет сделать несколько запросов. По одному на каждый фрагмент.
К сожалению, невозможно сказать «достань мне этот файл и верни его фрагментами размером X»;

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

Такой подход упрощает возобновление загрузки, когда пользователь возвращается в приложение. Вы просто смотрите, сколько было загружено ранее, и получаете следующий кусок.

Я написал приложение, которое загружает фильмы (до 2,6 ГБ) фрагментами по 64 КБ, а затем воспроизводит их из IsolatedStorage с помощью _ 2_. Игра через MediaElement тоже должна работать, но я не проверял. Вы можете проверить это, загрузив большой файл непосредственно в IsolatedStorage (через изолированный обозреватель хранилища или аналогичный) и проверив, как влияет такая игра на память.

person Matt Lacey    schedule 30.01.2013

Подтверждено: вы можете использовать BackgroundTransferRequest для загрузки файлов размером несколько ГБ, но вы должны установить TransferPreferences на None, чтобы загрузка происходила при подключении к внешнему источнику питания и при подключении к Wi-Fi, иначе BackgroundTransferRequest не удастся.


Интересно, можно ли использовать BackgroundTransferRequest для простой загрузки больших файлов и позволить телефону беспокоиться о деталях реализации? Документация, кажется, предполагает, что загрузка файлов размером более 100 МБ возможна, а глагол «Диапазон» зарезервирован для собственного использования, поэтому он, вероятно, использует это автоматически, если это возможно за кулисами.

Из документации по файлам размером более 100 МБ:

Для файлов размером более 100 МБ необходимо установить для свойства TransferPreferences передачи значение None, иначе передача не удастся. Если вы не знаете размер передачи и возможно, что он может превысить этот предел, вам следует установить значение None, что означает, что передача будет продолжаться только тогда, когда телефон подключен к внешнему источнику питания и имеет Wi-Fi. связь.

Из документации по использованию глагола «Диапазон»:

Свойство Headers объекта BackgroundTransferRequest используется для установки заголовков HTTP для запроса на передачу. Следующие заголовки зарезервированы для использования системой и не могут использоваться вызывающими приложениями. Добавление одного из следующих заголовков в коллекцию Headers вызовет исключение NotSupportedException, когда метод Add (BackgroundTransferRequest) используется для постановки в очередь запроса на передачу:

  • If-Modified-Since
  • Если-нет-совпадение
  • Если-диапазон
  • Диапазон
  • Без изменений с момента

Вот документация: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202955%28v=vs.105%29.aspx

person Gavin    schedule 13.05.2014