# Relative Pfade und Arbeitsverzeichnisse



## Freetree (5. Nov 2012)

Hi

Ich habe ein Problem und zwar will ich eine Datei laden, die im selbern Ordner, wie meine Jar ist. Ich gebe den Pfad also relativ mit ./foo.txt an. Nun ist es aber so, dass wenn ich meine Jar nicht in Eclipse starte(also das Programm), er die Datei nicht findet. Wenn ich im Terminel ins Verzeichniss der Jar gehe funktioniert es. Ich würde mal sagen, dass er die foo.txt nicht relativ von der Jar sieht, sondern relativ zu dem Verzeichniss, in der ich sie gestarten habe. Wie bekomme ich das jetzt nur hin, dass er die Datei findet, egal aus welchen Verzeichniss ich die Jar starte? Absolute Pfade möchte ich  nicht nehmen! 

Viele Grüße aus Hamburg


----------



## tvr (5. Nov 2012)

Du könntest die Datei in das Jar file einbinden und von da aus aufrufen

```
ImageIO.class.getResource("/foo.txt")
```


----------



## Freetree (5. Nov 2012)

Ja aber dafür müsste ich die Datei ja ansprechen können über einen Pfad. Doch genau da liegt ja dass Problem ich kann kein relativen Pfad nehmen, da das Verzichniss, aus dem die Jar Datei gestartet wird ja ein anderes sein kann als, in dem sie ist. Und absolute Pfade (alle mit /..) will ich auch nicht benutzen, da die Jar ja auch z.B. vom USB stick gestartet werden soll.


Viele Grüße aus Hamburg


----------



## TKausL (5. Nov 2012)

Guck dich mal hier um, ob du dort was findest:

http://www.java-forum.org/java-basi...4-aktueller-pfad-programms-jar-ermitteln.html

Allgemein wird aber IMMER Relativ vom Working-Dir gesucht, egal welches Programm...


----------



## Spacerat (5. Nov 2012)

TKausL hat gesagt.:


> Allgemein wird aber IMMER Relativ vom Working-Dir gesucht, egal welches Programm...


Das ist korrekt... aber was ist in Eclipse das Working-Dir? Der erwartete Pfad oder ein bin-Verzeichnis in diesem (egal... lässt sich ja einstellen )?
Wie auch immer. Wenn man innerhalb einer Anwendung das Working-Dir mittels einer Klasse aus dem aktuellen Archiv ermitteln will, beisst man bei Eclipse auf Granit... nur wegen eines dämlichen bin-Verzeichnisses. Der Sache kann man aber auf den Grund gehen.

```
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

public final class BaseURL {
	private static final String JAR_SIG = ".jar!/";
	private static final String DUMMY = "dummy";

	public static URL getJarURL(Class<?> clazz) {
		URL rc = clazz.getResource(clazz.getSimpleName() + ".class");
		String name = clazz.getName().replace('.', '/').concat(".class");
		name = rc.toString().replace(name, DUMMY);
		try {
			rc = new URL(null, name);
			return rc;
		} catch(MalformedURLException e) {
			return null;
		}
	}

	public static URL getCodeBase(Class<?> clazz) {
		URL rc = getJarURL(clazz);
		if(rc.getProtocol().equals("jar")) {
			String name = rc.toString();
			int i = name.indexOf(JAR_SIG);
			name = name.substring(4, i);
			try {
				rc = new URL(null, name);
				rc = new URL(rc, DUMMY);
			} catch(MalformedURLException e) {
				rc = null;
			}
		}
		return rc;
	}

	public static URL getDocumentBase() {
		try {
			return new URL(new File(".").getAbsoluteFile().toURI().toURL(), DUMMY);
		} catch(MalformedURLException e) {
			throw new RuntimeException("document base could not be determined");
		}
	}
}
```
Das kann man spasseshalber einmal in Eclipse (oder so) und ein mal in einem Archiv testen. Man kann diese Utilities aber auch in seinem Archiv aufnehmen und verwenden.


----------



## mla.rue (6. Nov 2012)

```
String identifyJarDir() {
     String strJarDir = "";
     
     try {
          CodeSource codeSource = GUI_Hauptfenster.class.getProtectionDomain().getCodeSource();
          File jarFile = new File(codeSource.getLocation().toURI().getPath());
          strJarDir = jarFile.getParentFile().getPath();
     } catch (URISyntaxException ex) {
          javax.swing.JOptionPane.showMessageDialog(null, ex, "URISyntaxException", 0);
     }
     return strJarDir;
}
```

Kriegst damit den Pfad zum jar File, unabhängig davon von welchen "Links" (Verknüpfungen) es gestartet wurde.


----------



## FArt (6. Nov 2012)

