This article describes how to create a java midlet that browses through all
stored files in a mobile phone and uploads a selected file to a web server. This
midlet uses the JSR 75 API for accessing the filesystem. JSR 75 provides methods
for enumerating folder contents as well as reading and writing to a file.
However the use of this API demands special permissions which means if your
midlet is not signed then an annoying message will pop up every time the midlet
tries to access a file or a folder asking for permission from the user. Such an
application can be used in various ways, such as backing up online the files
stored in a mobile phone, file sharing between a mobile phone user and a
computer - or mobile phone - user and in some occasions as a cheap alternative
to MMS.
If you want to download directly the code of the midlet go to the end of this
page.
The code of this midlet is fairly simple. It consists of a List that displays
the contents of a folder, one method for reading the contents of a folder, one
method for implementing the "back" functionality and one method for sending the
file to the web server. But let's begin with the php script that resides on the
server side and accepts the incoming file. A very basic script can be the
following:
<?
if($_FILES['file']['name']!=""){
$uploaddir = 'received_files/';
$file = basename($_FILES['file']['name']);
$uploadfile = $uploaddir . $file;
if(!move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)){
exit();
}else{
chmod($uploadfile,0755);
}
}
?>
What this script is doing is to receive an HTTP Post that includes a file and
store in a directory named "received_files" .
As far as the midlet is concerned the code starts as follows ( you can find a
link to the full source code at the end of the page):
public class FileUpload extends MIDlet implements CommandListener,Runnable {
List fileList;
Display display;
Command back;
String currentFolder; //keeps the current directory so to
implement the back functionality
String serverURL; //the url of the web page that recieves the
file
public void startApp() {
fileList = new List ("Select File",
Choice.IMPLICIT);
back = new Command("Back",Command.BACK,1);
display = Display.getDisplay(this);
currentFolder = "";
fileList.setCommandListener(this);
this.listRootDirectories();
display.setCurrent(fileList);
serverURL = "http://localhost/upload.php";
}
As we can see, it declares a List that will holds the contents of the folders, a
String named currentFolder that is used to keep the current path so to be able
to implement the "back"
functionality as well as the back command and the serverURL string that it holds
the url of the php script. The midlet implements the Runnable interface as we
want the network communication to be held by a separate thread. Initially what
the fileList should show is the root directories, thus a method name
listRootDirectories has been created and it is called at the beginning of the
execution. the code for this method is the following:
private void listRootDirectories(){
fileList.deleteAll();
Enumeration rootDirectories = FileSystemRegistry.listRoots();
while (rootDirectories.hasMoreElements())
fileList.append((String)rootDirectories.nextElement(),null);
fileList.removeCommand(back);
}
What this method is doing is to clear the List of any entries, to enumerate
the root directories and remove the back command from the list in case there
exists because there is no more back! The method for listing folder contents is
the following:
private void listFolderContents (FileConnection folder){
fileList.deleteAll();
try{
Enumeration folderContents =
folder.list();
while (folderContents.hasMoreElements())
fileList.append((String)folderContents.nextElement(),null);
fileList.addCommand(back);
folder.close();
display.setCurrent(fileList);
}catch (IOException e){}
}
It is obvious the code is very similar to the method displaying the root
directories. It receives as input a FileConnection class that represents the
folder and it sets the contents of the list accordingly to the contents of the
folder. The code for going one directory up is the following:
private void goOneDirectoryUp(){
int lastFolderSlashIndex = currentFolder.lastIndexOf('/',currentFolder.length()
-2);
if (lastFolderSlashIndex == -1){//up directory is the root
this.listRootDirectories();
currentFolder = "";
}
else{
currentFolder =
currentFolder.substring(0,lastFolderSlashIndex + 1);
try{
FileConnection fc = (FileConnection)Connector.open("file:///" + currentFolder);
this.listFolderContents(fc);
}catch(IOException
e){}
}
}
This method makes use of the currentFolder string that holds the path to the
current directory. If the path contains only one slash (i.e c:/ ) then the root
directories should be shown, if not then the previous directory is being found
by the position of the second slash counting form the end (i.e if currentFolder
is "c:/pictures/memory/ then the previous directory is c:/pictures/). Now
it's time to see what happens when a command is invoked:
public void commandAction(Command com, Displayable dis) {
if (com == List.SELECT_COMMAND){ // A direcotroy or folder
has been selected
try{
FileConnection fc = (FileConnection)Connector.open("file:///" + currentFolder +
fileList.getString(fileList.getSelectedIndex()));
if (fc.isDirectory()){
currentFolder = currentFolder + fileList.getString(fileList.getSelectedIndex());
this.listFolderContents(fc);
}else{// it
is a file, upload it to the server
try{
Thread t = new Thread(this);
t.start();
}catch(Exception e){ }
}
}catch(IOException e){}
}
if (com == back){
this.goOneDirectoryUp();
}
}
As it can been seen, when the user press select the code checks whether the
selected item is folder or file. It it is a folder it creates a FileConnection
class that represents the folder, it sets accordingly the currentFolder string
and it calls the listFolderContents. If the selected item is a file then it
calls the run method of the current thhread which - as it will be shown later on
- it send the file to the web server. If thee user press the back command the
code simply calls the goOneDirectoryUp method. The last method of the midlet is
the run method which send the file to the web server. The implementation of this
method is the follwoing:
public void run(){
try{
FileConnection fc = (FileConnection)Connector.open("file:///"
+ currentFolder +
fileList.getString(fileList.getSelectedIndex()));
currentFolder = currentFolder + "/";
int fileSize = (int)(fc.fileSize());
//Build a form that it will displays
a progress bar and
//the amount of data that haw been
sent
Form form = new Form
("Uploading....");
Gauge gauge = new Gauge("Progress",false,fileSize,0);
StringItem stringitem = new
StringItem ("","0 / " + fileSize);
form.append(gauge);
form.append(stringitem);
display.setCurrent(form);
//Build the necessary http post
messages
String message1, message2;
message1 = "--file\r\n";
message1 += "Content-Disposition:
form-data; name=\"file\"; filename=\"" + fc.getName()+ "\"\r\n";
message1 += "Content-Type:
text/plain\r\n\r\n";
message2 = "\r\n--file--\r\n";
String length = Integer.toString((message1.length()
+ message2.length() + fileSize));
OutputStream os = null;
HttpConnection conn = (HttpConnection)Connector.open(serverURL,Connector.READ_WRITE);
conn.setRequestMethod(HttpConnection.POST );
conn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=file");
os = conn.openOutputStream( );
os.write(message1.getBytes());
InputStream ins = fc.openInputStream();
byte[] buffer = new byte[2048];
int sendCounter = fileSize;
//Write the contents of the file
while (ins.read(buffer)> 0){
if (sendCounter
>=2048)
os.write(buffer,0,2048);
else
os.write(buffer,0,sendCounter);
sendCounter =
sendCounter - 2048;
gauge.setValue(gauge.getValue() + buffer.length);
stringitem.setText(gauge.getValue() + " / " + fileSize);
}
//Flushing the output
stringitem.setText("Please wait...");
os.close();
stringitem.setText("Upload
completed");
form.addCommand(back);
form.setCommandListener(this);
}catch(Exception e){}
}
As it can been seen this method, initially opens the selected file, it
appends a slash to the currentFolder string so the "back" functionality to work
correctly, it creates a form that shows the progress of the process using a
gauge as well as a string item, it constructs the necessary HTTP messages, it
connects to the web server, it sends the data, it adds the back command to the
form and it finishes
The complete source code can be found here |