# *.jar öffnet PDF-Datei nicht



## Jacl (15. Dez 2018)

Ich möchte eine Hilfedatei im PDF-Format wenn auf ein Bild mit der Maus geklickt wird öffnen – das Bild wurde in ein JLabel integriert (siehe Codeschnipsel).
In der IDE (Netbeans) funktioniert es wunderbar. Wenn ich jedoch eine *.jar erstelle und diese dann starte zeigt das Standard PDF-Programm folgendes an:

```
Dokument »file:///tmp/Hilfe9050727549084865621.pdf« konnte nicht geöffnet werden.
PDF document is damaged
```
Ich würde das ganze gerne "plattformunabhängig" hinbekommen!

```
if (Desktop.isDesktopSupported()) {
                    try {
                        InputStream is = getClass().getResourceAsStream("Hilfe.pdf");
                        byte[] data = new byte[is.available()];
                        is.read(data);
                        is.close();
                        String tempFile = "Hilfe";
                        File temp = File.createTempFile(tempFile, ".pdf");
                        FileOutputStream fos = new FileOutputStream(temp);
                        fos.write(data);
                        fos.flush();
                        fos.close();
                        Desktop.getDesktop().open(temp);
                    } catch (IOException ex) {
                    ex.printStackTrace();
                    }
                }
```

Was mache ich falsch, kann mir wer helfen?


----------



## httpdigest (15. Dez 2018)

Das Problem ist die Art und Weise, wie du InputStream.read(byte[]) verwendest, um die Bytes aus der Datei zu lesen.


> Reads *some number of bytes* from the input stream and stores them into the buffer array b. The *number of bytes actually read is returned as an integer*. ...


Heißt: Du solltest in einer Schleife solange InputStream.read() aufrufen, bis diese Methode -1 zurückliefert und jeweils den Rückgabewert benutzen, um die tatsächlich gelesenen Bytes in den FileOutputStream zu schreiben, vorzugsweise mittels FileOutputStream.write(byte[], int, int).

Auch liefert InputStream.available() nicht die tatsächliche Anzahl der Bytes, die der Stream potentiell zurückliefern kann:


> Returns *an estimate of the number of bytes* that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream.


----------



## Jacl (15. Dez 2018)

Gibt's hierfür irgendwo ein Codeschnipsel das ich anpassen kann? Das mit dem InputStream.read() kriege ich hin:

```
try {
   InputStream inputstream = new FileInputStream("Hilfe.pdf");
   int data = inputstream.read();
   while(data != -1) {
      data = inputstream.read();
   }
   inputstream.close();
```
Zumindest glaube ich das es so funktioniert. Aber bei der Ausgabe mit  FileOutputStream.write(byte[], int, int) stehe ich an!


----------



## httpdigest (15. Dez 2018)

Du solltest schon weiterhin InputStream.read(byte[]) verwenden. Das füllt ja im übergebenen byte[]-Array Bytes, von vorne beginnend, und liefert die Anzahl der geschriebenen/gefüllten Bytes als Rückgabewert. Jetzt dann halt einfach diesen Rückgabewert für `len` in FileOutputStream.write(byte[], int, int) verwenden, zusammen mit dem byte[]-Array.
Du brauchst also trotzdem noch ein byte[] Array als Puffer für die Effizienz. Kannst das irgendwie dimensionieren, z.B. 8192 Bytes.


----------



## Jacl (15. Dez 2018)

Also ich habe nun folgendes funktionierendes Konstrukt erstellt bei dem die *.jar Datei die PDF.Hilfedatei lädt. Ob's auch auf anderen Systemen läuft kann ich erst am Montag feststellen - unter Linux läuft es:

```
InputStream inputStream = this.getClass().getResourceAsStream("Hilfe.pdf");
File tempOutputFile = null;
try {
    tempOutputFile = File.createTempFile( "Hilfe", "pdf" ); // oder in ein Anwendungsverzeichnis!?
    } catch (IOException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
        tempOutputFile.deleteOnExit(); // löscht die Datei wieder, wenn die Anwendung geschlossen wird
        FileOutputStream out = null;
        try {
            out = new FileOutputStream( tempOutputFile );
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
        byte buffer[] = new byte[ 8192 ];
        int len;
        try {
        while( ( len = inputStream.read( buffer ) ) > 0 ) {
            out.write( buffer, 0, len );
        }
        } catch (IOException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            out.close();
        } catch (IOException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            inputStream.close();
        } catch (IOException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            Desktop.getDesktop().open(tempOutputFile);
        } catch (IOException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
```
Ein Anfänger halt - aber im Moment funktioniert es, ob systemübergreifend wird sich zeigen...
Vielen Dank jedenfalls für die Hilfe


