How to Read and Write Byte Streams

Businessman working at desk
Jetta Productions/Blend Images/Getty Images

Reading and writing binary streams is one of the commonest I/O tasks a Java application can perform. It can be performed by looking at each individual byte in a stream or by using a more structured buffered approach.

Note: This article looks at reading binary data from a example.jpg file. If you try this code then simply replace the name of the example.jpg with the path and name of a jpeg file on your computer.

Byte by Byte

The java.io class was the first Java api to provide Input/Output functionality. It has two methods that can be used to input and output byte streams (blocks of 8 bits) from and to a file. These classes are the FileInputStream and FileOutputStream. These methods provide a basic method of I/O by allowing a file to be input or output one byte at a time.  In practice it's better to use a buffered method for binary streams but it's good to look at the most basic building block of the Java I/O functionality.

Notice how we place the I/O handling inside a try, catch, finally block - this is to make sure we handle IO exceptions and to properly close the streams. The catch block will show any I/O exceptions that occur and print a message for the user. In the finally block it's important to close the streams explicitly by calling the close method otherwise they will remain open and a waste of resources.

There is a check to see if the FileInputStream and FileOutputStream are null before attempting to close. This is because an I/O error could occur before the streams are initialized. For example, if the file name is incorrect the stream will not be opened properly.

        FileInputStream fileInput = null;
        FileOutputStream fileOutput = null;

        try 
        {
            //Open the input and out files for the streams
            fileInput = new FileInputStream("C:\\example.jpg");
            fileOutput = new FileOutputStream(C:\\anewexample.jpg");
           
        }
        catch (IOException e)
        {
            //Catch the IO error and print out the message
            System.out.println("Error message: " + e.getMessage());
        }    
         finally 
        {
            //Must remember to close streams
            //Check to see if they are null in case there was an
            //IO error and they are never initialized
            if (fileInput != null)
            {
                fileInput.close();
            }
            if (fileInput != null)
            {
                fileOutput.close();
            }
        }
        
    

In the try block we can add code to read in the bytes:

            int data;

            //For each byte read it in from the input file
            //and write it to the output file
            while ((data = fileInput.read()) != -1) 
            {
                fileOutput.write(data);
            }

The read method reads in one byte from the FileInputStream and the write method writes one byte to the FileOutputStream. When the end of the file is reached and there are no more bytes to input the value of -1 is returned.

Now that Java 7 has been released you can see the benefit of one of its new features - the try with resources block. This means that if we identify the streams to the try block at the beginning it will handle closing the stream for us. This eliminates the need for the finally block in the previous example:

        try ( FileInputStream fileInput = new FileInputStream("C:\\example.jpg");
            FileOutputStream fileOutput = new FileOutputStream("C:\\anewexample.jpg"))
        {
            
            int data;

            while ((data = fileInput.read()) != -1) {
                fileOutput.write(data);
            }
        }
        catch (IOException e)
        {
            System.out.println("Error message: " + e.getMessage());
        }

The full Java code listings for the two versions of the byte reading program can be found in Binary Stream Example Code.