Java Dateien (File-Klasse) per ObjektStreams verschicken möglich?

Bender86

Lieutenant
Registriert
Jan. 2007
Beiträge
718
Hallo zusammen!

Ich arbeite grade an einer Netzwerk Komponente für eine Client/Server Anwendung die wir in einem Softwareprojekt erstellen sollen. Was ich bisher gemacht habe ist:

- Client und Server haben je einen Netzwerk Manager
- Beide Manager haben eine Methode send(Sendable) und recieve(), über die Objekte verschickt werden können die von einer eigenen "Sendable" Klasse abgeleitet sind.
- Wenn beim Client/Server ein Objekt an kommt, wird eine andere Komponente mittels Observer/Observable darüber informiert und diese holt sich das empfangene Objekt ab.

Nun ist es so, dass auch Dateien über das Netzwerk verschickt werden sollen. Ich hatte mir überlegt einfach eine Wrapperklasse um "File" zu machen und diese übers Netzwerk zu verschicken. Leider wird dabei aber nur quasi ein Verweis auf die Datei geschickt, nicht die Datei selber.

Im lokalen Test auf einem Rechner funktioniert das also, wenn die Datei von einem anderen Rechner geholt werden soll nicht mehr.

Daher meine Frage, gibt es eine Möglichkeit Dateien als ganzen über einen ObjektStream zu verschicken oder muss ich in jedem Fall dafür einen normalen/buffered OutputStream verwenden? Es wäre halt praktisch wenn ich das als Objekt verschicken könnte und die ganze Datei geht über den Stream mit, weil ich dann schon vorhandene Schnittstellen (send(Sendable)) benutzen könnte.

Danke schonmal für Vorschläge,
Bender
 
Du musst die Datei einfach auslesen, und die ausgelesen Bytes an den Empfänger schicken. Dieser nimmt die dann entgegen und setzt die einzelnen Bytes zu einer Datei wieder zusammen.
 
Ja die Frage ist ja wie mache ich sowas über ObjektOutputStreams? Bzw. ist das möglich irgendwie möglich?

Wie ich eine Datei Byteweise verschicke weiss ich ja. Aber ich möchte (falls möglich) eine vorhandene Schnittstelle benutzen, die aber mit ObjectStreams arbeitet.
 
Weißt du überhaupt was ein ObjectOutputStream ist? Die Klasse dient dazu Java Objekte und primitive Datentypen zu serialisieren.

Welchen Sinn hätte es beliebige Dateien damit über einen SocketStream zu versenden?

Das nur ein Verweis bei File gesendet wird, dürfte auch nicht verwundern, schließlich handelt es sich bei der Klasse um ein gewöhnliches Handle.

Nebenbei sollte bei Fragen auch stets ein kurzer Code gepostet werden, der sagt nämlich meist mehr als 1000 Worte.
 
Mir ist schon klar das ein ObjectStream dafür da ist Objekte zu verschicken. Aber auch da müssen ja Daten von A nach B kommen. Desshalb dachte ich mir das es vielleicht einen Trick gibt wie man auch eine ganze Datei mit schicken kann auf die verwiesen wird.

Code sollte dabei unerheblich sein, aber vom Prinzip her so:

Code:
public class MyFile {

	private File file;
	
	public MyFile(File file) {
		this.file = file;
	}
	
}

public class NetworkManager() {
	
	public static void main(String[] args) {
	
		Socket socket = // Verbindung zum Server ...
		
		ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream());
	
		os.wirteObject(new MyFile(new File(/home/user/file.pdf)));
		
	}
	
}

Bei mir ist das ganze nun etwas in verschiedene Methoden und Klassen verteilt aber das tut nun nichts zur Sache.

Eine andere Frage zu dem Thema wäre noch, ob solche Streams alle auf denselben "Kanal" schreiben. Sprich wenn ich die Situation habe:

- Client schreibt ein Objekt auf einen ObjectOutputStream der zu Socket A gehört
- Client schreibt ein byte[] auf einen BufferedOutputStream der zu Socket A gehört
- Server liest 1000 bytes vom BufferedInputStream der zu Socket A gehört
- Server liest Object vom ObjectInputStream der zu Socket A gehört.

Bekomme ich dann einmal 1000 bytes über den BufferedInputStream und das richtige Objekt über den ObjectInputStream oder werden erst 1000 bytes "weggelesen", die möglicherweise das zuerst gesendete Objekt erhalten und der ObjectInputStream liest irgendwelchen Murks?
 
So kann das sicherlich nicht funktionieren. Du versuchst ein File Handle, das du gewrappt hast zu versenden. Das einzige was du in den ObjectOutputStream schreibst ist ein serialisiertes File.

Ein File ist ein abstrakter Pfadname, nichts weiter. Du sendest damit nicht den Inhalt der Datei! Du musst die Daten vorher auslesen.

Eine Datei kann man so senden.

PHP:
File myFile = new File("/home/user/file.pdf");
byte[] mybytearray  = new byte [(int)myFile.length()];
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read(mybytearray, 0, mybytearray.length);    // Datei auslesen!
OutputStream os = socket.getOutputStream();
os.write(mybytearray, 0, mybytearray.length);   // Senden
os.flush();   // Verbleibende Daten explizit flushen
socket.close();
Ergänzung ()

Bender86 schrieb:
Eine andere Frage zu dem Thema wäre noch, ob solche Streams alle auf denselben "Kanal" schreiben. Sprich wenn ich die Situation habe:

- Client schreibt ein Objekt auf einen ObjectOutputStream der zu Socket A gehört
- Client schreibt ein byte[] auf einen BufferedOutputStream der zu Socket A gehört
- Server liest 1000 bytes vom BufferedInputStream der zu Socket A gehört
- Server liest Object vom ObjectInputStream der zu Socket A gehört.

Bekomme ich dann einmal 1000 bytes über den BufferedInputStream und das richtige Objekt über den ObjectInputStream oder werden erst 1000 bytes "weggelesen", die möglicherweise das zuerst gesendete Objekt erhalten und der ObjectInputStream liest irgendwelchen Murks?

1. Ein Socket ist immer an einen OutputStream gebunden. Ein Socket kennt keinen ObjectStream.
2. Wenn du eine TCP Verbindung etabliert hast, zu der Socket A gehört, dann landet alles was du über diesen Socket sendest im TCP-Buffer des Empfängers. Es liegt beim Empfänger die Daten mit read() korrekt auszulesen.
3. TCP kennt keine Nachrichtengrenzen! Das heißt, wenn du 1000 bytes sendest, und gleich danach ein serialisiertes Objekt, dann muss der Empfänger exakt 1000 bytes einlesen und anschließend das serialisierte Objekt. Undzwar in derselben Reihenfolge und mit derselben Anzahl über read(). Der Empfänger erhält nur Bytes, sie zu lesen und zu interpretieren ist die Aufgabe des Anwendungsprotokolls.

Ich empfehle dir eine kurze Einführung, damit du das Prinzip verstehst!

http://www.codeplanet.eu/tutorials/java/57-battleship.html?start=4
 
Zuletzt bearbeitet:
Hm das habe ich befürchtet. Was wäre denn wenn ich die Datei in ein byte-Array stecke und dann das byte-Array in die Wrapperklasse stecke und verschicke. Oder wird dann nur ein Verweis auf den Speicherbereich verschickt und die Datei bleibt wieder beim Sender?
 
Deine Klasse MyFile muss dann aber eine Methode toBytes() besitzen oder das Interface Serializable implementieren. Dann wäre es möglich es über ObjectOutputStream zu schreiben und später beim Empfänger wieder zu deserialisieren.

In der Klasse MyFile muss dann aber ein Member byte[] existieren, in dem die Daten der Datei stehen. Die Klasse darf allerdings keine mutable Objekte als Member besitzen, sonst musst du sie speziell serialisieren.

Mit writeObject() kannst du eine Instanz der Klasse MyFile dann direkt in den OutputStream des Sockets schreiben. Beim Empfänger wird es dann mit readObject() wieder in eine MyFile Instanz gecasted.

Alternativ dazu, kannst du das erwähnte toBytes() nutzen. Du schreibst dann einfach socket.write(MyFile.toBytes());.

Ich verweise nochmal auf http://www.codeplanet.eu/tutorials/java/57-battleship.html?start=4.

Dort kann man sehr schön sehen, wie Nachrichten übertragen werden, und was es bei TCP zu beachten gibt, wenn man Daten senden und empfangen will.
 
Zuletzt bearbeitet:
Serializable ist die MyFile schon. Wie gesagt das sollte nur ein beispielhafter Codeschnipsel sein. In meinem Programm erbt die Wrapper Klasse für die Datei von einer Klasse "Sendable" und die wiederrum implementiert das Serializable Interface. (Da ich verschiedene Klassen über den Object Stream verschicke)

Das Tutorial muss ich mir später mal durchlesen da ich im Moment wenig Zeit habe.
 
Also ich habe nun die Datei einfach in ein Byte Array geschrieben und so kann ich sie dann auch über den ObjektStream schicken. Sollte mal jemand mit demselben Problem auf den Thread stoßen, so sieht in etwa meine Lösung aus:

Code:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;


public class WrapperFile {
	
	private File wrappedFile;
	private byte[] byteRepresentation;
	
	public WrapperFile(File fileToWrap) {
		
		this.wrappedFile = fileToWrap;
	}
	
	/**
	 * Schreibt die private File in ein byte[].
	 */
	public void fileToByteArray() throws IOException {
		
		byteRepresentation = new byte[(int) wrappedFile.length()];
		FileInputStream fileInputStream = new FileInputStream(wrappedFile);
		fileInputStream.read(byteRepresentation);
		fileInputStream.close();
		
	}
	
	/**
	 * Schreibt das byte[] zurueck in die angegebene File.
	 */
	public void byteArrayToFile(File file) throws FileNotFoundException,
	  											  IOException {

		FileOutputStream fileOutputStream = new FileOutputStream(file);
		fileOutputStream.write(byteRepresentation);
		fileOutputStream.close();

	}

}
 
Hi,

auch wenn der Thread schon paar Tage alt ist:
Vielen Dank, die Lösung ist genau die, die ich gesucht habe!

Mick
 
Zurück
Oben