Jak zapisac zalacznik(JavaMail) do bazy (JPA)

0

Witam

Wyodbrebnilem zalaczniki z wiadomosci email. Teraz mam kilka zmiennych typu InputStream przechowywujacych zawartosc owych zalacznikow.
Aby to wsadzic do bazy danych poprzez JPA musze miec obiekt SerialBlob. Utworzyc go mozna podajac w konstruktorze tablice byte[]. Ale teraz jak stworzyc taka tablice z obiektu InputStream? Przeszukalem rozne klasy IO odpowiedzialne za czytanie danych ale nigdzie nie ma funkcji, ktora zwracala by tablice byte[].

Jak to zrobic? Moze za bardzo motam i istnieje jakis inny sposob na pobranie zalacznika w innej formie niz InputStream?

Ok problem polowicznie rozwiazany.
Tzn wiem jak utworzyc obiekt SerialBlob, zapisac i odczytac z niego dane. Problemem jest natomiast wyjatek:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at javax.sql.rowset.serial.SerialBlob.<init>(SerialBlob.java:83)
        at testy.Iotest.main(Iotest.java:42)

Chce zapisywac w bazie pliki do 200 Mb. A problem z pamiecia wystepuje juz przy kilku Mb. Co moge z tym zrobic?

0

Zmienne -Xmx, -Xms dla aplikacji ustawić na jakieś ludzkie wartości. 1GB i 256MB powinno być git ;)

0

Podzialalo :-)
Tyle, ze to bedzie wykonywane po stronie serwera dla aplikacji typu multiuser. Jesli np 10 osob na raz bedzie chcialo pobrac zalacznik to makabra.

Pomyslalem, zeby zalaczniki zapisywac w bazie po podzieleniu na wiele fragmentow. Tak jak mialem to zrobic pierwotnie.
Napisalem kod testowy do dzielenia, tworzenia obiektow SerialBlob, pobierania z nich wartosci i ponownego zapisywania do pliku. Ale wychodza jakies glupoty, tzn plik test.txt nie zawiera tekstu ale puste znaki. Testujac to samo na archiwum zip, zip nie dal sie otworzyc.

public class Iotest2 {

