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;
}
使用HTTP进行文件下载其实就是获取输入流数据,然后通过输出流写到文件里,主要代码跟上面FTP下载差不多
至于使用哪个协议下载好,就要看具体情况了,对个人而言没啥区别
FTP协议是专门用于文件传输的协议,网络传输更加安全
HTTP只是超文本传输协议,最原始的目的仅仅只是为了将超文本的信息(包括图象视频等)传到你的机子上就行了,但是使用方便