----------



## mihe7 (15. Dez 2018)

Du brauchst das try-catch nicht um jedes Statement einzeln machen. Mit try-with-resources wirds noch schöner, weil man sich um das Schließen nicht mehr zu kümmern braucht. Dann kann man das ganze noch in ein paar Methoden packen.

Mal ein paar Impulse (alles ungetestet):


```
private Optional<File> helpFile = Optional.empty();

public void showHelp() {
    try {
        createHelpFileIfNeccessary();
        Desktop.getDesktop().open(helpFile.get());
    } catch (IOException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
}

private void createHelpFileIfNeccessary() throws IOException {
    if (helpFile.isPresent()) {
        return;
    }

    File pdfFile = File.createTempFile("Hilfe", "pdf");
    pdfFile.deleteOnExit();
    saveResourceTo(pdfFile, "Hilfe.pdf");
    helpFile = Optional.of(pdfFile);
}

void saveResourceTo(File dest, String resource) throws IOException {
    try(InputStream in = getClass().getResourceAsStream(resource);
            FileOutputStream out = new FileOutputStream(dest)) {
        transfer(in, out);
    }
}
```

Die Methode transfer ist als Platzhalter zu verstehen. Ab Java 9 kann man direkt `is.transferTo(out);` aufrufen. In früheren Versionen macht man es halt so, wie Du es gemacht hast:

```
void transfer(InputStream is, OutputStream os) throws IOException {
    byte[] buf = new byte[8192];
    int n;
    while ((n = is.read(buf)) != -1) {
        os.write(buf, 0, n);
    }
}
```


----------



## Jacl (16. Dez 2018)

hmm, muss ich mir ansehen - Danke mihe7, was würde ich ohne dir machen...


----------



## mrBrown (16. Dez 2018)

Und wenn man's noch einfacher will, nimmt man Files.copy


----------



## mihe7 (16. Dez 2018)

mrBrown hat gesagt.:


> Und wenn man's noch einfacher will, nimmt man Files.copy




```
void saveResourceTo(File dest, String resource) throws IOException {
    try(InputStream in = getClass().getResourceAsStream(resource)) {
        Files.copy(in, dest.toPath());
    }
}
```


----------



## Jacl (17. Dez 2018)

Hallo, bin wieder mit einer Frage zu diesem Thema hier. Hat alles wunderbar mit den oben angeführten Tipps funktioniert - allerdings nur unter Linux. Wenn ich das Test.jar unter Windows starte, wird meine Hilfe.pdf aus dem Test.jar nicht geladen. Es gibt auch keine Fehlermeldung oder sonstwas. Die Hilfe.pdf liegt innerhalb der Jar-Datei im selben Verzeichnis wie die Test.class es gibt keine anderen Verzeichnisse/Ordner, nur eine einzige Ebene in der alles drinnen ist! Daher steht auch nur "Hilfe.pdf" und kein "/" oder "\" oder ein Pfad:

```
InputStream inputStream = this.getClass().getResourceAsStream("Hilfe.pdf");
```
Hat hierbei jemand Erfahrung - woran kann das liegen, dass es unter Windows 7 plötzlich nicht geht?  Wie gesagt, unter Linux ist es perfekt...
Jacl


----------



## mihe7 (17. Dez 2018)

Probier mal `"/Hilfe.pdf"`


----------



## mrBrown (17. Dez 2018)

Jacl hat gesagt.:


> Es gibt auch keine Fehlermeldung oder sonstwas


Der Stream ist auch sicher nicht null?



mihe7 hat gesagt.:


> Probier mal "/Hilfe.pdf"


Allerdings sollte der Pfad das ganze unter Linux brechen lassen?


----------



## mihe7 (17. Dez 2018)

mrBrown hat gesagt.:


> Allerdings sollte der Pfad das ganze unter Linux brechen lassen?


Warum das denn? 

getClass().getResourceAsStream("/Hilfe.pdf") sollte an den ClassLoader als "Hilfe.pdf" übergeben werden, während getClass().getResourceAsStream("Hilfe.pdf") zu "Hilfe/pdf" führen dürfte.


----------



## mrBrown (17. Dez 2018)

mihe7 hat gesagt.:


> getClass().getResourceAsStream("/Hilfe.pdf") sollte an den ClassLoader als "Hilfe.pdf" übergeben werden, während getClass().getResourceAsStream("Hilfe.pdf") zu "Hilfe/pdf" führen dürfte.



`getClass().getResourceAsStream("/Hilfe.pdf")` lädt Hilfe.pdf, `getClass().getResourceAsStream("Hilfe.pdf")` lädt package_mit_slashes/Hilfe.pdf


----------



## mihe7 (17. Dez 2018)

mrBrown hat gesagt.:


> lädt package_mit_slashes/Hilfe.pdf


Stimmt. Erklärt aber immer noch nicht, warum /Hilfe.pdf unter Linux nicht funktionieren sollte.


----------



## mrBrown (17. Dez 2018)

mihe7 hat gesagt.:


> Stimmt. Erklärt aber immer noch nicht, warum /Hilfe.pdf unter Linux nicht funktionieren sollte.


Weil "Hilfe.pdf" und "package_mit_slashes/Hilfe.pdf" unterschiedliche Pfade sind? (zumindest, wenn sein Code nicht im Default-Package liegt)


----------



## mihe7 (17. Dez 2018)

mrBrown hat gesagt.:


> zumindest, wenn sein nicht im Default-Package liegt)


Das wäre ja meine Vermutung gewesen: "es gibt keine anderen Verzeichnisse/Ordner, nur eine einzige Ebene in der alles drinnen ist!"


----------



## mrBrown (17. Dez 2018)

mihe7 hat gesagt.:


> Das wäre ja meine Vermutung gewesen: "es gibt keine anderen Verzeichnisse/Ordner, nur eine einzige Ebene in der alles drinnen ist!"



sollte sich dann aber unter Windows und Linux identisch verhalten, /Hilfe.pdf und Hilfe.pdf werden aus
dem Default-Package heraus ja gleich aufgelöst.


----------



## mihe7 (17. Dez 2018)

mrBrown hat gesagt.:


> sollte sich dann aber unter Windows und Linux identisch verhalten, /Hilfe.pdf und Hilfe.pdf werden aus
> dem Default-Package heraus ja gleich aufgelöst.


Ja, darum habe ich die Doku zu Class-HTML auch nochmal angesehen und bin dann mit dem modified package name durcheinander gekommen.

Einzige Erklärung, die mir dazu einfällt ist die, auf die Du oben hinaus wolltest: das PDF ist im jar nicht enthalten und liegt auf seinem Linux-System zufällig im Classpath an der betreffenden Stelle.


----------



## mrBrown (17. Dez 2018)

mihe7 hat gesagt.:


> Einzige Erklärung, die mir dazu einfällt ist die, auf die Du oben hinaus wolltest: das PDF ist im jar nicht enthalten und liegt auf seinem Linux-System zufällig im Classpath an der betreffenden Stelle.


Wäre zumindest aktuell die einzige Erklärung, die mir einfällt - sollte dann aber auch 'nen Fehler geben.
Der Fall, dass es unter Windows funktioniert und beim Wechsel nicht mehr hätte mehr Erklärungsmöglichkeiten...


----------



## Jacl (18. Dez 2018)

> Probier mal "/Hilfe.pdf"


Führt unter Linux bereits beim Ausführen in der IDE zu einem Fehler.


> Einzige Erklärung, die mir dazu einfällt ist die, auf die Du oben hinaus wolltest: das PDF ist im jar nicht enthalten und liegt auf seinem Linux-System zufällig im Classpath an der betreffenden Stelle.


Nein, die Hilfe.pdf ist definitiv im Jar enthalten, ich hab's mir angesehen.
Ich kann's mir noch nicht erklären, aber möglicherweise hängt es mit dem addMouseListener zusammen

