Hey again! Since I used my last post to show you the difficulties I experienced during implementing Drag&Drop in combination with JavaFX 2.0, I today want to introduce my file sharing components. After a DropEvent is registered on the GUI (see last post), a new thread responsible for transferring the dropped files is launched. Creating a socket with the receiver´s address enables the actual communication. After a standard handshake, the receiver itself lauches an own file-receiving thread, in order to provide simultaneous up- and download of two users.
Before sending the first actual file, the number of all files, including directories, is sent. Why I just don´t send the number of actual files? Well, to provide maintaing the folder structure of the transferred files and to also enable dragging of multiple, nested folders (by holding cmd during selection) I took the following considerations: a folder (also known as root) and all of its nested files and folders share a common prefix of the path (like C:\Users\XYZ\Downloads\rootFolder). The DropEvent return a list of files. In case of one folder being sent, it just contains a single file object. I then sort the list, that all single files (files that were solely selected) appear at the beginning, and folders are attached to its end. Morever, each folder is recursively searched for its contained files. If you now iterate through this list, printing the absolute file paths, you have a tree like structure.
To be able to rebuild the folder structure I just need to iterate through this list, filter out the directories with a simple if (file.isDirectory()) and then calculate the difference getAbsolutePath().length() – getName().length(). This offset is sent to the receiver, and then all contained files of arbitray nested depth are sent. Before the actual file content is transferred, I have to send the absolutePath. On the receiver´s side I then just have to create a new file for each incoming file, with the correct path of just invoking receivedAbsolutePath.substring(offset) . Of course I have a few more conditions checks, for sending and parsing the offset, and of course there is also a difference in processing single selected files, or files that are nested inside a folder, but basically, this offset is the key to victory.
The file transfer itself uses the standard Java best practice approach: By opening a FileInputStream on each file, they are opened and read in blocks of 16384 bytes into an array of the same size. Before that, by creating a Serversocket on a random port, and sending this random port to the receiver, a new data-channel is set up, which is used for transferring the files. The aforementioned blocks are continually sent by invoking the Outputstream.write() of the socket´s output stream, to which the receiver is bound. After a successful transmittance, streams and socket are closed.
The receiver part of the implementation is of course very similar: I run a for loop, as long as it´s loop-variable reaches the value all files being sent. For each file
being sent, the name is received as well as the port for this specific file. After that, as mentioned above, a new file using receivedAbsolutePath.substring(offset) is created. Instead of opening a FileInputStream, a FileOutputStream is opened to write the received content into the just created file. Writing the actual file content is then exectued by invoking read() of the socket´s input stream, and writing the received blocks of 16384 bytes via FileOutputStream.write().
As always stay tuned and thanks for your tuning in! There will be one more blog entry about the final look&feel of the GUI soon – watch out!