Wenn es nur darum geht eine Datei zu lesen, ist der bessere Ansatz sie über den Classloader zu laden  anstatt über das Filesystem. Damit bist du flexibler.


----------



## maki (6. Nov 2012)

mla.rue hat gesagt.:


> ```
> String identifyJarDir() {
> String strJarDir = "";
> 
> ...


Eben, die anderen Lösungen die hier vorgestellt wurden arbeiten eben nur mit dem Working Directory, nicht wirklich mit der Location der Jar/Klassen


----------



## Spacerat (6. Nov 2012)

Das ist nicht ganz richtig... folgendes ergibt, wenn man z.B. "String.class" übergibt 'ne NPE, im Gegensatz zu meiner "getCodeBase()"-Methode, welche das korrekte Verzeichnis der "rt.jar" liefert.

```
public static URL getJarBase(Class<?> clazz) {
		try {
			CodeSource codeSource = clazz.getProtectionDomain().getCodeSource();
			return codeSource.getLocation().toURI().toURL();
		} catch (URISyntaxException | MalformedURLException e) {
			throw new RuntimeException("jar dir could not be determined");
		}
	}
```
[WR]Aktualisierte Version:

```
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

public final class BaseURL {
	private static final String JAR_SIG = ".jar!/";
	private static final String CONCAT = ".class";

	public static URL getJarURL(Class<?> clazz) {
		URL rc = clazz.getResource(clazz.getSimpleName() + CONCAT);
		String name = clazz.getName().replace('.', '/').concat(CONCAT);
		name = rc.toString().replace(name, "");
		try {
			rc = new URL(null, name);
			return rc;
		} catch (MalformedURLException e) {
			throw new RuntimeException("jar dir could not be determined");
		}
	}

	public static URL getCodeBase(Class<?> clazz) {
		URL rc = getJarURL(clazz);
		if (rc.getProtocol().equals("jar")) {
			String name = rc.toString();
			int i = name.indexOf(JAR_SIG);
			name = name.substring(4, i);
			try {
				rc = new URL(null, name);
				rc = new URL(rc, "./");
			} catch (MalformedURLException e) {
				rc = null;
			}
		}
		return rc;
	}