```
addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
      //TODO
    }
});
```
Der hängt nämlich an einem JLabel mit Bild, das beim anklicken eben die Hilfe.pdf öffnen soll. Und unter Windows muss der Listener irgendwie anderns an's JLabel angehängt werden, möglicherweise...
Ich kann das immer nur am Abend ändern und am nächsten Tag in der Arbeit ausprobieren. Der Inhalt meiner Jar:
Verzeichnisse: META-INF und Test. Nachdem öffnen des Ordners Test steht folgender Inhalt:
	
	
	
	





```
0000ff.gif, Hilfe.pdf, Test$1.class, Test$2.class, Test$3$1.class, Test$3.class, Test$4.class,Test$5.class, Test$6.class, Test$7.class, Test$8.class Test.class.
```


----------



## mihe7 (18. Dez 2018)

OK, schau Deinen Code durch, ob Du irgendwo Exceptions abfängst und ignorierst/nicht ausgibst. Falls das zu keinem Ergebnis führt, würde ich einfach mal in den Methoden, die am Aufruf beteiligt sind, Debugging-Ausgaben reinhauen und schauen, wo es hängt.


----------



## mrBrown (18. Dez 2018)

Kannst du einmal deine Test-Klasse zeigen?


----------



## mrBrown (18. Dez 2018)

Moment - woher weißt du eigentlich, dass die PDF nicht geladen wird, wenn es keine Ausgabe dazu gibt?


----------



## Jacl (20. Dez 2018)

> Moment - woher weißt du eigentlich, dass die PDF nicht geladen wird, wenn es keine Ausgabe dazu gibt?


Weil die Datei Hilfe.pdf nicht geöffnet wird und im Programmmanager (hätte sich was im Hintergrund getan) auch nix ersichtlich ist. Wenn man auf's Bild mit dem Listener klickt tut sich überhaupt nix (wie wenn kein Listener vorhanden ist)! Ich habe nun die ganze Woche probiert, den Listener einmal auf ein JLabel mit und ohne Bild zu legen, dann habe ich's mit einem Button probiert - leider alles erfolglos. Ich bin mit meinem Latein am Ende. Mit meinem jetztigen Wissensstand krieg ich's leider nicht hin. Mit den Exceptionsabfragen tue ich mir auch noch recht schwer. Hinzu kommt noch, dass der TestPC ein Windows-Firmenrechner ist der nur die JRE installiert hat. Das hat sicher etwas mit meinem Code zu tun (der ist veraltet).  Ich werde mich jetzt mit dem Schnipsel:

```
void saveResourceTo(File dest, String resource) throws IOException {
    try(InputStream in = getClass().getResourceAsStream(resource)) {
        Files.copy(in, dest.toPath());
    }
}
```
auseinandersetzen. Probiert habe ich's zwar schon, kriege aber in der IDE ständig fehler. Vielleicht macht mir Java ja ein Weihnachtsgeschenk in Form eines funktionierenden Hilfe.pdf das zur Laufzeit aus dem *.jar auf einem Windowsrechner geladen wird...


----------



## mihe7 (20. Dez 2018)

Mal eine ganz dumme Idee: kann es sein, dass Du das jar auf dem Windowsrechner per Doppelklick aufrufst und deshalb überhaupt keine Fehler siehst? Wenn ja, starte das Ding doch mal über die Befehlszeile (EDIT: natürlich mit java.exe und nicht mit javaw.exe)


----------



## Jacl (20. Dez 2018)

Ich hab's soeben über die Befehlszeile gestartet und bekomme folgende Fehlermeldung:
	
	
	
	





```
Fehler: Hauptklasse h:\test.jar konnte nicht gefunden oder geladen werden
```
Ich habe meine test.jar in der NetbeansIDE mit jar erstellen und bereinigen erstellt.


----------



## mihe7 (20. Dez 2018)

```
H:
cd \
java -jar test.jar
```

bzw. wenn Du keine Libs brauchst:

```
java -jar H:\test.jar
```


----------



## Jacl (20. Dez 2018)

Eingabe von


mihe7 hat gesagt.:


> java -jar H:\test.jar


ergibt golgende Ausgabe:

