How do I stream large files over a network using Socket in Java?

Streaming large files over a network using sockets in Java requires splitting the file into manageable chunks to avoid memory overhead, as well as safely reading and transmitting data between the client and server. Below is a step-by-step guide with code to demonstrate how to achieve this.

Key Steps:

  1. Open a file stream to read the file at the source (server).
  2. Send the file in chunks over the socket output stream.
  3. Receive the chunks on the target (client) and write them to a file.
  4. Ensure proper resource management using try-with-resources to close file streams and sockets.
  5. Use buffering for efficient file and network I/O.

Example Code

Server Code (File Sender)

The server reads the file from the disk and streams it in chunks to the client over a socket.

package org.kodejava.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class FileServer {
   private static final int PORT = 5000;
   private static final int BUFFER_SIZE = 4096; // 4 KB

   public static void main(String[] args) {
      try (ServerSocket serverSocket = new ServerSocket(PORT)) {
         System.out.println("Server is listening on port " + PORT);
         Socket socket = serverSocket.accept();
         System.out.println("Client connected.");

         // File to send
         File file = new File("path/to/large-file.txt");
         try (FileInputStream fileInputStream = new FileInputStream(file);
              BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
              OutputStream outputStream = socket.getOutputStream()) {

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
               outputStream.write(buffer, 0, bytesRead);
            }
            System.out.println("File sent successfully.");
         }
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

Client Code (File Receiver)

The client receives the file data from the server and writes it to a local file.

package org.kodejava.net;

import java.io.*;
import java.net.Socket;

public class FileClient {
   private static final String SERVER_ADDRESS = "localhost";
   private static final int SERVER_PORT = 5000;
   private static final int BUFFER_SIZE = 4096; // 4 KB

   public static void main(String[] args) {
      try (Socket socket = new Socket(SERVER_ADDRESS, SERVER_PORT)) {
         System.out.println("Connected to the server.");

         // Destination file
         File file = new File("path/to/saved-file.txt");
         try (InputStream inputStream = socket.getInputStream();
              BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
              FileOutputStream fileOutputStream = new FileOutputStream(file);
              BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)) {

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
               bufferedOutputStream.write(buffer, 0, bytesRead);
            }
            System.out.println("File received successfully.");
         }
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

Explanation of the Code:

  1. Buffering:
    • Both server and client use BufferedInputStream and BufferedOutputStream. This ensures efficient reading and writing of data by reducing direct interaction with the file system or socket streams.
  2. Fixed Buffer Size:
    • The BUFFER_SIZE limit prevents memory overload by reading and writing manageable chunks of file data.
  3. Socket Communication:
    • The server listens for incoming requests on a specific port. Once the client connects, the file is transmitted through the socket’s output stream.
  4. File Transmission Loop:
    • Data from the server is sent in chunks (bytesRead from the buffer). The client reads and writes these chunks to the output file until the end of the file is reached (when bytesRead returns -1).
  5. Resource Management:
    • Using try-with-resources ensures all resources—file streams, sockets—are properly closed, even in case of exceptions.

Example Workflow:

  1. Run the Server:
    • Start the FileServer. The server will wait for a connection from the client.
  2. Run the Client:
    • Start the FileClient. The client will connect to the server, receive the file, and save it locally.

Notes:

  • File Size Limitations: This approach handles files of any size since the data is streamed in chunks rather than loading the entire file into memory.
  • Error Handling: Always include error handling for socket timeouts, file not found, and I/O errors.
  • Security: For production, consider encrypting the file data while transmitting over the network, especially on public networks.

How do I read a file into byte array?

package org.kodejava.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ReadFileIntoByteArray {
    public static void main(String[] args) throws IOException {
        File file = new File("README.md");
        try (InputStream is = new FileInputStream(file)) {
            if (file.length() > Integer.MAX_VALUE) {
                throw new IOException("File is too large.");
            }

            int offset = 0;
            int bytesRead;
            byte[] bytes = new byte[(int) file.length()];
            while (offset < bytes.length
                    && (bytesRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += bytesRead;
            }
        }
    }
}

How do I get the content of an InputStream as a String?

We can use the code below to convert the content of an InputStream into a String. At first, we use FileInputStream create to a stream to a file that going to be read. IOUtils.toString(InputStream input, String encoding) method gets the content of the InputStream and returns a string representation of it.

package org.kodejava.commons.io;

import org.apache.commons.io.IOUtils;

import java.io.InputStream;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;

public class InputStreamToString {
    public static void main(String[] args) throws Exception {
        // Create an input stream for reading data.txt file content.
        try (InputStream is = new FileInputStream("data.txt")) {
            // Get the content of an input stream as a string using UTF-8
            // as the character encoding.
            String contents = IOUtils.toString(is, StandardCharsets.UTF_8);
            System.out.println(contents);
        }
    }
}

Maven Dependencies

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.16.1</version>
</dependency>

Maven Central

How do I convert InputStream to String?

This example will show you how to convert an InputStream into String. In the code snippet below we read a data.txt file, could be from common directory or from inside a jar file.

package org.kodejava.io;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class StreamToString {

    public static void main(String[] args) throws Exception {
        StreamToString demo = new StreamToString();

        // Get input stream of our data file. This file can be in
        // the root of your application folder or inside a jar file
        // if the program is packed as a jar.
        InputStream is = demo.getClass().getResourceAsStream("/student.csv");

        // Call the method to convert the stream to string
        System.out.println(demo.convertStreamToString(is));
    }

    private String convertStreamToString(InputStream stream) throws IOException {
        // To convert the InputStream to String we use the
        // Reader.read(char[] buffer) method. We iterate until the
        // Reader return -1 which means there's no more data to
        // read. We use the StringWriter class to produce the string.
        if (stream != null) {
            Writer writer = new StringWriter();

            char[] buffer = new char[1024];
            try (stream) {
                Reader reader = new BufferedReader(new InputStreamReader(stream,
                        StandardCharsets.UTF_8));
                int length;
                while ((length = reader.read(buffer)) != -1) {
                    writer.write(buffer, 0, length);
                }
            }
            return writer.toString();
        }
        return "";
    }
}

How do I convert string into InputStream?

Here you will find how to convert string into java.io.InputStream object using java.io.ByteArrayInputStream class.

package org.kodejava.io;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class StringToStream {
    public static void main(String[] args) {
        String text = "Converting String to InputStream Example";

        // Convert String to InputStream using ByteArrayInputStream
        // class. This class constructor takes the string byte array
        // which can be done by calling the getBytes() method.
        InputStream stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
    }
}