	public static URL getDocumentBase() {
		try {
			String rc = new File(".").getAbsoluteFile().toURI().toURL().toString();
			return new URL(null, rc.substring(0, rc.length() - 2));
		} catch (MalformedURLException e) {
			throw new RuntimeException("document base could not be determined");
		}
	}
}
```
[/WR]
[EDIT]"getJarURL()" liefert im übrigen das Root-Verzeichnis des Jars (Jar-Protokoll) oder die CodeBase in welcher sich die Klasse befindet. Somit kann man klassenunabhängig auf Resourcenverzeichnisse im Jar (oder der CodeBase) zugreifen.[/EDIT]


----------



## maki (6. Nov 2012)

> Das ist nicht ganz richtig... folgendes ergibt, wenn man z.B. "String.class" übergibt 'ne NPE, im Gegensatz zu meiner "getCodeBase()"-Methode, welche das korrekte Verzeichnis der "rt.jar" liefert.


Liegt anscheinend am benutzten Classloader, aber ich hatte noch nie den Fall dass ich die rt.jar finden wollte 

Hauptsächlich geht es bei mir dann darum, den Ort der anderen jars (der App) herauszufinden, nicht die des JRE/JDK.
Die Methode welche die meisten einsetzen würden wäre dann die [c]new File(".")[/c] kiste, welche aber eben nur das Workingdirectory liefern, das Problem hat dein Code nicht, weil getDocumentBase offensichtlich nur da ist um das Working Directory herauszufinden.


----------



## Spacerat (6. Nov 2012)

maki hat gesagt.:


> Liegt anscheinend am benutzten Classloader, aber ich hatte noch nie den Fall dass ich die rt.jar finden wollte
> 
> Hauptsächlich geht es bei mir dann darum, den Ort der anderen jars (der App) herauszufinden, nicht die des JRE/JDK.
> Die Methode welche die meisten einsetzen würden wäre dann die [c]new File(".")[/c] kiste, welche aber eben nur das Workingdirectory liefern, das Problem hat dein Code nicht, weil getDocumentBase offensichtlich nur da ist um das Working Directory herauszufinden.


Keine Ahnung woher die NPE kommt, ist auch nicht wichtig. "das Problem hat mein Code nicht" ist allerdings ein Satz...
Mein Code hat auch andere Probleme nicht, z.B. "getClass().getRessource("relativeClassPath")". Damit gibt's ja ständig irgendwelche Probleme, weil Ressource-Pfade falsch angegeben werden (z.B. CP-Relativ mit vorangestelltem "/" oder Class-Relativ evtl. mit vorangestellten und widerholten "./" oder "../"). Diese Problematik wird zwar durch meinen Code (noch) nicht ersetzt, aber zumindest bietet mein Code 'ne ganze Menge Verzeichnisse mehr, die man auf der Suche nach Ressourcen abklappern kann. Für IDEs, die Klassen in einem "bin"-Verzeichnis ablegen, hat sich mein Code für mich schon als ausgesprochen nützlich erwiesen und das eigentlich nur, weil "getCodeBase()", abhängig davon ob die Klasse in einem Archiv oder einem Verzeichnis liegt, entweder die DocumentBase oder das "bin"-Verzeichnis liefert. Wenn also DocumentBase und CodeBase verschieden sind, weis man, dass die Klasse (oder die Ressource) entweder in keinem oder in einem anderen Archiv ausserhalb von DocumentBase ist.


----------



## Freetree (6. Nov 2012)

Danke für die ganzen Antworten!

Gesehen und verstanden  .

Ich habe nur eine Frage.

Und warum gibtst du den Pfad immer als Url aus, und nicht einfach als String? Hat das iregendeinen Besonderen Sinn? Ich kann keinen sehen.

Vielen Dank!!!!

Viele Grüße aus Hamburg


----------



## Spacerat (6. Nov 2012)

Jedem das Seine... für mich machen andere Sachen keinen Sinn, Strings am allerwenigsten. Was genau möchte man den mit solchen Pfaden machen, wenn nicht irgend etwas nachladen? Für mich macht's einen riesen Unterschied, ob ich mich in Programmentwicklungen darauf konzentrieren muss, ob ich eine Datei per FileStream, eine Ressource per was weis ich für'n InputStream oder rigoros mit [c]new URL(base, "newPath).openStream()[/c] arbeiten kann.
Okay, Dateien umkopieren kann man damit nicht, aber was solls. Man programmiert ja nicht immer irgendwelche Datei-Manager wie z.B. den Windows Explorer. Ansonsten eignen sich URLs für jedwede Quelle inkl. Dateisystem, Archive und Internet.


----------



## FArt (7. Nov 2012)

Spacerat hat es schon gesagt: zu einer URL gehört immer auch ein Protokoll. In der Regel möchte ich mich nicht um das Protokoll kümmern, kann dann über den passenden UrlStreamHandler auf die Ressource zugreifen ohne mich um das Protokoll kümmern zu müssen.


----------



## Prudiiarca (21. Nov 2012)

Wenn ichs recht verstanden habe (ok, eure Lösungen hab ich nicht wirklich verstanden ) habe ich ein sehr ähnliches Problem... In meinem Fall habe ich für eins meiner Spiele die Textdateien "Level1.dat" und "Level2.dat" erstellt und in dem Package <DAT> gesichert. Nun versuche ich aus dem Package <TestGame> auf diese zuzugreifen. Um deren Inhalt zu scannen habe ich folgende URL erstellt: 


```
LevelURL = getClass().getResource("/DAT/Level"+selectedLevel+".dat"); [JAVA/]. 

In Eclipse funktioniert alles, wie es soll, aber sobald ich das Projekt zur runnableJar exportiere lädt das Programm nicht mehr den Inhalt der Datei. DAT und die Files sind jedoch in der Jar...

Das selbe Problem hatte ich schon einmal mit den benötigten Bilddateien im Package <GFX>, jedoch habe ich es da mit den URLs lösen können.

