0

I have the following code which transfers file via Sockets. How do I send the file name?

Socket socket = new Socket("localhost", port);//machine name, port number
File file = new File(fileName);
// Get the size of the file
long length = file.length();
if (length > Integer.MAX_VALUE) 
{
    System.out.println("File is too large.");
}
byte[] bytes = new byte[(int) length];
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());

int count;

while ((count = bis.read(bytes)) > 0) 
{
    out.write(bytes, 0, count);
}

out.flush();
out.close();
fis.close();
bis.close();
socket.close();
Hugo Dozois
  • 8,147
  • 12
  • 54
  • 58
sap
  • 1,141
  • 6
  • 41
  • 62
  • how to send file name with file? If I'm correct use `File.getName()` – Glenn Mar 27 '13 at 00:34
  • 3
    I think the OP is looking for a protocol definition so that the receiving end can know where to store the file. This requires End-of-record markers and the like - why not use a well-established protocol like FTP? –  Mar 27 '13 at 00:37
  • 1
    @RonDahlgren Because FTP is kind of terrible, overkill for this purpose, and I'm not sure the Java support for it is any good? For a simple file upload from sender A to a not necessarily filesystem-shaped receiver B, HTTP would work better. – millimoose Mar 27 '13 at 00:44
  • With TCP socket you can basically send anything, it's not limited to certain classes / types. Think of every single objects / variable as a collection of bytes. – gerrytan Mar 27 '13 at 00:47
  • no I'm building a p2p network. When the file is send, the receiver has to know the name of the file, does not need to know where to store the file. It can store it anywhere. – sap Mar 27 '13 at 01:22

3 Answers3

6

You can invent your own protocol for your socket. If all you need is a filename and data, DataOutputStream.writeUTF is easiest:

BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
try (DataOutputStream d = new DataOutputStream(out)) {
    d.writeUTF(fileName);
    Files.copy(file.toPath(), d);
}

The peer must use the same protocol, of course:

BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
try (DataInputStream d = new DataInputStream(in)) {
    String fileName = d.readUTF();
    Files.copy(d, Paths.get(fileName));
}
VGR
  • 40,506
  • 4
  • 48
  • 63
2

Use a character that can never be in a file name - such as a null (0x00, \0, whatever you want to call it). Then send a 64 bit integer that indicates how long, in bytes, the file is (make sure you don't run into buffer overflows, little endian/big endian issues, etc... just test all edge cases). Then send the file data. Then the ending socket will know which part is the file name, the file length and the file data, and will even be ready for the next file name if you want to send another.

(if file names can be arbitrary characters including control characters, ouch! Maybe send a 64 bit integer length of file name, the file name, a 64 bit integer length of file data, the file data, repeat ad infinitum?)

EDIT: To send a 64 bit integer over a socket, send its constituent bytes in a specific order, and make sure sender and receiver agree on the order. One example of how to do this is How to convert a Java Long to byte[] for Cassandra?

Community
  • 1
  • 1
Patashu
  • 21,443
  • 3
  • 45
  • 53
1

I tried to wrap a buffer which cause MalfuctionUTF and putting it on try-with resource closes the underlining socket stream and cause connection reset exception
Following code worked for me

Client

DataOutputStream d = new DataOutputStream(out);
        d.writeUTF(filename);
        d.writeLong(length);

Server

DataInputStream d = new DataInputStream(in);
filename = d.readUTF();
fileLength = d.readLong();
Rajat
  • 2,467
  • 2
  • 29
  • 38