Pasek postępu ściąganego załącznika

0

Chciałbym dla ściąganego załącznika wyświetlić pasek postępu pokazujący jaki jest procent pobierania załącznika. Problem jednak w tym, że funkcja getInputStream pobiera chyba cały załącznik za jednym razem:

InputStream in = bodyPart.getInputStream();

zatrzymując cały wątek do czasu, aż nie skończy pobierać całego tego załącznika, a więc w tym wątku nie ma możliwości wyświetlenia paska postępu.

Może jednak jest jakiś sposób? Być może rozwiązaniem byłoby w trakcie pobierania załącznika spróbować dostać się do zmiennej "in" z innego wątku? A może w JavaMail jest jakiś inny sposób na pobranie załącznika niż za pomocą funkcji getInputStream()?

1

getInputStream może co najwyżej zatrzymać na czas łączenia - ale wtedy pasek postępu nie ma sensu..
co się później stanie to zależy od Ciebie... po prostu zczytując kolejne bajty dodawaj value do progressbaru....

1

Pierwsze pytanie co to jest bodyPart? Jakiej klasy jest to obiekt.

zamiast metody read() możesz użyć read(byte[]) i znając wielkość strumienia można wtedy:

  • odczytujesz ileś tam bajtów do tablicy.
  • aktualizujesz pasek postępu.
0
Kerai napisał(a)

getInputStream może co najwyżej zatrzymać na czas łączenia - ale wtedy pasek postępu nie ma sensu..
co się później stanie to zależy od Ciebie... po prostu zczytując kolejne bajty dodawaj value do progressbaru....

No ale właśnie problem w tym, że metoda:

InputStream in = p.getInputStream();

(gdzie p to zmienna typu Part - http://javamail.kenai.com/nonav/javadocs/javax/mail/Part.html) wczytuje cały załącznik za jednym razem (a nie partiami) do zmiennej "in" (typu InputStream) i do czasu aż cały załącznik nie zostanie pobrany cały wątek jest zablokowany. Wyświetlania paska postępu po wykonania tej funkcji (czyli w momencie gdy cały załącznik jest już w pamięci RAM w zmiennej typu "in") nie ma chyba sensu.

0
Koziołek napisał(a)

Pierwsze pytanie co to jest bodyPart? Jakiej klasy jest to obiekt.

zamiast metody read() możesz użyć read(byte[]) i znając wielkość strumienia można wtedy:

  • odczytujesz ileś tam bajtów do tablicy.
  • aktualizujesz pasek postępu.

W pierwszym swoim poście podałem kod z jakiegoś przykładu z Internetu, u mnie jest dokładnie taki taki:

InputStream in = p.getInputStream();

tak jak wcześniej pisałem p jest zmienną typu Part.

Ja nawet tak robię, że używam in.read(readData), gdzie readData to zmienna tablica bajtów. Jednak to nic nie daje, bo trzeba najpierw poczekać aż cały załącznik za jednym razem zostanie ściągnięty przez p.getInputStream().

Chyba, że po prostu może ja coś źle robię lub coś źle rozumiem? :-)

Czy jest więc jakiś sposób, żeby taki pasek postępu ściąganego załącznika wyświetlić? Informacja, której mi brakuje to aktualny rozmiar zmiennej "in" podczas wykonywania "długo trwającej" metody p.getInputStream(). Wtedy mógłbym z jakiegoś innego wątku (np. timera uruchamianego co ileś milisekund) aktualizować ten pasek postępu

1

Jeżeli getInputStream blokuje Ci cały wątek to jest coś nie tak z implementacją ;]
Możesz zawsze wykorzystać klasę URLConnection -> tam getInputStream zwróci Ci strumień z którego będziesz sobie pobierał dane dowolnymi paczkami. URLConnection pozwala Ci również na podgląd nagłówków, a tam z kolei jest coś takiego jak kontentLength. Czyli wtedy wiesz jak duży jest załącznik, i wiesz ile bajtów pobierasz przy każdej iteracji -> pasek postępu bezproblemowo do implementacji.
W moim programie mam cały manager pobierania (100 plików równocześnie np).Każdy ma progressBar implementowany na zasadzie którą Ci podałem powyże. Korzystam z URLConnection i nie mam z tym żadnych problemów czy blokad.

