1. 程式人生 > >Android開發-使用FTP協議和HTTP協議進行檔案下載和上傳

Android開發-使用FTP協議和HTTP協議進行檔案下載和上傳

FTP 是File Transfer Protocol(檔案傳輸協議)的英文簡稱,從這個名字也能看出來,這個協議是為了檔案傳輸而生的。

使用前需要下載一個commons-net-3.3.jar

直接上傳程式碼

/** 
     * 向FTP伺服器上傳檔案 
     *  
     * @param url 
     *            FTP伺服器hostname 就是ip
     * @param port 21
     *            埠預設80 
     * @param username 
     *            使用者名稱 
     * @param password 
     *            密碼 
     * @param path 
     *            FTP伺服器儲存目錄
     * @param filename
     *            檔名稱 上傳到FTP伺服器上的檔名,是自己定義的名字 
     * @param input 
     *            檔案輸入流 
     * @return 
     */  
    public static boolean upload(String url, int port, String username,  
            String password, String path, String filename, InputStream input) {  
        
        boolean success = false;  
        FTPClient ftp = new FTPClient();  
        
        try {  
            ftp.setDataTimeout(2000);//設定連線超時時間  
            ftp.connect(url, port); 
            // 如果採用預設埠,可以使用ftp.connect(url)的方式直接連線FTP伺服器  
            ftp.login(username, password); 
            if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {  
                ftp.disconnect();  //未連線到FTP,使用者名稱或密碼錯誤
                return success;  
            }  
            
            boolean isExist =  createDirecroty(path,ftp) ;  
            if(!isExist){  
                return success;  
            }  
            
            //獲取伺服器當前目錄指定檔案資訊
            FTPFile[] files = ftp.listFiles(filename);
            if(files.length > 1){
                ftp.deleteFile(filename);
            }
              
            ftp.setControlEncoding("UTF-8");
            ftp.setBufferSize(1024);  
            ftp.enterLocalPassiveMode();    
            ftp.setFileType(FTP.BINARY_FILE_TYPE);  
            //處理中文名稱的檔名,如果不加這一句的話,中文命名的檔案是不能上傳的  
            filename = new String(filename.getBytes("GBK"), "iso-8859-1") ;  
            ftp.storeFile(filename, input);  
  
            input.close();  
            ftp.logout(); 
            success = true;  
        } catch (IOException e) {
            e.printStackTrace();  
        } finally {  
            if (ftp.isConnected()) {  
                try {  
                    ftp.disconnect();  
                } catch (IOException ioe) {  
                }  
            }  
        }  
        return success;  

    }  

 /**
     * 遞迴建立遠端伺服器目錄
     * 
     * @param remote
     *            遠端伺服器檔案絕對路徑
     * @return 目錄建立是否成功
     * @throws IOException
     */
    public static boolean createDirecroty(String remote,FTPClient ftp) throws IOException {
        
        String totalPath = "";
        boolean success = true;
        String[] path = remote.split("/");
        for(int i=0; i<path.length; i++){            
            String father = path[i];
            if(father == null || "".equals(father)){
                success = false;
                break;
            }
            totalPath = totalPath + "/"+father;
            try {
                boolean isExit = ftp.changeWorkingDirectory(totalPath);
                if(!isExit){
                    boolean make = ftp.makeDirectory(totalPath);
                    if(!make){
                        success = false;
                        break;
                    }
                    //工作路徑切換到此
                    boolean change = ftp.changeWorkingDirectory(totalPath);
                    if(!change){
                        success = false;
                        break;
                    }
                }
            } catch (IOException e) {
                success = false;
                break;
            }
        }  
        return success;

    }

流程其實很簡單的,就是先連線FTP伺服器(這裡要求你的伺服器支援FTP協議),再登陸,然後再建立相關目錄,記住目錄要一級一級建立,然後就是通過ftp上傳了。這裡其實看不到上傳進度,就下來看另外一個方法

