Zum Inhalt springen

FileInputStream und FileOutputStream: bytes werden nicht richtig kopiert


Kadaj

Empfohlene Beiträge

Hallo, ich versuche grade eine Datei stückchenweise in ein byte-array einzulesen und in eine andere Datei zu schreiben. Das funktioniert aber nur, wenn die Datei bis 1024 Byte groß ist, muss das Programm sie zerlegen, dann ist sie unbrauchbar. Hier der Code dazu:


FileInputStream fis = new FileInputStream(args[2]);
byte[] DataBytes = new byte[1024]; // Bytes für die Daten
int PacketNum = 0, NumCounter=1;

PacketNum = (int)filesize/1024;
if( filesize%1024 != 0 )
PacketNum++;

int Lastsize = (int)filesize%1024;

FileOutputStream fos = new FileOutputStream("clienttest");
for(; NumCounter <= PacketNum;NumCounter++ ){

while( fis.read( DataBytes ) != -1 ){}

if( NumCounter == PacketNum )
fos.write(DataBytes, 0, Lastsize);
else
fos.write(DataBytes);

}

[/PHP]

Und so sieht der Inhalt der Datei aus, die ich zum testen angebe:

[code] start Ich bin ein Text Das sind 1024 Byte Ich bin ein Text Das sind 2048 Byte und noch ein wenig... end [/code]
Das ist die Kopie:
[code] und noch ein wenig... end Das sind 2048 Byte und noch ein wenig... end Das sind 2048 Byte und noch ein wenig... end [/code]

Ich kann mir absolut nicht erklären wie das zustande kommt. Hat jemand von euch einen Durchblick?

Gruß

Link zu diesem Kommentar
Auf anderen Seiten teilen

Seh ich denn beim debug was in der datei passiert? Ich weiß leider ohnehin nicht wie ich das unter fedora-eclipse mache :/

Jedenfalls hab ich Beispiel im Netz gefunden und mein Fehler ist hier:


//while( fis.read( DataBytes ) != -1 ){}
fis.read( DataBytes );
[/PHP]

Ich dachte es wird immer nur ein byte beim Aufruf in das byte geschrieben und -1 wird zurückgegeben, wenn das byte-array voll ist. Aber das byte wird gleich vollgeschrieben und -1 wird zurückgegeben, wenn das Dateiende erreicht wurde. Das habe ich an der Stelle wohl damit verwechselt:

[PHP]
int c;
while ((c = in.read()) != -1) {
...

Jedenfalls wird die Kopie jetzt fehlerfrei kopiert. :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Gast runtimeterror

Hier noch mein üblicher Senf dazu :)

while( fis.read( DataBytes ) != -1 ){}
Das ist gefährlich, da du nicht wissen kannst, wie viele Bytes gelesen wurden. Wenn z.B. nur 100 Bytes gelesen wurden, dann schreibt der später dennoch die vollen 1024 bytes ins Ziel. Man sollte immer den Rückgabewert der read-Methode auslesen und diesen für den späteren zugriff auf den Buffer verwenden. Und noch ein kleiner Verbesserungsvorschlag:
PacketNum = (int)filesize/1024;

if( filesize%1024 != 0 )

     PacketNum++;
Geht kürzer:
final int BUFFER_SIZE = 1024;

PacketNum = (filesize + BUFFER_SIZE - 1) / BUFFER_SIZE

Mit dem Trick funktioniert das Aufrunden wie gewünscht.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das ist gefährlich, da du nicht wissen kannst, wie viele Bytes gelesen wurden. Wenn z.B. nur 100 Bytes gelesen wurden, dann schreibt der später dennoch die vollen 1024 bytes ins Ziel. Man sollte immer den Rückgabewert der read-Methode auslesen und diesen für den späteren zugriff auf den Buffer verwenden.

Ja, das ist mir auch aufgefallen. Ich hatte mich anfangs gewundert, dass die Dateien viel größer sind, bis mir bei der Textdatei aufgefallen ist, dass noch Nullen am Ende stehen. Ich hab es dann so gelöst, dass mein Client und mein Server anhand der Dateigröße im ersten Paket einfach berechnen wie viele bytes sie einlesen müssen.

Geht kürzer:

final int BUFFER_SIZE = 1024;

PacketNum = (filesize + BUFFER_SIZE - 1) / BUFFER_SIZE

Mit dem Trick funktioniert das Aufrunden wie gewünscht.

Gut zu wissen, danke :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Gast runtimeterror

Ich weiß nicht, ob du mich richtig verstanden hast: Jeder read()-Aufruf kann eine zufällige Anzahl an Bytes einlesen. Das heißt vom buffer sind immer nur die ersten n Bytes korrekt. Der Rest ist Müll.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Kann das passieren? Also bei mir funktioniert es sowohl mit Bildern als auch mit mp3-Dateien von einem Rechner zum anderen.

Laut doc auf oracle.com:

read(byte[] B)

Reads up to b.length bytes of data from this input stream into an array of bytes.

Vielleicht passiert das, wenn man im Laufe des Quellcodes diesem Array mal ein anderes zuweist mit "=", dann verändert sich nämlich auch dessen Größe. Anders könnte ich mir das nicht erklären ;)

Gruß

Link zu diesem Kommentar
Auf anderen Seiten teilen

Gast runtimeterror

Steht eigentlich da: "Reads up to b.length bytes" = "liest bis zu b.length Bytes"

Siehe auch: InputStream (Java Platform SE 7 )[]%29

"The number of bytes read is, at most, equal to the length of b."

(Die Forensoftware macht den Link kaputt:)

http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#read%28byte[]%29

Falls dein Eingabemedium langsamer ist (Modem, Serielle Schnittstelle, Tastatur, Temperatursensor, ...) dann liefert die Methode u.U. nur so viele Bytes, wie das Eingabegerät gerade ohne nennenswerte Verzögerung liefern kann.

Bin beim Implementieren eines HTTP-Stacks damit mal böse auf die Schnauze gefallen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Gast runtimeterror

Hier mal ein (ungetestetes) Beispiel in dem zunächst alle Bytes in einen Puffer eingelesen werden und dann geschrieben werden:

final int bytesExpected = 1234;

final byte[] buffer = new byte[bytesExpected];

int bufferOffset = 0;

while (bufferOffset < buffer.length) {

    final int bytesRead = in.read(buffer, bufferOffset, buffer.length - bufferOffset);

    if (bytesRead == -1) throw new Error("Zu wenig Eingabedaten");

    bufferOffset += bytesRead;

}


out.write(buffer);
Und hier die (ebenfalls ungetestete) performante und latenzarme Variante, welche den Stream mit einem kleinen Buffer kopiert:
final int bytesExpected = 12345;

final int BUFFER_SIZE = 4096;

final byte[] buffer = new byte[BUFFER_SIZE];

// Anzahl bislang gelesener Bytes

int byteCount = 0;

while (byteCount < bytesExpected) {

    // Nicht mehr Bytes lesen, als in den Buffer passen (buffer.length)

    // Nicht mehr Bytes lesen, als noch benötigt (bytesExpected - byteCount)

    final int bytesRead = in.read(buffer, 0, Math.min(buffer.length, bytesExpected - byteCount));

    if (bytesRead == -1) throw new Error("Zu wenig Eingabedaten");

    // Nur so viele Bytes schreiben, wie zuvor gelesen wurden

    out.write(buffer, 0, bytesRead);

    byteCount += bytesRead;

}

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...