Ich hoffe ihr könnt mir helfen...
```


----------



## trääät (21. Nov 2012)

verwende zwar keine IDE (wozu auch ?) ... aber meine varianten

file im jar

```
InputStream in=this.getClass().getClassLoader().getResourceAsStream("/absoluter/pfad/Datei");
```
und dann mit dem stream entsprechend weiter arbeiten

file außerhalb jar

```
File file=new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
if(file.isFile()) { file=file.getParentFile(); }
File DATEI=new File(file, "DATEI");
```
und dann entsprechend einem konstruktor der FILE erwartet übergeben oder mit streams weiter machen
ich verwende bewusst File(URI) und nicht File(String) wie hier mit URL.getPath() schon mehrfach angedeutet wurde ...
dabei hat der if-block eigentlich nur die aufgabe zu prüfen ob die klasse als CLASS so im dateisystem vorhanden ist oder in einer JAR gepackt wurde ...
würde so natürlich nicht bei einem jar-in-jar classloader funktionieren ... wobei dann ja eh die erste variante greifen würde


----------



## Spacerat (22. Nov 2012)

Prudiiarca hat gesagt.:


> ...In Eclipse funktioniert alles, wie es soll, aber sobald ich das Projekt zur runnableJar exportiere lädt das Programm nicht mehr den Inhalt der Datei. DAT und die Files sind jedoch in der Jar...





Spacerat hat gesagt.:


> Für IDEs, die Klassen in einem "bin"-Verzeichnis ablegen, hat sich mein Code für mich schon als ausgesprochen nützlich erwiesen und das eigentlich nur, weil "getCodeBase()", abhängig davon ob die Klasse in einem Archiv oder einem Verzeichnis liegt, entweder die DocumentBase oder das "bin"-Verzeichnis liefert. Wenn also DocumentBase und CodeBase verschieden sind, weis man, dass die Klasse (oder die Ressource) entweder in keinem oder in einem anderen Archiv ausserhalb von DocumentBase ist.


Das kann ich nur widerholen. Eigentlich musst du meine gepostete Klasse oben (die in dem roten Feld) kopieren und in deinen Utility-Archiven aufnehmen. Für Resourcen, die du in einem Archiv ablegen willst, verwendest du "getCodeBase()", welche dir für deine Klassen entweder das Rootverzeichnis des Archives oder das bin-Verzeichnis für Eclipse liefert. Für Resourcen im Dateipfad des Systems verwendest du "getDocumentBase()", welches dir das im Projekt eingestellte Arbeitsverzeichnis liefert. Bleibt nur noch anzumerken, dass Resourcen, welche du in einem Archiv promoten willst, in Eclipse definitiv in das src-Verzeichnis gehören. So werden sie beim Kompilieren mit in das bin-Verzeichnis kopiert und gehören damit zur CodeBase.
Und wer's nun immer noch nicht verstanden hat: Die CodeBase ist das Verzeichnis, von welchem aus die JVM Unterverzeichnisse als Paketnamen ansieht. Von dort findet man auch Resourcen, welche sich in anderen Archiven oder Projekten befinden, solange sie im Classpath sind bzw. in Eclipse als Abhängigkeit definiert wurden.
@trääät: Das mit der Protectiondomain sollte nun Geschichte sein, von wegen der oben erwähnten NPE.


----------



## trääät (22. Nov 2012)

Spacerat hat gesagt.:


> @trääät: Das mit der Protectiondomain sollte nun Geschichte sein, von wegen der oben erwähnten NPE.



gut .. das man es nicht unbedingt bei system-classes aus rt.jar nutzt weil sonst java mit ner NPE um sich wirft ... oder weil mal wieder irgendeine IDE nicht mit ordentlichem resourcen management klarkommt (noch ein grund wesshalb ich IDEs verachte und lieber mit Notepad2 und nem terminal arbeite) hat sicher seine gründe und bestimmt auch seine tweaks oder würg-a-raunds (bewusst so formuliert) ... aber in normalerweise verwendet man sowas um eben pfade zu klassen zu bekommen die eben nicht in rt.jar liegen oder über einen custom-classloader (der bei sowas natürlich auch dazwischen funken kann) geladen wurden zu bekommen ...
ich habe es zwar nicht ausprobiert was passiert wenn ich anstatt "this.getClass()" sowas wie "String.class" (sehe hier den fehler : müsste eigentlich java.lang.String.class heißen) oder mal ganz witzig übertrieben "javax.crypto.Ciper.class" übergebe ...

klar dürfte es sinn machen bei factories um zu wissen wo genau die class-files der implementierungen liegen ... aber ansonsten wüsste ich jetzt eigentlich eher weniger warum man sowas nutzen sollte außer um seinen eigenen "standort im filesystem" zu finden ... und dafür sollte Class.getProtectionDomain().getCodeBase() eigentlich ausreichen ... zumindest ist mir bisher damit noch keine NPE um die ohren geflogen ... und desshalb hatte ich es auch noch mal gepostet ...


----------



## Spacerat (22. Nov 2012)

trääät hat gesagt.:


> gut .. das man es nicht unbedingt bei system-classes aus rt.jar nutzt weil sonst java mit ner NPE um sich wirft ... oder weil mal wieder irgendeine IDE nicht mit ordentlichem resourcen management klarkommt (noch ein grund wesshalb ich IDEs verachte und lieber mit Notepad2 und nem terminal arbeite) hat sicher seine gründe und bestimmt auch seine tweaks oder würg-a-raunds (bewusst so formuliert) ... aber in normalerweise verwendet man sowas um eben pfade zu klassen zu bekommen die eben nicht in rt.jar liegen oder über einen custom-classloader (der bei sowas natürlich auch dazwischen funken kann) geladen wurden zu bekommen ...
> ich habe es zwar nicht ausprobiert was passiert wenn ich anstatt "this.getClass()" sowas wie "String.class" (sehe hier den fehler : müsste eigentlich java.lang.String.class heißen) oder mal ganz witzig übertrieben "javax.crypto.Ciper.class" übergebe ...


Welches von beidem ist denn nun ein "würg-a-raund"? Ich sag's mal so:
1. Meine "getCodeBase()"-Methode findet alles, was man auch über die PD finden würde plus das, was man per PD eben nicht findet, vorrausgesetzt, die Klasse ist bekannt.
2. Nicht die IDE muss mit Resourcenmanagement klar kommen, sondern der Benutzer der IDE (oder Notepad oder wie auch immer). Kriegsentscheidend ist letztendlich, dass Resourcen, die man in einem Archiv veröffentlichen will, in die CodeBase und solche, die man separat veröffentlichen will in die DocumentBase gehören und genau dies ist ein gerne fabrizierter Fehler z.B. in Eclipse; Wenn man Resourcen in der DocumentBase hat, findet sie die JVM natürlich nicht, wenn man per CodeBase (egal wie) drauf zugreift und umgekehrt.
3. String.class wirft diese NPE weil:


			
				Java_API hat gesagt.:
			
		

> public final CodeSource getCodeSource()
> 
> Returns the CodeSource of this domain.
> 
> ...


...maw, die CodeSource bereits null ist. PDs anderer Klassen sind ebenso nicht verpflichtet, da einen anderen Wert zurück zu geben, deswegen kann es durchaus passieren, dass man sich mit this.getClass()... usw. sein eigenes "Grab" schaufelt, z.B. wenn man sich dazu entschliesst, seinem Archiv ebenfalls eine solche PD zu verleihen.
Ausserdem heissts [c]Class.class.getProtectionDomain().getCodeSource().getLocation()[/c], aber das wolltest du auch sicher (wie bereits weiter oben) schreiben.


----------



## Prudiiarca (23. Nov 2012)

Ok, aber wie kann ich den inputStream (ich glaub ich sollte mich mal erkundigen was das genau ist...) in ein File umwandeln?
Ich versuche ja ein File zu scannen, mein Code sah dazu wie folgt aus:

```
LevelURL = getClass().getResource("/DAT/Level"+selectedLevel+".dat");
```
und diese URL wird in folgender Zeile aufgerufen:

```
Scanner loadScanner = new Scanner(new File(LevelURL.toURI()));
```

Das war meine bisherige Lösung...

Wie sähe das denn dann mit dem Stream aus?
Die Deklaration geht ja noch:

```
InputStream in = this.getClass().getClassLoader().getResourceAsStream("/DAT/Level"+selectedLevel+".dat");
```
aber wie gehts dann weiter?

schon mal danke im voraus, hoffe ihr könnt einem unwissenden wie mir helfen


----------



## Spacerat (23. Nov 2012)

Dann fangen wir mal von ganz vorne an...
Daten können per OutputStreams mittels "write()"-Methode geschrieben und per InputStream mittels "read()"-Methode wieder gelesen werden. Ein FileStream ist im Gegensatz zu einem URLStream schon wieder ein wenig spezifischer, weil dadurch die Quelle bzw. das Ziel auf ein Dateisystem begrenzt wird. Ein URLStream kann darüber hinaus aber noch aus/in Netzwerken oder Archiven lesen/schreiben.
Demnach braucht's kein [c]new FileInputStream(new File(url.toURI()))[/c] wenn man eine gültige URL hat, welche man unter anderem durch mein kleines Utility oben oder auch [c]getClass().getResource("/blaehta.taig")[/c] bekommt. Für solche URLs genügt dann [c]InputStream in = url.openStream()[/c] um an die Daten dahinter zu kommen, man muss diesen Stream halt nur genauso lesen, wie man es auch mit einem FileInputStream getan hätte.
Im Prinzip versuchst du also nicht unbedingt eine Datei zu scannen, sondern eine Resource. Der Unterschied ligt darin, dass sich eine Resource nicht nur in einem FileSystem, sondern auch in einem Archiv oder Netzwerk befinden kann. Befindet sich eine Resource also nicht in einem Filesystem, kannst du mit "new FileInput... usw" sehr lange suchen (wie auf 'ner leeren Festplatte... ewig ).


----------



## Prudiiarca (29. Dez 2012)

Ok. Soweit habe ich das (danke Spacerat) nun verstanden. Und das lesen eines Files funktioniert bei mir jetzt auch. Allerdings stehe ich jetzt grade vor einem anderen Problem:
Um eine generierte map zu speichern, habe ich die Pixel eines 500*500 großen BufferedImages in verschiedene Farben geändert. (Ausgabe über g.drawImage() klappt perfekt) In dem package Dat habe ich 6 PNGs (World1.png etc.), auch 500*500 Pixel groß. Nun möchte ich (nach Auswahl eines der Bilder, in dem Beispiel Wolrd1.png) die map in das png übertragen.
Ich benutze den Quellcode aus "Java ist auch eine Insel". 

Anmerkung vorweg: die benutze URL funktioniert.


```
String typ = ".png";
```


```
File datei = null;     //= null um die variable initialisiert zu haben
```


```
datei = new File(World1URL.toURI()); //in try/catch block
```


```
ImageIO.write(image, typ, datei);
```

Das Bild wird allerdings nicht geändert...?
auch mit: 
	
	
	
	





```
datei = new File("C://newpic".concat(typ));
```
 statt URL passiert nichts. Nach Sicherung lässt sich weder das newpic.png noch das neue World1.png finden...

Was mache ich denn falsch?


----------



## Timothy Truckle (29. Dez 2012)

Prudiiarca hat gesagt.:


> ```
> File datei = null;     //= null um die variable initialisiert zu haben
> ```


 Bitte lass das. Während der Laufzeit ist es kein unterschied, ob Du das 
	
	
	
	





```
= null
```
 hin schreibst oder nicht, aber ohne 
	
	
	
	





```
= null
```
 kann der Compiler feststellen, ob die Variable initialisiert wurde oder nicht. Dass ist bei umfangreichen If/else Konstrukten hilfreich. 



Prudiiarca hat gesagt.:


> ```
> File datei = new File(World1URL.toURI()); //in try/catch block
> ```


Wenn due die Deklaration von 
	
	
	
	





```
datei
```
aus dem try rausziehen wolltes, um dann nach dem Block damit zu arbeiten solltest Du den completten try/catch block in eine Methode auslagen und die IOExeption in eine RuntimeException umwandeln:
	
	
	
	





```
File datei = getFile(World1URL.toURI());