```
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Alle Rechte vorbehalten.

java -jar h:\test.jar
Dez 20, 2018 8:47:57 AM test.Test$1 mouseClicked
SCHWERWIEGEND: null
java.io.IOException: Failed to open C:\Users\user\AppData\Local\Temp\Hilfe552
6452732798370058pdf. Error message: Der angegebenen Datei ist keine Anwendung zu
geordnet.

        at sun.awt.windows.WDesktopPeer.ShellExecute(Unknown Source)
        at sun.awt.windows.WDesktopPeer.open(Unknown Source)
        at java.awt.Desktop.open(Unknown Source)
        at test.Test$1.mouseClicked(Test.java:199)
        at java.awt.AWTEventMulticaster.mouseClicked(Unknown Source)
        at java.awt.Component.processMouseEvent(Unknown Source)
        at javax.swing.JComponent.processMouseEvent(Unknown Source)
        at java.awt.Component.processEvent(Unknown Source)
        at java.awt.Container.processEvent(Unknown Source)
        at java.awt.Component.dispatchEventImpl(Unknown Source)
        at java.awt.Container.dispatchEventImpl(Unknown Source)
        at java.awt.Component.dispatchEvent(Unknown Source)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
        at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
        at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
        at java.awt.Container.dispatchEventImpl(Unknown Source)
        at java.awt.Window.dispatchEventImpl(Unknown Source)
        at java.awt.Component.dispatchEvent(Unknown Source)
        at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
        at java.awt.EventQueue.access$500(Unknown Source)
        at java.awt.EventQueue$3.run(Unknown Source)
        at java.awt.EventQueue$3.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionP
rivilege(Unknown Source)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionP
rivilege(Unknown Source)
        at java.awt.EventQueue$4.run(Unknown Source)
        at java.awt.EventQueue$4.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionP
rivilege(Unknown Source)
        at java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.run(Unknown Source)
```


----------



## mihe7 (20. Dez 2018)

Jacl hat gesagt.:


> Der angegebenen Datei ist keine Anwendung zugeordnet.


Aha. Das ist ziemlich eindeutig. Probier doch mal auf dem Windows-Rechner die PDF per Doppelklick zu öffnen


----------



## Jacl (20. Dez 2018)

Habe mir die Hilfe.pdf aus dem jar-File rausgeholt und mit doppeklick geöffnet - sie wird mit dem Adobe Acrobat Reader DC geöffnet...


----------



## mihe7 (20. Dez 2018)

Jacl hat gesagt.:


> sie wird mit dem Adobe Acrobat Reader DC geöffnet...


Automatisch oder erst, nachdem Du Windows gesagt hast, es soll die PDF mit dem Acrobat Reader öffnen?

Die Fehlermeldung sagt ja aus, dass dem Dateityp pdf keine Standard-Anwendung zugeordnet wäre.


----------



## mihe7 (20. Dez 2018)

Ach, jetzt habe ich es gesehen: Du schreibst die Datei ohne "." vor der Erweiterung raus: 234234pdf statt 234234.pdf -> da kommt Windows nicht zurecht.

Nachtrag: es geht um die Zeile

```
tempOutputFile = File.createTempFile( "Hilfe", "pdf" );
```

Ganz am Anfang hattest Du es richtig:

```
tempOutputFile = File.createTempFile( "Hilfe", ".pdf" );
```


----------



## Jacl (20. Dez 2018)

Mann, wenn's das gewesen ist! Werde ich heute am Abend gleich mal zu Hause ändern und ne neue test.jar erstellen und morgen dann wieder in der Arbeit probieren...   
Vielen Dank vorerst mal - wie schon mal gesagt, was würde ich ohne mihe7 machen  
jacl


----------



## mihe7 (20. Dez 2018)

Jacl hat gesagt.:


> Mann, wenn's das gewesen ist!


Mit an Sicherheit grenzender Wahrscheinlichkeit.



Jacl hat gesagt.:


> was würde ich ohne mihe7 machen


Antworten von anderen lesen


----------



## Jacl (21. Dez 2018)

Yes, der Punkt beim "pdf" war's.
	
	
	
	





```
tempOutputFile = File.createTempFile( "Hilfe", ".pdf" );
```
Ohne Punkt kein Ende  - jetzt funtionierts auch unter Windows. 
Vielen Dank für die Hilfe und fröhliche Weihnachten inklusive Feiertage...
Jacl


----------