0

Czy da się wykorzystać klasę URLConnection do pobrania załącznika maila?

1

Btw według dokumentacji istnieje kolejna przesłanka mówiąca o tym, że getInputStream() nie ma prawa blokować aktualnego wątku.(tak wiem, nie zmienia to faktu, że u Ciebie blokuje). Otóż założeniem metody getInputStream() jest wywołanie tak samo brzmiącej metody ale obiektu DataHandler.

http://docs.oracle.com/cd/E17802_01/j2se/javase/technologies/desktop/javabeans/glasgow/javadocs/javax/activation/DataHandler.html?is-external=true#getInputStream%28%29

Tam natomiast wykorzystany jest PipedOutputStream. Co to oznacza? Ano to, że dane są pobierane tak czy inaczej, jednak w osobnym wątku.

Get the InputStream for this object.

For DataHandlers instantiated with a DataSource, the DataHandler calls the DataSource.getInputStream method and returns the result to the caller.

For DataHandlers instantiated with an Object, the DataHandler first attempts to find a DataContentHandler for the Object. If the DataHandler can not find a DataContentHandler for this MIME type, it throws an UnsupportedDataTypeException. If it is successful, it creates a pipe and a thread. The thread uses the DataContentHandler's writeTo method to write the stream data into one end of the pipe. The other end of the pipe is returned to the caller. Because a thread is created to copy the data, IOExceptions that may occur during the copy can not be propagated back to the caller. The result is an empty stream.

Dalej. Part jest przecież interfejsem. Gdzie masz jego implementacje? Jak ona wygląda ? Sam musiałeś ją zaimplementować - albo korzystasz z klasy o której nam nie wspomniałeś.
Spróbuj sobie też zrobić i daj znać czy dalej Ci to blokuje.

part.getDataHandler().getInputStream();
0

Okazało się, że p.getInputStream() blokował z powodu sposobu w jaki łączyłem się ze skrzynką:

store = session.getStore("imaps");
store.connect();
folder.add( (IMAPFolder)store.getFolder("INBOX") );

a w ustawieniach podawałem:

props.setProperty("mail.imaps.partialfetch", "false");

czyli wyłączone było pobieranie kawałek po kawałku.

Gdy zamiast tego łączę się w ten sposób:

folder.add( (IMAPFolder) session.getFolder(server) );

p.getInputStream() już nie blokuje i można zrobić prawidłowe paski postępu :)

Ale pojawił się pewien problem - po ściągnięciu 73% pliku pojawią się błąd:

BASE64Decoder: Error in encoded stream: needed 4 valid base64 characters but only got 1 before EOF, the 10 most recent characters were: "DUp09Nn/Xd"

Wydaje mi się, że dobre wyjaśnienie jest tutaj: http://stackoverflow.com/a/5292975/909539.
Błąd jest właśnie przez to, że pobieranie załącznika jest teraz wykonywanie kawałek po kawałku, a niektóre serwery IMAP nie mają prawidłowo zaimplementowanego takiego pobierania kawałek po kawałku.

Żeby to obejść trzeba wrócić chyba niestety do tego, czego korzystałem do tej pory, czyli:

store = session.getStore("imaps");
store.connect();
folder.add( (IMAPFolder)store.getFolder("INBOX") );

oraz ustawić, żeby załącznik nie był pobierany w częściach:

props.setProperty("mail.imaps.partialfetch", "false");

wtedy nie ma już tego błędu.

Szkoda, bo te paski postępu by mi się przydały :) Chyba, że jest jakiś inny sposób? Jedyne co mi przychodzi do głowy, to poszperać w kodzie źródłowym tej metody getInputStream() i może tam by się udało dotrzeć do aktualnego rozmiaru ściąganego załącznika?

Antoniossss, zmienna p jest tworzona w ten sposób:

Object content = message.getContent();
if(content instanceof Multipart){
	Multipart mp = (Multipart) content;
	int count = mp.getCount();
	for(int k=0;k<count;k++){
             Part p = mp.getBodyPart(k);
........

Antoniossss, dodanie getDataHandler():

part.getDataHandler().getInputStream();

nic nie zmieniło.

1 użytkowników online, w tym zalogowanych: 0, gości: 1