10

I have used the code here to send an individual file over a socket. However, I need to be able to send multiple files (basically all files in a directory) over the socket and have the client recognize how the separation between files. Frankly, I am at a complete loss for what to do. Any tips would be helpful.

NOTE 1: I need a way to send the files in one continuous stream that the client can segregate into individual files. It cannot rely on individual requests from the client.

NOTE 2: To answer a question I am pretty sure I will get in the comments, no, this is NOT homework.

EDIT it has been suggested that I could send the size of the file before the file itself. How can I do this, as sending a file over the socket is always done in either a predetermined array of bytes, or a single byte individually, rather than the long returned by File.length()

ewok
  • 20,148
  • 51
  • 149
  • 254

5 Answers5

22

Here is a full implementation:

Sender Side:

String directory = ...;
String hostDomain = ...;
int port = ...;

File[] files = new File(directory).listFiles();

Socket socket = new Socket(InetAddress.getByName(hostDomain), port);

BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
DataOutputStream dos = new DataOutputStream(bos);

dos.writeInt(files.length);

for(File file : files)
{
    long length = file.length();
    dos.writeLong(length);

    String name = file.getName();
    dos.writeUTF(name);

    FileInputStream fis = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(fis);

    int theByte = 0;
    while((theByte = bis.read()) != -1) bos.write(theByte);

    bis.close();
}

dos.close();

Receiver Side:

String dirPath = ...;

ServerSocket serverSocket = ...;
Socket socket = serverSocket.accept();

BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
DataInputStream dis = new DataInputStream(bis);

int filesCount = dis.readInt();
File[] files = new File[filesCount];

for(int i = 0; i < filesCount; i++)
{
    long fileLength = dis.readLong();
    String fileName = dis.readUTF();

    files[i] = new File(dirPath + "/" + fileName);

    FileOutputStream fos = new FileOutputStream(files[i]);
    BufferedOutputStream bos = new BufferedOutputStream(fos);

    for(int j = 0; j < fileLength; j++) bos.write(bis.read());

    bos.close();
}

dis.close();

I did not test it, but I hope it will work!

LarsH
  • 27,481
  • 8
  • 94
  • 152
Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417
  • Not sure if I was unclear or what, but the client and server seem to be reversed here from my intentions. regardless, that was a pretty simple fix. Thanks! – ewok Jul 10 '12 at 19:56
  • @ewok oops, I write the code after seeing the title. However, there is no big difference and I am glad that I could help :) – Eng.Fouad Jul 10 '12 at 19:59
  • Perfect solution. Thank you so much – Monica Patel Feb 18 '17 at 11:14
  • How does `readUTF()` know when the fileName is finished? could you receive the stream in another language, what would you have to do to recognize end of fileName and start of the data? – xuiqzy Jan 22 '20 at 08:56
2

You could send the size of the file first before each file, that way the client will know when the current file is over and expect the next (size). This will allow you to use one contiguous stream for all files.

Attila
  • 28,265
  • 3
  • 46
  • 55
  • Can you give me a quick example of this? I'm pretty new to socket programming and I'm not sure how I would send the size of the file, since you either have to send a predetermined array of bytes, or a single byte, rather than the long that `File.length()` will return – ewok Jul 10 '12 at 19:34
  • You can convert `File.length()` to the appropriate size byte array and use that (`Long.SIZE` tells you the number of bits, with a little math you can get the number of bytes) -- check out [this SO thread](http://stackoverflow.com/questions/4299111/convert-long-to-byte-array-and-add-it-to-another-array) on how to do the long->byte array conversion – Attila Jul 10 '12 at 19:49
  • @Attila or simply use `DataInputStream` and `DataOutputStream` :) – Eng.Fouad Jul 10 '12 at 19:55
1

A very simple way to do it would be to send the file length before sending each file so that you can determine the separation between files.

Of course, if the receiving process is Java, you can just send Objects.

sshannin
  • 2,767
  • 1
  • 22
  • 25
0

You could zip the files on the client side and send this zipped stream to the server.

e.g: http://www.exampledepot.com/egs/java.util.zip/CreateZip.html

with ...

OutputStream output = connection.getOutputStream();
ZipOutputStream out = new ZipOutputStream(output);
Pierre
  • 34,472
  • 31
  • 113
  • 192
  • thanks, but zipping is not particularly useful. I need a stream that is capable of manipulating one file while the next is still being streamed – ewok Jul 10 '12 at 19:24
0

Maybe the fastest way is to automatically zip and unzip the files in your directory into one file, see java.util.zip package