最近Lab需要寫一個Android Ftp Upload Module,也希望有個管理介面
主要參考此篇加上Ftp的功能(使用apache commons FTP)
分享一下code囉!
功能
- 支援多檔上傳 (用,分開)
- 檔案
- 1-level 資料夾:資料夾底下只有檔案,沒有資料夾
- 完成的Job按下Clear會在畫面上清空
完整程式碼 Code
程式解說
MainActivity.java中為ftp server及手機端的設定:
- ftpHost:要連線的ftp server
- ftpUser:連到ftp server的帳號
- ftpPasswd:連到ftp server的密碼
- ftpRemoteDir:檔案上傳到ftp server的位置
- port:對應的ftp server port
- delete:手機端上傳檔案後是否刪除手機裡該筆資料(yes/no)
- localDir:手機端要上傳的檔案/資料夾,可上傳多筆檔案/單層資料夾(該層資料夾底下只有檔案,沒有其他資料夾)。每一筆用,分開的檔案/資料夾,視為一筆Task()
bData.putString("ftpHost", "");
bData.putString("ftpUser", "");
bData.putString("ftpPasswd", "");
bData.putString("ftpRemoteDir", "");
bData.putString("localDir", "/sdcard/test2.txt,/sdcard/test1.txt,/sdcard/folder/");
bData.putInt("port", 21);
bData.putString("delete", "no");
Task[] initTasks() {
String[] fileList = LocalDirectory.split(",");
final int count=fileList.length;
total_task=count;
Task[] result= new Task[count];
for (int i=0;i<count;i++){
result[i]=new Task(fileList[i],ftpHost,ftpUserName,ftpPassword,
ftpRemoteDirectory,LocalDirectory,Delete,port);
}
return result;
}
每一筆Task中,用開啟ftpclient執行ftp的功能(connect,upload...)
並依照ftp upload的進度,更新progress和顯示的檔案名稱(presentName):上傳到第幾個file
private Runnable ftpclient = new Runnable() {
public void run() {
ftpConnect(ftpHost,ftpUserName,ftpPassword,port);
//上傳目錄底下的檔案
if( desc.charAt(desc.length()-1) == '/') {
File dir = new File(desc);
String[] child = dir.list();
for(int i = 0; i < child.length; i++){
String fileName = child[i];
String presentName = desc +" : "+fileName+" ["+String.valueOf(i+1)+"/"+String.valueOf(child.length)+"]";
setPresentDesc(presentName); //更改看到的textView:顯示目錄下有幾個檔案完成
ftpUpload(desc+fileName,fileName,ftpRemoteDirectory+"/"+remoteDirName);
if( Delete == "yes"){
Deletefile(desc+fileName);
}
}
}
//File start
else{
String fileName = desc.substring(desc.lastIndexOf("/")+1,desc.length()); ftpUpload(desc,fileName,ftpRemoteDirectory);
if( Delete == "yes"){
Deletefile(desc+fileName);
}
}//File end
ftpDisconnect();
}
};//Runable
讓listener偵測到progress,presentName有改變時,更新對應到xml的區塊
如果Task是一個資料夾,則要所有資料夾中的檔案都上傳完成才能Clear finish=true
public void setProgress(int progress) {
this.progress = progress >= 100 ? 100 : progress;
if(this.progress>=100){
finish_file++;
if(finish_file == num_files){
finish=true;
}
}
if (!listenerList.isEmpty()) {
for (Task.taskListener listener : listenerList)
listener.onProgressChanged(this.progress,this.presentName);
}
}
this.l = new taskListener() {
@Override
public void onProgressChanged(final int progress,final String name) {
mHandler.post(new Runnable() {
@Override
public void run() {
tvTaskDesc.setText(name);
pbTask.setProgress(progress);
if(now_task < total_task-1){ //最後一個task不用進判斷式
if(myTasks[now_task].getFinish()){
//next task
now_task++;
new Thread(myTasks[now_task]).start();
}
}
}
});
}
}
資料夾中的檔案都上傳完成(finish=true),按下Clear才會從畫面中移除
public void onClick(View v) {
if(aTask.getFinish()){
tasks.remove(aTask);
notifyDataSetChanged();
}
}
捨棄掉的功能
多線程上傳
程式的架構是每一支Task(一個檔案/資料夾)會與ftp server連線,
傳自己Task中需要上傳的檔案(一個檔案/資料夾中所有的檔案),上傳完成之後與ftp server斷線。
原本想用ismsor程式中
for(Task t:tasks)
new Thread(t).start();
照理說應該是可以多線程上傳互不影響,但在實作後測試發現:
先完成的Task在斷線時,會切到其他還在上傳檔案的Task的連線,
如果Task是一個檔案,仍能正常傳完;但如果Task是一個資料夾,資料夾其他還沒開始上傳的檔案都會因為斷線而無法上傳。
由於本程式最重要的是檔案能完整上傳功能,所以這部分就變成是單線程上傳,按照順序執行每一個Task
protected void onCreate(Bundle savedInstanceState) {
now_task=0;
myTasks=initTasks();
tasksFtp = new ArrayList<Task>(Arrays.asList(myTasks));
MyAdapter adapter = new MyAdapter(this,tasksFtp);
getListView().setAdapter(adapter);
//只從第一個開始跑
new Thread(myTasks[now_task]).start();
}
.
.
.
this.l = new taskListener() {
@Override
public void onProgressChanged(final int progress,final String name) {
mHandler.post(new Runnable() {
@Override
public void run() {
tvTaskDesc.setText(name);
pbTask.setProgress(progress);
if(now_task < total_task-1){ //最後一個task不用進判斷式
if(myTasks[now_task].getFinish()){
//next task
now_task++;
new Thread(myTasks[now_task]).start();
}
}
}
});//onProgress
}//taskListener
按鈕更新
原本想實作在Task完成後,畫面按鈕才從Stop變成Clear (幫以後功能鋪路)
但實做測試後,執行到最後一個task的時候做相關動作 ex: btn.setText()
都會造成ftp Upload失敗,照理說這兩個沒什麼關聯,應該不會影響,
由於程式以檔案成功上傳為優先,這個就先捨棄掉囉!
參考資料
The Way to Update progress bar in ListView :Android
deleting item from a ViewHolder
note
Android 開發時,模擬器要傳檔案上去常會有權限問題
開啓Terminal
adb shell
mount -o rw,remount rootfs /
chmod 777 /mnt/sdcard
Mac設置adb的方法
1.找到android sdk的位置,adb命令在platform-tool資料夾中
我的adb路徑是/Applications/eclipse/android-sdk-macosx/platform-tools
假設你的路徑是XXXX
2.打開Terminal輸入
touch .bash_profile
open -e .bash_profile
3.新增路徑
vim .bash_profile
如果.bash_profile已經有內容,我們只要在之後添加:XXXX
如果是一個空白的話,我們就輸入一下內容export PATH=${PATH}:XXXX
,儲存關閉.bash_profile
export PATH=${PATH}:/Applications/eclipse/android-sdk-macosx/tools:/Applications/eclipse/android-sdk-macosx/platform-tools
export CLICOLOR='true'
export LSCOLORS="gxfxcxdxcxegedabagacad"
4.Terminal輸入source .bash_profile
就設置成功囉!
以後有時間可能會加上
- 暫停/續傳 or 停止/重傳
- ftpes 加密ftp協定