Ergebnis 1 bis 13 von 13

Thema: XdevFile: Datei-Speichern-Komponente

  1. #1
    Administrator Avatar von XDEV Team
    Registriert seit
    05.10.2010
    Beiträge
    230
    Blog-Einträge
    175
    Hat mir geholfen
    0
    Positive Bewertungen 71 in 55 Posts

    XdevFile: Datei-Speichern-Komponente

    Genauso wie man mit der Komponente XdevPicture Bilder als BLOB in einer Datenbank speichern kann, könnte man eine Komponente XdevFile anbieten, um damit beliebige Dokumente als BLOB speichern zu können.

    Evt. benötigt man sogar eine n:m Komponente, mit der man sogar mehrere Dokumente auf einmal zu einem Datensatz speichern kann. Eine entsprechende n:m Erweiterung ist auch für XdevPicture denkbar. Evt. könnte das ein Bilderkatalog sein, XdevCatalogue.

    Feature-Request von mehreren XDEV 3 Starter Workshops.
    XDEV Team

  2. #2
    Registrierter Benutzer
    Registriert seit
    29.01.2012
    Beiträge
    14
    Hat mir geholfen
    1
    Positive Bewertungen 0 in 0 Posts
    Ja, genau das könnte ich gerade gebrauchen!

  3. #3
    Registrierter Benutzer
    Registriert seit
    17.11.2011
    Beiträge
    51
    Hat mir geholfen
    11
    Positive Bewertungen 6 in 6 Posts
    Hallo XDEV,

    bitte umsetzen, wird benötigt.

  4. #4
    Registrierter Benutzer
    Registriert seit
    29.01.2012
    Beiträge
    14
    Hat mir geholfen
    1
    Positive Bewertungen 0 in 0 Posts

    Implementierungsansatz

    So könnte das in etwa aussehen:
    (Ist sicherlich noch mit Vorsicht zu genießen ;) )
    Code:
    package io;
    
    
    import xdev.db.Operator;
    import xdev.lang.EventHandlerDelegate;
    import xdev.ui.*;
    import xdev.ui.Validation;
    import xdev.ui.ValidationException;
    import xdev.ui.Validator;
    import xdev.ui.dnd.DropAdapter;
    import xdev.ui.dnd.DropEvent;
    import xdev.vt.VirtualTable;
    import xdev.vt.VirtualTableColumn;
    import xdev.vt.XdevBlob;
    
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.swing.BorderFactory;
    import javax.swing.filechooser.FileFilter;
    
    
    @BeanSettings(acceptChildren = true, useXdevCustomizer = true)
    public class FileComponent extends XdevContainer 
            implements FormularComponent<FileComponent>, XdevFocusCycleComponent
    {
        public final static String                                FILE_PROPERTY    = "file";
        private FileFilter                                        fileFilter        = null;
        private Object                                            formularValue;
        private final FormularComponentSupport<FileComponent>    support            = new FormularComponentSupport<FileComponent>(
                                                                                        this);
        private Object                                            savedState;
        private int                                                tabIndex;
        
    
        @Override
        @SuppressWarnings("rawtypes")
        public void addValidator(Validator validator)
        {
            this.support.addValidator(validator);
        }
        
    
        @Override
        public void addValueChangeListener(final ValueChangeListener valueChangeListener)
        {
            this.addPropertyChangeListener(FILE_PROPERTY,new PropertyChangeListener()
            {
                @Override
                public void propertyChange(PropertyChangeEvent evt)
                {
                    valueChangeListener.valueChanged(evt);
                }
            });
        }
        
    
        @Override
        public String getDataField()
        {
            return this.support.getDataField();
        }
        
    
        public FileFilter getFileFilter()
        {
            return this.fileFilter;
        }
        
    
        @Override
        public Operator getFilterOperator()
        {
            return this.support.getFilterOperator();
        }
        
    
        @Override
        public String getFormularName()
        {
            return this.support.getFormularName();
        }
        
    
        @Override
        public Object getFormularValue()
        {
            return this.formularValue;
        }
        
    
        @Override
        public Validator<?>[] getValidators()
        {
            return this.support.getValidators();
        }
        
    
        @Override
        public boolean hasStateChanged()
        {
            return this.getFormularValue().equals(this.savedState);
        }
        
    
        @Override
        public boolean isMultiSelect()
        {
            return false;
        }
        
    
        @Override
        public boolean isReadOnly()
        {
            return !this.buttonOpen.isVisible();
        }
        
    
        @Override
        @SuppressWarnings("rawtypes")
        public void removeValidator(Validator validator)
        {
            this.support.removeValidator(validator);
        }
        
    
        @Override
        public void restoreState()
        {
            this.formularValue = this.savedState;
        }
        
    
        @Override
        public void saveState()
        {
            this.savedState = this.getFormularValue();
        }
        
    
        @Override
        public void setDataField(String dataField)
        {
            this.support.setDataField(dataField);
        }
        
    
        @Override
        public void setEnabled(boolean enabled)
        {
            super.setEnabled(enabled);
            this.buttonOpen.setEnabled(enabled);
        }
        
    
        public void setFileFilter(FileFilter fileFilter)
        {
            this.fileFilter = fileFilter;
        }
        
    
        @Override
        public void setFilterOperator(Operator filterOperator)
        {
            this.support.setFilterOperator(filterOperator);
        }
        
    
        private void setFormularValue(Object value)
        {
            if(value instanceof java.io.File)
            {
                File file = (java.io.File)value;
                int fileLength = (int)file.length();
                byte[] fileBytes = new byte[fileLength];
                try
                {
                    new RandomAccessFile(file,"r").readFully(fileBytes);
                }
                catch(FileNotFoundException e)
                {
                }
                catch(IOException e)
                {
                }
                value = new XdevBlob(fileBytes);
            }
            this.formularValue = value;
            if(value == null)
                this.labelName.setText(null);
            else
                this.labelName.setText(value.toString());
        }
        
    
        @Override
        public void setFormularValue(VirtualTable vt, Map<String, Object> record)
        {
            String dataField = this.getDataField();
            VirtualTableColumn<?> column = vt.getColumn(dataField);
            String columnName = column.getName();
            Object value = record.get(columnName);
            this.setFormularValue(value);
        }
        
    
        @Override
        @Deprecated
        public void setFormularValue(VirtualTable vt, int columnIndex, Object value)
        {
            String columnName = vt.getColumnName(columnIndex);
            Map<String, Object> record = new HashMap<String, Object>();
            record.put(columnName,value);
            this.setFormularValue(vt,record);
        }
        
    
        @Override
        public void setReadOnly(boolean readOnly)
        {
            this.buttonOpen.setVisible(!readOnly);
        }
        
    
        @Override
        public void validateState() throws ValidationException
        {
            this.support.validateState();
        }
        
    
        @Override
        public void validateState(Validation validation) throws ValidationException
        {
            this.support.validateState(validation);
        }
        
    
        @Override
        public boolean verify()
        {
            return this.support.verify();
        }
        
    
        @Override
        public int getTabIndex()
        {
            return this.tabIndex;
        }
        
    
        @Override
        public void setTabIndex(int tabIndex)
        {
            this.tabIndex = tabIndex;
        }
        
        private void showOpenDialog()
        {
            FileFilter fileFilter = this.getFileFilter();
            
            XdevFileChooser fileChooser = new XdevFileChooser();
            fileChooser.setFileFilter(fileFilter);
            java.io.File choosedFile = fileChooser.showOpenDialog();
            this.setFormularValue(choosedFile);
        }
    
        @EventHandlerDelegate
        void labelName_drop(DropEvent event) 
        {
            File file = null;
            Object eventData = event.getData();
            if(eventData instanceof List)
            {
                List<?> fileList = (List<?>)eventData;
                Object fileObject = fileList.get(0);
                if(fileObject instanceof java.io.File)
                    file = (java.io.File)fileObject;
            }
            
            if(this.getFileFilter() != null && this.getFileFilter().accept(file))
                this.setFormularValue(file);
        }
        
        @EventHandlerDelegate void buttonOpen_actionPerformed(ActionEvent event) 
        {
            try
            {
                this.showOpenDialog();
            }
            catch(Exception exception)
            {
                exception.printStackTrace();
            }
        }
    
    
        
        XdevLabel labelName;
        XdevButton buttonOpen;
        
        
        {
            labelName = new XdevLabel();
            buttonOpen = new XdevButton();
            
            labelName.setBorder(BorderFactory.createLoweredBevelBorder());
            buttonOpen.setTabIndex(1);
            buttonOpen.setText("...");
            
            labelName.saveState();
            
            this.setLayout(new BorderLayout());
            this.add(labelName,BorderLayout.CENTER);
            this.add(buttonOpen,BorderLayout.EAST);
            
            new DropAdapter(labelName)
            {
                @Override
                public void drop(DropEvent event)
                {
                    labelName_drop(event);
                }
            };
            buttonOpen.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent event)
                {
                    buttonOpen_actionPerformed(event);
                }
            });
        }
        
    }

  5. #5
    Administrator Avatar von XDEV Team
    Registriert seit
    05.10.2010
    Beiträge
    230
    Blog-Einträge
    175
    Hat mir geholfen
    0
    Positive Bewertungen 71 in 55 Posts
    Es wurde ein Feature Request erstellt. Dieser wird geprüft und ggf. umgesetzt. Issue 339
    XDEV Team

  6. #6
    Registrierter Benutzer
    Registriert seit
    29.01.2012
    Beiträge
    14
    Hat mir geholfen
    1
    Positive Bewertungen 0 in 0 Posts

    XdevFileChooserContainer

    Hübsch wäre auch, wenn die Datei sich direkt aus der Komponente heraus, im Sinne von
    Code:
    Desktop#open(File)
    , öffnen ließe.
    Z.B durch Doppelklick in das Textfeld oder mit einem zweiten Button.

  7. #7
    Registrierter Benutzer
    Registriert seit
    17.11.2011
    Beiträge
    51
    Hat mir geholfen
    11
    Positive Bewertungen 6 in 6 Posts
    Hallo,

    in meinem Projekt wird jetzt diese Funktionalität benötigt. Zu einem Datensatz muss ein Anhang(PDF) in der Tabelle ( H2, Datentyp 'BLOB') gespeichert werden können. Für ein Bild habe ich das schon mit dem XdevPicture Control umgesetzt, aber hier finde ich als Java Newbie keinen Ansatz.

    Für Hinweise, Vorgehensweise und ein paar Codezeilen wäre ich sehr dankbar.





    Viele Grüße
    Stephan

  8. #8
    Registrierter Benutzer
    Registriert seit
    29.01.2012
    Beiträge
    14
    Hat mir geholfen
    1
    Positive Bewertungen 0 in 0 Posts
    Hi Stephan,
    wenn
    Java Newbie
    für dich das gleiche bedeutet wie für mich ist es kaum verwunderlich, dass du keinen Ansatz findest.
    Was du tun musst, ist aus einem der vorhandenen Steuerelement ein völlig neues zu machen, dass einen völlig anderen Datentyp in der DB erwartet. Dir jetzt Quellcode Schnipsel zu schicken wäre Unsinn, da du die warscheinlich nicht integriert bekommen wirst und dir 'ne fertige Lösung vor zu kauen hab ich jetzt keine Lust ;)
    Wenn du dich aber heran wagen möchtest:
    Schnapp dir mal das XdevTextField (oder irgend eine andere Klasse, die das Interface FormularComponent implementiert)
    darin gibt es die beiden Methoden setFormularValue und getFormularValue.
    Die musst du überschreiben und denen klar machen, dass Sie von nun an XdevBlob Objekte annehmen müssen.
    Viel Erfolg ;)

    Dennoch gilt weiterhin natürlich:
    Fragenden Menschen kann geholfen werden!

    Gruß Tobi

  9. #9
    Registrierter Benutzer
    Registriert seit
    17.11.2011
    Beiträge
    51
    Hat mir geholfen
    11
    Positive Bewertungen 6 in 6 Posts
    Hallo Tobi,

    danke für deine Rückmeldung. Unabhängig vom Formular habe ich mir mit Hilfe von Google mal 2 klassen
    gebastelt die das speichern und laden von Blob's übernehmen sollen. Zum testen habe ich die Kfz-DB missbraucht.

    DocumentSave:
    Code:
    package Klassen;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import Datenquellen.DBKfz;
    
    
    
    public class DocumentSave
    {
    	public static void docSave(int id, String fn) {
    	  try {
    	        java.sql.Connection con = DBKfz.getInstance().getConnectionProvider().getConnection();
    	        FileInputStream  fis = new FileInputStream(new File(fn));
    	        PreparedStatement ps = con.prepareStatement("UPDATE HERSTELLER set PDF = ? WHERE id = ?;");
    	        ps.setBlob  ( 1, fis);            // blob-content
    	        ps.setInt   ( 2, id);             // satz-id
    	        System.out.println("Speichere Dokument");
    	        if (ps.executeUpdate()==1) {
    	           System.out.println("Dokument gespeichert.");
    	        } else {
    	           System.out.println("Speichern fehlgeschlagen.");
    	        }
    	  } catch (SQLException ex) {
    	     ex.printStackTrace();
    	  } catch (FileNotFoundException ex) {
    	     ex.printStackTrace();
    	  }
    	}
    }
    DocumentLoad:
    Code:
    package Klassen;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import Datenquellen.DBKfz;
    
    
    public class DocumentLoad
    {
    	public static void docLoad(String ID, String fn) {
    	  {
    	     PreparedStatement ps = null;
    	     InputStream is       = null;
    	     FileOutputStream fos = null;
    	     ResultSet rs         = null;
    	     try {
    	        java.sql.Connection con = DBKfz.getInstance().getConnectionProvider().getConnection();
    	        ps = con.prepareStatement("SELECT PDF FROM HERSTELLER WHERE ID = ?");
    	        ps.setString(1, ID);
    	        rs = ps.executeQuery();
    	        if (rs.next()) {
    	           is = rs.getBinaryStream("PDF");
    	           fos = new FileOutputStream(fn);
    	           byte[] buff = new byte[8192];
    	           int len=0;
    	           while ( (len=is.read(buff)) != -1 ){
    	              fos.write(buff,0,len);
    	           }
    	           fos.close();
    	        }
    	        rs.close();
    	        ps.close();
    	     } catch (IOException ex) {
    	        ex.printStackTrace();
    	     } catch (SQLException ex) {
    	        ex.printStackTrace();
    	     } finally {
    	        try {
    	           fos.close();
    	           is.close();
    	        } catch (IOException ex) {
    	           ex.printStackTrace();
    	        }
    	     }
    	  }
    	}
    }
    Es funktioniert, ist das aus Java Sicht ok?




    Viele Grüße
    Stephan

  10. #10
    Registrierter Benutzer
    Registriert seit
    29.01.2012
    Beiträge
    14
    Hat mir geholfen
    1
    Positive Bewertungen 0 in 0 Posts
    Aus Java Sicht sieht das OK aus (abgesehen davon, dass ich es nicht in zwei eigenen Klassen halten würde)
    aus XDEV Sicht jedoch ist es ein Graus, da die tollen und praktischen Eigenschaften des XDEV-Framework völlig ignoriert werden!
    Befass dich Mal mit den Klassen http://cms.xdev-software.de/xapidoc/...tualTable.html und http://cms.xdev-software.de/xapidoc/...lTableRow.html.
    In VirtualTable gibt es die Methoden createRow und addRow, damit kanst du die VirtualTableRow instanziieren und in deine VirtualTable einfügen. In VirtualTableRow kannst du mit der Methode set die einzelnen Spalten einer Zeile füllen.
    Damit kannst du dir das ganze getConnection und prepareStatement gehampel sparen!
    Aber in deinem konkretem Fall ist selbst das nicht notwendig, da dir auch das XDEV abnimmt,
    wenn du deine Oberfläche vernünftig über XDEVFormulare aufbaust und deinen Spezialfall wie von mir beschrieben behandelst!
    Hast du die DB denn richtig in deinem Projekt eingebunden? Das heißt hast du eine Datenquelle für die KFZ-DB erstellt und die VirtualTables importiert? (Siehst du daren, das du für die Tabellen jeweils eine Klasse im VirtuelleTabellen Ordner im Projekt hast)
    Ist dir klar was ich meine, wenn ich sage das du eine Unterklasse von einem XDEV-Steuerelement erstellen sollst?

    Gruß Tobi

  11. #11
    Registrierter Benutzer
    Registriert seit
    14.05.2011
    Beiträge
    162
    Hat mir geholfen
    8
    Positive Bewertungen 17 in 14 Posts
    Also mir fällt an dem Code auf (und das hat meist nicht mal spezifisch mit Java zu tun, sondern allgemein mit Struktur):

    • Beim update Fall fehlt das schließen des Statements. Je nach DBMS bzw. JDBC Implementierung kann das datenbankseitig Resourcen offen lassen, was früher oder später fatal endet.
    • Beim select Fall werden ResultSet und Statement nur im Erfolgsfall wieder aufgeräumt, aber nicht im Fehlerfall (um beide Fälle elegant abzubilden gibt es das finally).
    • Die Connection wird weder irgendwo "zurückgegeben" (falls im Hintergrund ein Pool ist, der Connections managet) noch wird sie geschlossen (was im Falle eines Pools aber unter Umständen fatal sein kann, je nach Pool Implementierung). Programmieren ist wie echtes Leben: Man muss seine Werkzeuge nach der Arbeit auch wieder wegräumen, nicht einfach rumliegen lassen.
    • Plain String als Definition der Datei anstatt File Instanz (potenziller Programmierfehler: niemand hindert einen daran, als Pfad einfach nur "" oder "Hans" zu übergeben, beim spezifischen Typ File ist dieses Problem nicht)
    • wenig sprechende Bezeichner (fn, con, ps, usw.). Old-School C-Hacker-Style ;). Es spricht auch nichts dagegen, etwas verständlichere Methodenbenamungen wie z.B. "saveDocument" anstatt kryptisches "docSave" zu schreiben.
    • hardgecodete Konsolenausgaben (was ist, wenn das mal generisch als modul wo verwendet werden soll, wo man nicht einfach so auf die Konsole plappern kann? Wenn das alle Module machen würden ...)
    • verschluckte Exception (nur weil man die printed heißt das nicht, dass das programm sauber darauf reagiert. Sondern das läuft munter weiter in wahrscheinlich inkonsistentem Zustand und kracht irgendwo später noch schlimmer). Leider sieht man diese fatale Unart ÜBERALL, weil anscheinend kaum jemand richtig versteht, was eine Exception ist.
    • Code-Redundanzen in den catch-Blöcken: das könnte man auch schreiben als "catch(IOException | SQLException e)". Redundanzen sollte man möglichst vermeiden, siehe auch nächster Punkt.
    • Keine saubere Trennung von Concearns: die fachliche Logik (welcher SELECT / UPDATE soll ausgeführt werden) ist vermischt mit technischer Logik (JDBC-level Ressourcenhandling). Das sollte getrennt und abstrahiert werden. Ansonsten muss man ja an jedem einzelnen Punkt, wo man etwas fachliches ausführen möchte (z.B. 1 Zeile SELECT) jedes mal den ~30 Zeilen Rattenschwanz schreiben (bzw. copy&pasten) und später mitwarten (evtl. hunderte von Stellen!).
    • Ich persönlich würde empfehlen, den Code etwas zu entzerren und z.B. nicht nach einer schließenden geschweiften Klammer (Strukturierungsmittel!) weiter Code zu schreiben. Zeilenumbrüche kosten kein Geld und verbessern meistens die Lesbarkeit (solange man es nicht übertreibt).
    • Es fehlen erklärende Kommentare, warum etwas gemacht wird, damit sich ein anderer oder man selbst in einigen Monaten beim Einlesen leichter tut und nicht auf eine Wand Spaghetti-Code prallt.


    Da das alles typische Anfängerfehler sind möchte ich kurz einen kleinen Rat aus vielen Jahren Softwareentwicklung geben:
    Software Entwicklung hat gar nicht so sehr mit Wissen über technische Details zu tun (das kann man alles googeln), sondern mit Weitsicht, Sorgfalt, Strukturierung/Modularisierung von Arbeitsschritten. Siehe die "Werkzeug" Metapher: Man muss kein "Java Profi" sein, um zu wissen, dass man sein Zeugs auch mal wieder aufräumen muss. Oder dass ein anderer oder das zukünftige Ich sich später mal einigermaßen bequem einlesen können wollen.
    Geändert von b00ns (22.07.2014 um 09:25 Uhr)

  12. #12
    Registrierter Benutzer
    Registriert seit
    17.11.2011
    Beiträge
    51
    Hat mir geholfen
    11
    Positive Bewertungen 6 in 6 Posts
    Hallo,

    danke für eure Rückmeldungen. Mir ist schon klar das ich das XDEV-Framework komplett umgehe (nicht absichtlich),
    mir fehlt hier einfach der Ansatz und das KnowHow. Ein paar Zeilen Code für die Unterklasse mit Methode(n) wären schon hilfreich.Meine Anwendung ist komplett XDEV konform aufgebaut (formulare, virtuelle tabellen, datenquellenanbindung mit relations, master-detail etc.)
    @b00ns:
    wow, danke für deinen professionellen Kommentar. Die beiden Klassen werden so natürlich nie produktiv in ein Programm kommen. Dann hoffe ich mal das der Feature Request umgesetzt wird.


    Nochmals Danke:-)

    Viele Grüße
    Stephan


    edit: ich werde mich morgen nochmal dransetzen, vielleicht schaffe ich es doch noch;)
    oder XDEV gibt ein wenig Unterstützung *wink*
    Geändert von S. Muth (22.07.2014 um 11:02 Uhr)

  13. #13
    Registrierter Benutzer
    Registriert seit
    14.05.2011
    Beiträge
    162
    Hat mir geholfen
    8
    Positive Bewertungen 17 in 14 Posts
    Bitte, gerne.

    Wenn ein Stück Code nur so als "Testwiese" benutzt wird, dann kann man vieles (Bezeichner, Kommentare, Exceptions) natürlich schleifen lassen, gar keine Frage. Habe ich mir schon gedacht, aber immerhin gab es ja auch eine direkte Frage nach Feedback ;).

    Andererseis macht man überraschend oft die Erfahrung, dass man zuerst sagt "Also das ist hier alles nur ein Prototyp zum rumprobieren" und ehe man sich's versieht läuft das doch irgendwo produktiv.

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •