2013-01-30 9 views
6

W mojej aplikacji mogę pobrać pliki multimedialne z Internetu. Zwykle używam metody pobierania, odszyfrowywania i zapisywania pliku na IsolatedStorage w celu pobrania, odszyfrowania i zapisania metody WebClient.OpenReadCompleted. To działa dobrze i wyglądał tak:Pobierz plik w kawałkach (Windows Phone)

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 
       } 
      } 

Ale po jakimś dochodzeniu okazało się, że z dużymi plikami (dla mnie to więcej niż 100 MB) Dostaję OutOfMemory wyjątku podczas pobierania tego pliku. Przypuszczam, że to dlatego, że WebClient.OpenReadCompleted ładuje cały strumień do pamięci RAM i dławików ... I będę potrzebować więcej pamięci do odszyfrowania tego strumienia.

Po kolejnym dochodzeniu, znalazłem sposób dzielenia dużego pliku na porcje po zdarzeniu OpenReadCompleted podczas zapisywania tego pliku do IsolatedStorage (lub odszyfrowywania, a następnie zapisywania w mojej pamięci), ale to pomogłoby tylko w części problemu. Głównym problemem jest zapobieganie dławikom telefonicznym podczas procesu pobierania. Czy istnieje sposób na pobranie dużego pliku w porcjach? Następnie mógłbym użyć znalezionego rozwiązania do przejścia przez proces odszyfrowywania. (I nadal będę musiał znaleźć sposób, aby załadować taki duży plik do MediaElement, ale to byłoby kolejne pytanie)


Odpowiedź:

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)); 
           } 
          } 
         } 
        } 
       } 
      } 
+0

pytałem ty na to pytanie? http://stackoverflow.com/questions/14600426/how-to-download-and-save-large-1gb-video-files-from-web-into-windows-phone-8/14602104#14602104 Spójrz na moje Odpowiedź –

+0

@Hermit, nie, nie zauważyłem tego, bo nie mam Windows-phone-8 w moich ulubionych tagach. Niestety, twoja odpowiedź nie pomaga mi, teoretycznie wiem, co powinienem zrobić :) Pytanie brzmi "Jak?" – Olter

+0

może to pomoże http://stackoverflow.com/questions/5659189/how-to-split-a-large-file-into-chunks-in-c –

Odpowiedz

4

Aby pobrać plik w kawałki you” Będę musiał wykonać wiele żądań. Jeden za każdą porcję.
Niestety nie można powiedzieć "pobierz ten plik i zwróć go w rozmiarze X";

Zakładając, że serwer go obsługuje, można użyć nagłówka HTTP Range, aby określić, które bajty pliku powinien zwrócić serwer w odpowiedzi na żądanie.
Następnie można wykonać wiele żądań, aby plik został podzielony na części, a następnie odłożyć wszystkie razem na urządzenie. Najprawdopodobniej najprościej będzie wykonać sekwencyjne połączenia i rozpocząć następną, gdy już masz i zweryfikowałeś poprzedni fragment.

Takie podejście ułatwia wznowienie pobierania po powrocie użytkownika do aplikacji. Wystarczy spojrzeć, ile zostało pobrane wcześniej, a następnie uzyskać następny fragment.

Napisałem aplikację, która pobiera filmy (do 2,6 GB) w porcjach 64K, a następnie odtwarza je z IsolatedStorage za pomocą MediaPlayerLauncher. Odtwarzanie za pomocą MediaElement również powinno działać, ale nie sprawdziłem. Możesz to przetestować, ładując duży plik bezpośrednio do IsolatedStorage (za pomocą Isolated Storage Explorer lub podobnego) i sprawdź implikacje związane z odtwarzaniem w pamięci w ten sposób.

1

Potwierdzone: Można użyć BackgroundTransferRequest pobierania plików multi-GB, ale należy ustawić TransferPreferences do None wymusić pobieranie się zdarzyć po podłączeniu do zewnętrznego źródła zasilania i podczas połączenia z Wi-Fi, w przeciwnym razie BackgroundTransferRequest będzie zawieść.


Zastanawiam się, czy można użyć obiektu BackgroundTransferRequest, aby pobrać duże pliki i pozwolić telefonowi martwić się szczegółami implementacji? Dokumentacja wydaje się sugerować, że pliki do pobrania ponad 100 MB są możliwe, a czasownik "Range" jest zarezerwowany na własny użytek, więc prawdopodobnie używa tego automatycznie, jeśli może za kulisami.

Z dokumentacji dotyczącej plików ponad 100 MB:

przypadku plików większych niż 100 MB, należy ustawić TransferPreferences właściwość przekazywania sobie równych lub przeniesienia się nie powiedzie. Jeśli wykonasz nie znając rozmiaru przelewu i możliwe, że może on przekroczyć ten limit, powinieneś ustawić wartość na None, co oznacza, że ​​transfer będzie kontynuowany tylko wtedy, gdy telefon jest podłączony do zewnętrznego źródła i połączenie Wi-Fi.

Z dokumentacji odnośnie użycia czasownika „zasięg”:

Obiekt nagłówki BackgroundTransferRequest obiektu służy ustawić nagłówki HTTP dla żądania transferu. Następujące nagłówki są zarezerwowane do użytku przez system i nie mogą być używane przez wywoływanie aplikacji . Dodanie jednego z następujących nagłówkami do zbierania Główki spowoduje NotSupportedException być generowany, gdy metoda dodawania (BackgroundTransferRequest) stosuje się w kolejce Przeniesienie żądanie:

  • Jeśli Modified-Od
  • If -nie-Match
  • Jeśli-klasy
  • Zakres
  • ile-Modified-Since

Oto dokumentacja: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202955(v=vs.105).aspx