//...

private File getFile(Url url){
  try{
    return new File(url);
  } catch (Exception ex){
     throw new RuntimeException("could not open File "+url, ex);
  }
}
```



Prudiiarca hat gesagt.:


> ```
> ImageIO.write(image, typ, datei);
> ```
> Das Bild wird allerdings nicht geändert...?


Doch, wird es, aber im Arbeitsspeicher der JVM.


Prudiiarca hat gesagt.:


> auch mit:
> 
> 
> 
> ...


Was bedeutet "nach Sicherung"?


```
new File()
```
 legt keine neue Datei im Dateisystem an! das tut erst 
	
	
	
	





```
datei.newFile()
```
...

bye
TT


----------



## Prudiiarca (30. Dez 2012)

Mhhh. ok. das file wird jetzt auch auf dem desktop abgelegt, aber es ist leer!? ???:L sorry das ich euch hier so belästige, aber meine java kenntnisse beruhen auf learning-by-doing, daher habe ich einige lücken. danke für eure hilfe!
Hier der quellcode:

```
String typ = ".png";
File datei;
datei = new File("C:/users/prudii/desktop/newpic".concat(typ)); /*später werde ich mit urls arbeiten, der
absolute pfad dient zu testzwecken*/
ImageIO.write(image, typ, datei); //schreibt doch das !gefüllte! image in die datei, oder?
datei.createNewFile();
```

Was mach ich jetzt schon wieder falsch?
Und muss ich auf irgenwas achten, wenn ich ein schon bestehendes Bild überschreiben will? oder geht das einfach so wie oben?


----------



## Spacerat (30. Dez 2012)

1. Du solltest gleich mit URLs (in Verbindung mit URLConnection und "getOutputStream()") arbeiten, denn
2. Bei URLs abeitest du definitiv mit Streams, die man explizit schliessen muss, damit die darunter liegenden Puffer "abgeschlossen" werden können. Tut man dieses nicht
3. haben Dateien entweder keinen Inhalt oder nur einen Teil vom erwarteten.

Leider verwende ich ImageIO schon so lange nicht mehr, dass mir dessen Methode "write(image, type, file)" geläufig sein könnte. Soweit ich weiss verwendete ImageIO nur Streams, die man explizit schliessen musste, sonst bekam man ein Problem wie deines. Bei Fileobjekten kann ich mir das aber kaum vorstellen.


----------



## Prudiiarca (1. Jan 2013)

Also, die deklaration müsste so gehen (SlotURL zeigt auf den jeweiligen speicherplatz des bildes): 

```
URLConnection connection;
connection = SlotURL.openConnection();
OutputStream output;
output = connection.getOutputStream();
    	
