Android中SD卡內容讀取和簡易FTP文件上傳(番外)


  大家好,又是一周一次的拔旗時間。本周公司事情較多,原本安排的關於MediaPlayer多媒體應用的技術在本周內分享的事宜要歌啦~ 為了不女裝,這次的技術分享內容關於Android中SD卡處理和FTP上傳功能,算是一個番外篇(其實我才不告訴你們我是代碼沒有寫完)。So,下面正式開始吧。

Android中SD卡內容讀取

         有人可能會問,這有什么好寫的,網上有許多這方面的內容。Noop, 只有自己親自去Coding才會發現一些很神奇的坑點,比如SD卡內的文件內容讀取這一個很小的功能點。

         首先,實現這個功能,在mainifest里面加入這句權限:

1 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>

   然后在代碼中加入以下這句話:

   Path = Environment.getExternalStorageDirectory().getAbsolutePath()

         用遞歸算法依次遍歷每一個File,OK,思路沒錯,網上或者書上絕大部分都是建議你這么做的。Environment.getExternalStorageDirectory()可以動態的獲取每個Android設備中外置SD存儲卡的位置。當我編寫完程序進行測試的時候發現,File is null!!!! 當時蒙蔽了,路徑不是都能拿到么,怎么new File(path)就會報錯呢???

        進行下一步的調查可以FA現,我得到的path是 /storage/emulated/0/, Nexus和Galaxy系列的手機都會進入到這個目錄,這個查了下會根據不同的安卓廠商發生改變,目的是隱藏SD卡的路徑進行保護。這樣就麻煩了,adb shell的訪問是進入不了這個目錄的。那我怎么能獲取到整個路徑下的文件呢?

   於是我打開萬能的stackoverflow搜索了一下,除了要加上權限之外,還需要判斷SD卡是否處於mounted的狀態,好,那我加上如下代碼:

1  private boolean isSdCardExist() {
2       logcat("state: " + Environment.getExternalStorageState());
3       return Environment.getExternalStorageState().equals(
4               Environment.MEDIA_MOUNTED);
5  }

         但結果令人失望,debug過程中可以發現,代碼邏輯依然可以進去走原先的邏輯,也就是說,當前的狀態處於加載狀態的。

         經過多次嘗試,我觀察了下Environment這個類,發現一個神奇的方法,名字叫:getRootDirectory(),用來獲取SD卡的根目錄。發現可以了!!

         整理下之前的思路,介紹部分代碼: 獲取SD卡路徑(這里用File來做)

    public File getSDCardPath(){
        // 不要用Environment.getExternalStorageDirectory().getAbsoluteFile()這個方法
        if (isSdCardExist()) {
            logcat("path is " + Environment.getExternalStorageDirectory().getAbsolutePath());
//            return Environment.getExternalStorageDirectory();
            return Environment.getRootDirectory();
        } else {
            return null;
        }
    }

   進行遞歸操作,遍歷每個文件夾:

    private void getFileName(File[] files) {
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    getFileName(file.listFiles());
                } else {
                    String fileName = file.getName();
                    if (isImageFile(fileName)) {
                        addToImageFileLists(file.getAbsolutePath());
                    } else {
                        continue;
                    }
                }
            }
        } else  {
            logcat("Files is null");
        }
    }

   當然我做的程序內容是讀取SD卡上的圖片和音樂文件,以上提供一個無法讀取SD卡內容的一個解決思路,希望可以幫助到大家。這個奇葩Bug在4.4中就被發現了,Nexus和Galaxy機型就會碰到。

AndroidFTP文件上傳:

     有時候,我們在手機上獲得到一些數據(通常是一些圖片),然后想用安卓App,一鍵上傳到指定網絡的ftp服務器,讓其他人下載到最新數據。那么以下的程序正是基於此需求進行開發。

         准備工作1:

         到Apache的官網下載commons-net-3.5.jar(我用的時候的最新版本),將此包導入到工程中(詳細方法可以參考PS第一條)。

         准備工作2:

         下載一款適合你的FTP服務器工具軟件,這里我推薦FTPServer,方便快捷非安利。打開FTPServer后,輸入賬號名和密碼,指定好訪問目錄,然后把“下載文件”和“上傳文件”兩個權限開啟(否則會有Permission Denied, 別問我為什么知道),點擊“啟動服務”。

         以上,便做好了前置工作, 開始下一步。

         設計一個配置實體類Config,包含hostname, port, username, passwd 四個參數。在CommonUtil中寫入如下核心代碼:

 1     public String ftpUpload(UploadConfig config, ArrayList<String> fileLists) {
 2 
 3         FTPClient ftpClient = new FTPClient();
 4         FileInputStream fis;
 5         String returnMessage = "Recived message from ftp";
 6 
 7         try {
 8             logcat("Upload Config" + config.toString());
 9             ftpClient.connect(config.getHostname(), config.getPort());
10             boolean isLogin = ftpClient.login(config.getUsername(), config.getPassword());
11             int replyCode = ftpClient.getReplyCode();
12             logcat("replyCode = " + replyCode);
13             if (isLogin && FTPReply.isPositiveCompletion(replyCode)) {
14                 ftpClient.changeWorkingDirectory("/share");
15                 ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
16                 ftpClient.setBufferSize(1024);
17                 ftpClient.setControlEncoding("UTF-8");
18                 ftpClient.enterLocalPassiveMode();
19 
20                 for (String filepath : fileLists) {
21                     File f = new File(filepath);
22                     fis = new FileInputStream(f);
23                     ftpClient.storeFile(getFileName(filepath), fis);
24                 }
25                 returnMessage = "Update Complite";
26             }
27             else {
28                 returnMessage = "Update Faile";
29             }
30         } catch (NumberFormatException e) {
31             // TODO Auto-generated catch block
32             e.printStackTrace();
33         } catch (SocketException e) {
34             // TODO Auto-generated catch block
35             e.printStackTrace();
36         } catch (IOException e) {
37             // TODO Auto-generated catch block
38             e.printStackTrace();
39         } finally {
40             try {
41                 ftpClient.disconnect();
42             } catch (IOException e) {
43                 // TODO Auto-generated catch block
44                 e.printStackTrace();
45             }
46         }
47         return returnMessage;
48     }

        傳入的兩個參數,一個是配置信息文件,一個是上傳文件絕對路徑列表。FTP的連接會有一個ftpClient.getReplyCode()這個方法,可以得知是否與FTPServer進行了通信,當這個replyCode為230表示上傳成功。changeWorkingDirectory() 這個方法可以指定FTP共享文件的路徑,剩下的就是一些上傳方式設定,比如設定為文件二進制流方式,流大小限定1M等。

        在AndroidMenifest中加入:

1     <uses-permission android:name="android.permission.INTERNET"/>
2     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    OK完成了,運行程序,我們發現會有java.net.SocketException: socket failed: EACCES (Permission denied) 報錯, 查了下原因,在4.0之后像聯網這樣的耗時操作需要在另外一個線程中做,不能放在主進程中,因此,添加如下兩行代碼:

1         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
2         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects().penaltyLog().penaltyDeath().build());

    以上,便實現了FTP上傳的主要功能。

PS:

  1 進入AndroidStudio時代,就要用AndroidStudio的方式來進行編譯,跟以往Eclipse的操作有一些不同。補充一些小技巧,首先是關於導包,將jar文件放入如下圖所示的lib文件夾中,然后在build.gradle中的dependencies里面加入compile files('libs/commons-net-3.5.jar'),這樣在編譯的時候將jar包一起打包編譯就可以了。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM