Java. Reading from BufferedInputStream and write to FileOutputStream

12,692

Solution 1

You have to remember the amount of bytes you read into your buffer and must only write those bytes back. Like this here:

int got;
while ((got = bis.read(res)) != -1) {

    fout.write(res, 0, got);
}

Solution 2

Not the answer, but a suggestion...

This doesn't look right. Basically, you are reading a an array of bytes up to a maximum of bufferSize, but then just writing a single byte to the output stream. You are also risking an index out of bounds error because you are incrementing the i on each loop unbound...

while (bis.read(res) != -1) {
    fout.write(res[i]);
    i++;
}

Instead you should be using something more like...

int bytesRead = -1;
while ((bytesRead = bis.read(res)) != -1) {
    fout.write(res, 0, bytesRead);
}

Which will write a the byte array up to the number of bytes read...

The next problem is, you need some way to know when the you've reached the end of the file.

Now, you can put in some kind of terminator character but that could, potentially, corrupt the output file.

Now, because I'm a UI developer, I like to know how much I'm downloading. So, the first then you could send down the stream is the number of bytes to expect, then you can simply keep a count of the bytes read and stop when you've reached your limit...

This would require a slight change to your server, it would need to be able to send a long value, indicating the size of the image (in bytes) to be read and a \n

BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String header = br.readLine();
long expectedBytes = Long.parseLong(header);

Then you would simply loop until you have the required number of bytes...

int bytesRead = 0;
int totalBytes = 0;
while (totalBytes < expectedBytes) {
    bytesRead = bis.read(res);
    fout.write(res, 0, bytesRead);
    totalBytes += expectedBytes;
}

// Flush and close your steams as required.

I did something simular for a screen sharing question

Solution 3

Memorize the canonical way to copy streams in Java:

while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

Use any buffer size greater than zero, typically 4k or 8k.

Share:
12,692
sanatik
Author by

sanatik

Student of International IT university, Almaty

Updated on June 05, 2022

Comments

  • sanatik
    sanatik almost 2 years

    I'm trying to write client and server side on java. Server side is ok (checked on several clients).

    So, the problem is on client side. I allocate memory for bytearray, read from BufferedInputStream and write to that bytearray. Then writing from bytearray to FileOutputStream. Everything is ok but free space of bytearray is filling by NULLs, so received file is not correct(e.g. images).

    I found 2 decision of that problem:

    1. Read to bytearray till the end of file(but I don't know where the end of file)
    2. Read from BufferedInputStream to FileInputStream, but it doesn't work:

    I actually need receive header and file. Output header to console and write the file to the disc.

    Full Source

    public class SClient {
    private static int bufferSize = 8192;
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        System.out.println("Enter the address:");
        BufferedReader bufferRead = new BufferedReader
                    (new InputStreamReader(System.in));
    
        try {
            String address = bufferRead.readLine();
            System.out.println("Enter the extention of receiving file:");
            String fileExt = bufferRead.readLine();
            // TODO code application logic here
            Socket socket = new Socket(address,4040);
            BufferedInputStream bis = new BufferedInputStream
                    (socket.getInputStream());
    
            BufferedOutputStream bout = new BufferedOutputStream
                    (socket.getOutputStream());
            System.out.println("Enter the request:");
            String message = bufferRead.readLine();// GET /index.html HTTP/1.0
    
            System.out.println("Header read");
            if(message!=null){
                bout.write(message.getBytes());
            }
            FileOutputStream fout = new FileOutputStream("out"+fileExt);
            String s1 = "\r\n\r\n";
            bout.write(s1.getBytes());
            bout.flush();
            System.out.println("Header sent");
    
            byte[] res = new byte[bufferSize];
            int got;
            while((got = bis.read(res))!=-1){
                fout.write(res,0,got);
            }
            fout.close();
            bout.flush();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
      }
    }
    

    Server side source:

        String endLine = "\r\n";
        File f = new File(fileName);
        FileInputStream fstream;
        fstream = new FileInputStream(f);
        response = "HTTP/1.0 200 OK" + endLine;
        header = "Content-type: "+ contentType + endLine + "Content-length: " + f.length() + endLine + endLine;
        bout.write(response.getBytes());
        bout.write(header.getBytes());
        while(fstream.read(buffer) != -1) {
            bout.write(buffer);
        }
        System.out.println("Message sent");
        bout.flush();
        socket.close();
    
  • sanatik
    sanatik over 10 years
    Thanks, your code helped me to remove NULLs after Header, but image still doesn't show, because I've nulls at the end of file.
  • MadProgrammer
    MadProgrammer over 10 years
    @sanatik Socket streams won't end with -1 because until they are closed, they don't end. Instead they rely on terminator characters (for example) or known byte lengths...
  • Matthias
    Matthias over 10 years
    @sanatik I guess you already close the file properly, so you are not seeing a partially flushed file or something like that. I also asume you just want to send a single File through your socket, not anything else. Because the code reads everything from the socket until it closes. Maybe show the code to load / show the image or explain on what you want to do so I can check if anything is wrong with that.
  • user207421
    user207421 over 10 years
    @sanatik If you have nulls at the end of the file you haven't used this code. You need to use it at both ends.
  • sanatik
    sanatik over 10 years
    @EJP what means both ends?
  • sanatik
    sanatik over 10 years
    @Matthias look full source above
  • sanatik
    sanatik over 10 years
    As I read, reading by bytes is the best way. Using BufferedReader can make some misrepresentations.
  • Matthias
    Matthias over 10 years
    @sanatik client code looks OKish, how does your server send the file. You have to write the bytes somewhere, could you also share that code piece? Probably the error lies there.
  • MadProgrammer
    MadProgrammer over 10 years
    @sanatik You might need to expand that a little, I'm not sure I understand what you're trying to say. Yes, reading/writing a byte array is more efficient then simply reading/writing a single byte, if the buffer size works with the underlying network requirements
  • sanatik
    sanatik over 10 years
    @Matthias have a look
  • Matthias
    Matthias over 10 years
    @sanatik aah ok. So your server is not sending an image file or something, it is sending a http response. So you expect to receive a html file on your client. If the html you receive is invalid, then cause is probably a similiar error while reading fstream and writing this data to your socket. If you watch this in a browser, the browser might strip additional data since you send a specific content length in your http header. If you store the response in a file, you however see the trailing null values which the browser ignores?
  • MadProgrammer
    MadProgrammer over 10 years
    @sanatik IF your server is adhering to the correct http requirements, then it should be sending a bunch of header information BEFORE the image, for example HTTP/1.1 200 OK Date: Fri, 13 Sep 2013 07:26:10 GMT Server: Apache/2.2.24 (FreeBSD) PHP/5.4.13 mod_ssl/2.2.24 OpenSSL/0.9.8y Last-Modified: Sun, 25 Aug 2013 23:42:46 GMT ETag: "376467-3d47f-4e4ce325a0980" Accept-Ranges: bytes Content-Length: 251007 Connection: close Content-Type: image/jpeg
  • sanatik
    sanatik over 10 years
    @Matthias soo, what should I do? How to strip additional data on my client?
  • sanatik
    sanatik over 10 years
    @MadProgrammer in header variable I have this info, may be not full but the part of it. Is it really necessary?
  • MadProgrammer
    MadProgrammer over 10 years
    As I understand it, the server will send a number a new lines between the "header" and the image, this the "terminator" between sections, you need to be reading for this...
  • Matthias
    Matthias over 10 years
    @sanatik To fix the problem you have, do not write extra data on server side. Thus check how many bytes you read from file and only write those files to your Socket's OutputStream like shown in above answer. I do not know what you try to achieve with your Client and Server, but usually http is not a very easy protocol to implement. If you want to establish a communication between a client and a server which you design, you should have a look at MadProgrammer's comments, they are very valuable advice.
  • sanatik
    sanatik over 10 years
    @MadProgrammer I add the "\r\n\r\n" to the end of the header, isn't it enough? Also I wll need to divide on client side header and file after receiving will be done.
  • MadProgrammer
    MadProgrammer over 10 years
    @sanatik That's what you wrote the server, but the server should be sending a response header, which you're not taking into consideration...
  • sanatik
    sanatik over 10 years
    @Mattias MadProgrammer thanks guis! It works! I just needed to add int lol; while((lol = fstream.read(buffer)) != -1) { bout.write(buffer,0,lol); } To my server!
  • sanatik
    sanatik over 10 years
    Also I've no enough reputation to plus your answers! Really thank you!
  • user207421
    user207421 over 8 years
    @sanatik 'Misrepresentations' such as what?
  • user207421
    user207421 over 8 years
    @sanatik 'Both ends' means both peers: the server and the client.