output.close();
```

aber wie kann ich das jetzt bei imageIO.write verwenden?
habe versucht einfach mit 
	
	
	
	





```
imageIO.write(image,".png",output);
```
 zu arbeiten, das ergibt jedoch
eine java.net.UnknownServiceException: protocol doesn't support output

trotzdem danke für die Hilfestellung...


----------



## Spacerat (1. Jan 2013)

Afaik unterstützt Java von Haus aus die Protokolle HTTP, JAR, FILE und FTP (Datenbank-Protokolle nicht mitgezählt). Schreibrechte hängen dabei halt von den jeweiligen Benutzerrechten ab, bzw. beim HTTP-Protokoll, ob auf dem Server die PUT-Methode aktiviert wurde, was sie in der Regel aber nicht ist. Ob das Schreiben in ein JAR-Archiv überhaupt per Protokoll funktioniert, kann ich nicht sagen, ich verwendete es bisher nur zum Lesen. Auf jeden Fall solltest du diese Schreibrechte der jeweiligen URLConection vorher prüfen. Das du die write-Methode jedoch vor dem close des OutputStreams aufrufen musst, muss glaub' ich nicht erwähnt werden, dein Codebeispiel scheint dieser Analogie jedenfalls nicht zu folgen.
Um das Ganze zu testen, kannst du dir ja von File-Objekten zu Dateien, zu denen du sicher weisst, dass du da reinschreiben kannst URLs erstellen lassen, z.B. mit "File.createTempFile().toURI().toURL()". Danach kannst du andere Protokolle testen und ggf. mit "setDoOutput(true)" bei den URLConnections nachhelfen.


----------



## Prudiiarca (2. Jan 2013)

Oh. habe grade hier das eigentliche problem entdeckt:
Ich versuche in der tat während der laufzeit mein bild innerhalb der jar zu speichern. bei meiner Programmstruktur sind innerhalb des src folders die beiden packages MAIN für die klassen und DAT für Dateien. In diesem DAT Folder ist auch das zu speichernde bild. meine frage ist jetzt: gibt es inzwischen eine möglichkeit in die laufende jar zu schreiben, oder muss ich einen ordner in demselben verzeichnis platzieren und aus diesem die bilder auslesen und speichern?
und wenn letzteres der fall ist: ich benutze eclipse und das projekt befindet sich in dem ordner workspace. muss der DAT ordner sich nun in dem workspace-ordner oder im projekt-ordner befinden?


----------



## Spacerat (3. Jan 2013)

Also wie in dem Thread dort beschrieben, kann in ein geladenes Archiv nicht geschieben werden, da nutzen einem auch Zugriffsrechte nichts.
Gerade für Eclipse entstand meine oben veröffentlichte Klasse, denn Eclipse führt Java-Applikationen im Dateipfad aus, ohne vorher Archive draus zu machen. Das ist ehrlich gesagt auch ein Pitfall in Eclipse, weil man in der Testphase noch fröhlich Ressourcen in den bin-Ordner schreiben kann, dies aber nicht mehr funktioniert, sobald man ein Archiv draus gemacht hat. Wo der schreibbare Ordner letztendlich zu liegen hat, ist die Sache des Entwicklers. Dazu eignet sich jedes erreichbare Verzeichnis (vorzugsweise also DocumentBase) bis auf die CodeBase.
Für Eclipse ist die DocumentBase der Projektordner (voreingestellt) und die Codebase das sich darin befindliche bin-Verzeichnis.


----------



## Timothy Truckle (3. Jan 2013)

Spacerat hat gesagt.:


> Wo der schreibbare Ordner letztendlich zu liegen hat, ist die Sache des Entwicklers. Dazu eignet sich jedes erreichbare Verzeichnis (vorzugsweise also DocumentBase


Es gibt prinzipiell nur 2 Verzeichnisse bei denen wir Entwickler  davon ausgehen können, dass diese zur Laufzeit schreibbar sind: 
	
	
	
	





```
System.getProperty("user.home")
```
 und 
	
	
	
	





```
System.getProperty("java.io.temp")
```
. Bei allen anderen Verzeichnissen können uns administrative Einschränkungen auf die Füße fallen. Deshalb sollte man in seiner Installation-Anleitung deutlich darauf hinweisen, wenn man in andere Verzeichnisse schreiben will.

bye
TT


----------



## Spacerat (3. Jan 2013)

Also ich hatte bisher auch unter Win7 nie Probleme in das Arbeitsverzeichnis (DocumentBase) zu schreiben, wenn ich nicht gerade in "Programme" oder andere Win7 Ordner installiert habe. Was bei Win7 auf keinen Fall geht, sind Rootverzeichnisse von Laufwerken, Ornerverknüpfungen und jene mit administrativen oder Benutzereinschränkungen. Aber klar, für benutzerbezogene Konfigurationsdateien eignet sich UserHome natürlich besser, allgemein verwendete Ressourcen aber gehören ins Arbeitsverzeichnis. Wenn ich darein schreiben will, werde ich mit Sicherheit niemanden drauf hinweisen wollen, andere Apps machen das ja auch nicht.


----------



## Timothy Truckle (3. Jan 2013)

Spacerat hat gesagt.:


> allgemein verwendete Ressourcen aber gehören ins Arbeitsverzeichnis.


Solange sie nicht geschrieben werden müssen (einmalige Änderung wärend der Installation mal ausgenommen)


Spacerat hat gesagt.:


> Wenn ich darein schreiben will, werde ich mit Sicherheit niemanden drauf hinweisen wollen, andere Apps machen das ja auch nicht.


Dass sind aber auch genau die Apps, die die Hufe hochreißen, wenn der Admin mal ein vernünftiges Rechtekonzept aufsetzt, oder die App von einer RO-Netzwerkfreigbe laufen muss...

Wer nur für Heinanwender Coded, mag mit Deiner Strategie ja gut fahren, im Unternehmensumfeld ist das aber ein wirtschaftliches Risiko. 

bye
TT


----------



## Spacerat (3. Jan 2013)

Timothy Truckle hat gesagt.:


> Wer nur für Heinanwender Coded, mag mit Deiner Strategie ja gut fahren, im Unternehmensumfeld ist das aber ein wirtschaftliches Risiko.


Dieses "nur", klingt ein wenig herablassend. Ich nehme mal an, dass unheimlich viele "nur" für den allgemeinen Hausgebrauch (kommerziell) ihrer SW coden und nicht unbedingt darauf aus sind, speziell Software zu schreiben, die auf irgendwelchen Buisnessrechnern laufen muss. Erst wenn das der Fall ist, halte auch ich die Erwähnung der beiden von dir benannten Verzeichnisse für angemessen.


----------



## Timothy Truckle (3. Jan 2013)

Spacerat hat gesagt.:


> Dieses "nur", klingt ein wenig herablassend.


Dass "nur" soll bedeuten, dass die Rechte auf Privatrechnern idR. großzügiger sind als auf Firmenrechnern.

Verlassen würde ich  ich darauf aber nicht...

bye
TT


----------



## Prudiiarca (5. Jan 2013)

Irgendwo ist der Wurm drin ;( Habe jetzt einen ordner dat im selben ordner erstellt, in der die jar ist. 
um das bild World1.png auszulesen habe ich folgendes geschrieben (ich benutze deine Methode):

```
String SlotPath = getDocumentBase().toString()+"dat/World1.png";
BufferedImage image = ImageIO.read(new File(SlotPath));
```
an der stelle gibt die konsole mir den pfad aus: file:/C:/Users/Benutzer/workspace/Game/dat/World1.png
javax.imageio.IIOException: Can't read input file!
was ist jetzt schon wieder? das bild World1.png existiert in dem ausgegebenen ordner...


----------



## Spacerat (5. Jan 2013)

Ist ja auch klar, ein File Object sollte man nicht mit einem URL-String instanzieren. Aber ImageIO kann ja zum Glück auch URLs und URLs sind, was Zieländerungen angeht, ähnlich flexibel wie File.

```
try {
  URL slotPath = new URL(getDocumentBase(), "dat/World1.png");
  BufferedImage image = ImageIO.read(slotPath);
} catch(MalformedURLException e) {
  // kann bei dieser Art der Instanzierung eigentlich gar nicht auftreten
  e.printStackTrace();
}
```


----------



## Prudiiarca (6. Jan 2013)

habe es exakt so gemacht und es funktioniert in eclipse bestens. jetzt habe ich das projekt exportiert: auf meinem desktop liegt jetzt in einem ordner die jar und der ordner dat. doch die dokument base weist auf c:/windows/sysem32 ?? warum? in eclipse weist es auf die richtige stelle im programmordner, nach verlagerung nicht mehr.. ich habe die jar über rechtsklick-öffnen mit-java gestartet.


----------



## Spacerat (6. Jan 2013)

Ich bin mir zwar nicht sicher, aber mit dieser Öffnen-Methode, änderst du auch das Arbeitsverzeichnis. Kann es sein, dass sich ein Exemplar der "java.exe" in "windows/system32" befindet?
Wenn der simple Doppelklickstart fehlschlägt, weil da jemand seine Konfiguration zerkloppt hat, hat man genau zwei Möglichkeiten:
1. Die funktionierende Konfiguration widerherstellen, ggf. Java neu installieren, oder
2. Man erstellt sich eine Verknüpfung. In dieser kann man Kommandozeile, Argumente und Arbeitsverzeichniss separat festlegen.


----------



## Prudiiarca (6. Jan 2013)

Ich muss per rechtsklick starten, weil die jar sonst mit winrar geöffnet würde. und es liegt eine java.exe in c:/windows/system32... wie könnte man denn die jar über kommandozeile starten? bzw. wie sähe dann der code für die batch datei aus um das arbeitsverzeichnis festzulegen?

EDIT: habe es per batch datei (@echo off java -jar "Game.jar" pause) hinbekommen. 

1000 Dank an dich für deine freundliche und geduldige Hilfe.


----------

