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;
}