    public static void main(String[] args){
        try {
            InputStream is = new FileInputStream(new File("booksToGet.txt"));
            int bufferSize = 65536;
            byte[] input = new byte[bufferSize];
            ArrayList<SerialBlob> blobList = new ArrayList<SerialBlob>();
            
            for(int i=0;true;i++) {
                int nb = is.read(input, 0, bufferSize);
                if(nb == -1){
                    break;
                }

                SerialBlob sb = new SerialBlob(input);
                blobList.add(sb);
            }

            System.out.println(blobList.size());

            OutputStream os = new FileOutputStream("test.txt");
            for(int i=0; i<blobList.size(); i++){
                SerialBlob sbTemp = blobList.get(i);
                InputStream isTemp = sbTemp.getBinaryStream();
                byte[] output = new byte[bufferSize];
                while(true){
                    if(bufferSize > isTemp.available()){
                        System.out.println("malo");
                    }
                    int nb = isTemp.read(input, 0, bufferSize);
                    if(nb == -1){
                        break;
                    }

                    os.write(output, 0, nb);
                }
                isTemp.close();
            }

            is.close();
            os.flush();
            os.close();
        }
        catch (SerialException ex) {
            Logger.getLogger(Iotest.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SQLException ex) {
            Logger.getLogger(Iotest.class.getName()).log(Level.SEVERE, null, ex);
        }        catch (IOException ex) {
            Logger.getLogger(Iotest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
0

@folly, zacznij używać do testów JUnita lub TestNG. Jest zdecydowanie łatwiej.

Co do dużej liczby userów to niestety trzeba będzie w razie czego postawić farmę serwerów i zbić jakiś klaster. Nie kombinuj z dzieleniem plików, bo to niepotrzebne komplikowanie sobie życia.

0

Hehe w sumie tak, ale ja chcialbym to zrobic w miare malo "pamieciozernie", zeby moc zaprezentowac dzialajaca aplikacje na zwyklym serwerku.

Na upartego to co w tym kodzie od dzielenia/laczenia plikow jest zle?

UPDATE: hehe plik ma 34 kb a po odtworzeniu ma 64 kb czyli tyle ile rozmiar buffora. Wynika z tego, ze na sile zapisuje pelne 64kb porcje.

0

Hm... można zrobić inaczej. Zamiast pchać pliki do bazy zapisujesz je na dysk pod unikalnymi nazwami (mogę dać generator nazw jako gotowiec w serwisie). W bazie zapisujesz informacje o pliku. nazwę, długość, do kogo należy, czas utworzenie, z jakiego maila pochodzi. Następnie serwujesz plik z dysku, a nie z bazy. Chyba tak jest ładniej i szybciej. Pamięć i tak jest potrzebna, ale można ograniczyć czas jej blokowania przez danego użytkownika.

0

Architektura J2EE serwer Glassfish. Nie znalazlem zadnej informacji jak tam pliki zapisywac. Zreszta kiedys tutaj pytalem o to i rozwiazanie bylo dosc skomplikowane :D Dlatego zdecydowalem sie na DB i BLOBa

UPDATE: Ale jestem slepy: zapisuje odczytane dane do tablicy input a do wpliku pusta tablice output :d Poprawilem i dziala prawidlowo, tyle ze pliki wynikowe sa wieksze. Ale to z tego wzgledu ze poszczegolne czesci maja rowno 64kb. Zaraz postaram sie jakos wykombinowac zeby mialy tyle ile wynosi rzeczywisty rozmiar danych.

0

Nie jest skomplikowane ;) Wystarczy utworzyć MBeana, który będzie zapisywał i odczytywał pliki. Chodzi o to by nie robić tego bezpośrednio w aplikacji.

0

Zdecydowalem sie jednak na wariant z zapysywaniem plikow w bazie. Z tego wzgledu ze nigdy czegos takiego nie robilem.

Algorytm zapisu i odczytu przedstawia sie nastepujaco:

InputStream is = new FileInputStream(new File("qtjambi.zip"));
            int bufferSize = 65536;
            int available;

            ArrayList<SerialBlob> blobList = new ArrayList<SerialBlob>();

            System.out.println(is.available());
            for(int i=0;true;i++) {
                byte[] input;
                int tempSize;
                if(bufferSize > is.available() && is.available() != 0){
                    input = new byte[is.available()];
                    tempSize = is.available();
                }
                else{
                    input = new byte[bufferSize];
                    tempSize = bufferSize;
                }

                int nb = is.read(input, 0, tempSize);

                if(nb == -1){
                    System.out.println(tempSize);
                    break;
                }

                SerialBlob sb = new SerialBlob(input);
                blobList.add(sb);
            }
                
            System.out.println(blobList.size());

            OutputStream os = new FileOutputStream("test.zip");
            
            for(int i=0; i<blobList.size(); i++){

                SerialBlob sbTemp = blobList.get(i);
                int size = (int)sbTemp.length(); // 

                
                InputStream isTemp = sbTemp.getBinaryStream();
                byte[] output = new byte[size];
                while(true){

                    int nb = isTemp.read(output, 0, size);
                    if(nb == -1){
                        break;
                    }

                    os.write(output, 0, nb);
                }
                isTemp.close();
            }

No i dziala. Tyle, ze to tylko symulacja. Jesli rzeczywiscie zapisze dane do bazy przy pomocy obiektow SerialBlob a potem je odczytam i polacze w jeden plik - to juz nie dziala. Plik sie oczywiscie tworzy i w przypadku pliku tekstowego mozna odczytac poprawnie tresc. Natomiast pliki .zip juz nie dzialaja. Wyskakuje blad. Plik sie otwiera, widac w nim zawartosc - ale nie kompletna.

W bazie pole BLOB ma wielkosc 128kb a czesci dzielone sa na 127kb. Jak dzielilem na 128 to przekraczalem rozmiar pola.

No i dlaczego to nie chodzi oO?

Czy jesli BLOB w bazie ma 128kb i zapisuje do niego 127kb to on dopisuje puste znaki tak, zeby zawsze zajmowal te 128 kb? I w rezultacie pobieram pakiety 128kb i zapisuje te puste dane do pliku z tego wynika blad?

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