public static boolean upload(String url, int port, String username,  
            String password, String remotePath, File localFile) {  
        
        boolean success = false;  
        RandomAccessFile raf = null;
        OutputStream output = null;
        FTPClient ftp = new FTPClient();  
        
        try {  
            ftp.connect(url, port); 
            // 如果採用預設埠,可以使用ftp.connect(url)的方式直接連線FTP伺服器  
            ftp.login(username, password); 
            int reply = ftp.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {  
                ftp.disconnect();  //未連線到FTP,使用者名稱或密碼錯誤
                return success;  
            }  
            
            boolean isExist =  createDirecroty(remotePath,ftp) ;  
            if(!isExist){  
                return success;  
            }  
            
            //獲取當前目錄指定檔案資訊
            FTPFile[] files = ftp.listFiles(localFile.getName());
            if(files.length > 1){
                ftp.deleteFile(localFile.getName());
            }
              
            raf = new RandomAccessFile(localFile, "r");  
            long serverSize = 0;
            /* enterLocalPassiveMode 
             * 每次資料連線之前,ftp client告訴ftp server開通一個埠來傳輸資料。
             * 為什麼要這樣做呢,因為ftp server可能每次開啟不同的埠來傳輸資料,
             * 但是在linux上或者其他伺服器上面,由於安全限制,可能某些埠沒有開啟,
             * 所以就出現阻塞
             * */
            ftp.enterLocalPassiveMode();  
            ftp.setFileType(FTP.BINARY_FILE_TYPE);//設定二進位制傳輸,還支援傳輸ACSII資料  
            ftp.setRestartOffset(serverSize);  
            raf.seek(serverSize);  
            
            // 進度  
            long localSize = localFile.length(); // 本地檔案的長度 
            long step = localSize / 100;  
            long process = 0;  
            long currentSize = 0; 
            String filename = localFile.getName();
            filename = new String(filename.getBytes("GBK"), "iso-8859-1") ;  
            output = ftp.appendFileStream(filename);  
            byte[] b = new byte[1024];  
            int length = 0;  
            while ((length = raf.read(b)) != -1) {  
                output.write(b, 0, length);  
                currentSize = currentSize + length;  
                if (currentSize / step != process) {  
                    process = currentSize / step;  
                    Log.e(TAG, "上傳進度:" + process);
                }  
            }  
             
            if (ftp.completePendingCommand()) {
                success = true; 
                Log.e(TAG, "檔案上傳成功");
            } 
  
        } catch (IOException e) {
            e.printStackTrace();  
        } finally {  
            
            try {
                output.flush();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }  
            
            try {
                raf.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            } 
            
            try {
                ftp.logout();
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            if (ftp.isConnected()) {  
                try {  
                    ftp.disconnect();  
                } catch (IOException ioe) {  
                }  
            }  
        }  
        return success;  

    }

這裡就是通過appendFileStream方法獲取輸出流來進行進度獲取。

還可以通過ftp.setRestartOffset(serverSize);  raf.seek(serverSize);  來進行斷點上傳

上傳結束,再來看下載,

public static boolean downloadFile(String url, int port, String username,  
            String password, String localPath, String serverPath){  
        
        boolean success = false;  
        FTPClient ftpClient = new FTPClient();
 
        try {
            ftpClient.setControlEncoding("GBK"); 
            ftpClient.connect(url, port);
            ftpClient.login(username, password);
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {  
                ftpClient.disconnect();  //未連線到FTP,使用者名稱或密碼錯誤
                return false;    
            }
            
            // 先判斷伺服器檔案是否存在  
            FTPFile[] files = ftpClient.listFiles(serverPath);  
            if (files.length == 0) {  
                Log.e(TAG,"伺服器檔案不存在 serverPath="+serverPath);  
                return false;  
            }  
            
            localPath = localPath + files[0].getName();  
            long serverSize = files[0].getSize(); // 獲取遠端檔案的長度  
            File localFile = new File(localPath);  
            long localSize = 0;  
            if (localFile.exists()) {  
                localFile.delete(); 
            }  
            // 進度  
            long step = serverSize / 100;  
            long process = 0;  
            long currentSize = 0;  
            // 開始準備下載檔案  
            ftpClient.enterLocalActiveMode();  
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);  
            OutputStream out = new FileOutputStream(localFile, true);  
            ftpClient.setRestartOffset(localSize); //設定從哪裡開始下,就是斷點下載 
            InputStream input = ftpClient.retrieveFileStream(serverPath);  
            byte[] b = new byte[1024];  
            int length = 0;  
            while ((length = input.read(b)) != -1) {  
                out.write(b, 0, length);  
                currentSize = currentSize + length;  
                if (currentSize / step != process) {  
                    process = currentSize / step;  
                    Log.e(TAG,"下載進度:" + process);   
                }  
            }  
            out.flush();  
            out.close();  
            input.close();  
            // 此方法是來確保流處理完畢,如果沒有此方法,可能會造成現程式死掉  
            if (ftpClient.completePendingCommand()) {  
                Log.e(TAG,"檔案下載成功");  
                success = true;
            } 
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return success;

    }

這裡就是通過retrieveFileStream獲取輸入流進行下載。

再看看使用HTTP進行檔案上傳

/**
     * 通過拼接的方式構造請求內容,實現引數傳輸以及檔案傳輸
     * 
     * @param url http://192.168.1.19:8080/web/servlet/UploadServlet
     * @param params text content
     * @param files pictures
     * @return String result of Service response
     * @throws IOException
     */
    public static String post(String url, Map<String, String> params, Map<String, File> files)
            throws IOException {
        String BOUNDARY = java.util.UUID.randomUUID().toString();
        String PREFIX = "--";
        String LINEND = "\r\n";
        String MULTIPART_FROM_DATA = "multipart/form-data";
        String CHARSET = "UTF-8";

        URL uri = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
        conn.setReadTimeout(10 * 1000); 
        conn.setDoInput(true);// 允許輸入
        conn.setDoOutput(true);// 允許輸出
        conn.setUseCaches(false); // 不允許使用快取
        conn.setRequestMethod("POST");
        conn.setRequestProperty("connection", "keep-alive");
        conn.setRequestProperty("Charsert", "UTF-8");
        conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY);
        conn.setChunkedStreamingMode(1024);

        // 首先組拼文字型別的引數
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            sb.append(PREFIX);
            sb.append(BOUNDARY);
            sb.append(LINEND);
            sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND);
            sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND);
            sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
            sb.append(LINEND);
            sb.append(entry.getValue());
            sb.append(LINEND);

        }

        DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
        outStream.write(sb.toString().getBytes());
        // 傳送檔案資料
        if (files != null)
            for (Map.Entry<String, File> file : files.entrySet()) {
                StringBuilder sb1 = new StringBuilder();
                sb1.append(PREFIX);
                sb1.append(BOUNDARY);
                sb1.append(LINEND);
                sb1.append("Content-Disposition: form-data; name=\"uploadfile\"; filename=\""
                        + file.getValue().getName() + "\"" + LINEND);
                sb1.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINEND);
                sb1.append(LINEND);
                outStream.write(sb1.toString().getBytes());

                InputStream is = new FileInputStream(file.getValue());
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = is.read(buffer)) != -1) {
                    outStream.write(buffer, 0, len);
                }
                is.close();
                outStream.write(LINEND.getBytes());
            }

        // 請求結束標誌
        byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
        outStream.write(end_data);
        outStream.flush();
        // 得到響應碼

        int res = conn.getResponseCode();

       //獲取伺服器返回結果

       InputStream in = conn.getInputStream();

        StringBuilder sb2 = new StringBuilder();
        if (res == 200) {
            int ch;
            while ((ch = in.read()) != -1) {
                sb2.append((char) ch);
            }
        }
        outStream.close();
        conn.disconnect();
        return sb2.toString();

    }

呼叫方式如下

public String uploadFile(File f){
        final Map<String, String> params = new HashMap<String, String>();
        params.put("sessionId", "123456");
        final Map<String, File> files = new HashMap<String, File>();
        files.put("uploadfile",f);
        String response=null;
        try {
            response = UploadUtil.post(FAULT_FILEUPLOAD_URL, params, files);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }