1 断点续传
1.1 简介
断点续传指得是在上传或是下载时,由于网络或是其他原因中断了,下次可以从中断的的地方继续上传或是下载,不需要重头来过,用途可以提高效率。
1.2 原理
原理其实很简单,比起普通的上传或是下载,你多了一步设置上传和下载的开始位置。
如果是上传,你需要向服务器发送请求,知道在服务器上的文件的大小,然后以这个大小为开始位置上传。反之,下载就是你告诉服务器你本地文件的大小,服务器会根据这个大小设置开始位置下载。
1.3 实例
文件服务端:Apache Ftp Server(任意ftp服务器都可以)
客户端:commons-net.jar中的FTPClient
package com.rj.eden.attachment.test;
import java.io.File;
import java.io.FileOutputStream;
package com.rj.eden.attachment.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import com.rj.eden.attachment.common.CommonContext;
import com.rj.eden.attachment.common.DownloadStatus;
import com.rj.eden.attachment.common.UploadStatus;
/**
* 功能说明: <br>
* 系统版本: v1.0 <br>
* 开发人员: chenglibin@rongji.com<br>
* 开发时间: 2013-9-30<br>
* 审核人员: <br>
* 相关文档: <br>
* 修改记录: <br>
* 修改日期 修改人员 修改说明 <br>
* ======== ================================================== <br>
*
*/
public class TestFTP {
public FTPClient ftpClient = new FTPClient();
public static String FTP_ENCODE="UTF-8";
public TestFTP(){
}
/**
* 连接到FTP服务器
* @param hostname 主机名
* @param port 端口
* @param username 用户名
* @param password 密码
* @return 是否连接成功
* @throws IOException
*/
public booleanconnect(String hostname,int port,String username,String password) throwsIOException{
ftpClient.setControlEncoding(FTP_ENCODE);
ftpClient.connect(hostname,port);
if(FTPReply.isPositiveCompletion(ftpClient.getReplyCode())){
if(ftpClient.login(username,password)){
returntrue;
}
}
disconnect();
returnfalse;
}
/**
* 断开与远程服务器的连接
* @throws IOException
*/
public voiddisconnect() throws IOException{
if(ftpClient.isConnected()){
ftpClient.disconnect();
}
}
/**
* 上传文件到FTP服务器,支持断点续传
* @param local 本地文件名称,绝对路径
* @param remote 远程文件路径,使用/home/directory1/subdirectory/file.ext按照Linux上的路径指定方式,支持多级目录嵌套,支持递归创建不存在的目录结构
* @return 上传结果
* @throws IOException
*/
public booleanupload(String local,String remote) throws IOException{
//设置PassiveMode传输
ftpClient.enterLocalPassiveMode();
//设置以二进制流的方式传输
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
//对远程目录的处理
StringremoteFileName = remote;
if(remote.contains("/")){
remoteFileName= remote.substring(remote.lastIndexOf("/")+1);
//创建服务器远程目录结构,失败报异常
CreateDirecroty(remote);
}
//检查远程是否存在文件
FTPFile[]files = ftpClient.listFiles(remoteFileName);
File f =new File(local);
RandomAccessFileraf = new RandomAccessFile(f,"r");
OutputStreamout = ftpClient.appendFileStream(remoteFileName);
longlocalSize = f.length();
if(files.length== 1){
//服务器文件的大小
longremoteSize = files[0].getSize();
if(remoteSize<localSize){
//设置上传的开始位置
raf.seek(remoteSize);
}else{
ftpClient.deleteFile(remoteFileName);
}
}
byte[]bytes = new byte[1024*1024];
int c;
while((c =raf.read(bytes))!= -1){
out.write(bytes,0,c);
}
out.flush();
raf.close();
out.close();
returnftpClient.completePendingCommand();
}
/**
* 递归创建远程服务器目录
* @param remote 远程服务器文件绝对路径
* @throws IOException
*/
public voidCreateDirecroty(String remote) throws IOException{
Stringdirectory = remote.substring(0,remote.lastIndexOf("/")+1);
if(!directory.equalsIgnoreCase("/")&&!ftpClient.changeWorkingDirectory(directory)){
//如果远程目录不存在,则递归创建远程服务器目录
intstart=0;
intend = 0;
if(directory.startsWith("/")){
start = 1;
}else{
start= 0;
}
end= directory.indexOf("/",start);
while(true){
StringsubDirectory = remote.substring(start,end);
if(!ftpClient.changeWorkingDirectory(subDirectory)){
if(ftpClient.makeDirectory(subDirectory)){
ftpClient.changeWorkingDirectory(subDirectory);
}else{
throw new IOException("创建文件夹"+subDirectory+"失败");
}
}
start= end + 1;
end= directory.indexOf("/",start);
//检查所有目录是否创建完毕
if(end<= start){
break;
}
}
}
}
/**
* 从FTP服务器上下载文件,支持断点续传,上传百分比汇报
* @param remote 远程文件路径
* @param local 本地文件路径
* @return 上传的状态
* @throws IOException
*/
public booleandownload(String remote,String local) throws IOException{
//设置被动模式
ftpClient.enterLocalPassiveMode();
//设置以二进制方式传输
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
File f =new File(local);
FileOutputStreamout = new FileOutputStream(f,true);
//检查远程文件是否存在
FTPFile[]files = ftpClient.listFiles(remote);
if(files.length==0){
thrownew IOException("服务器不存在"+remote);
}
//本地存在文件,进行断点下载
if(f.exists()){
longlocalSize = f.length();
longRemoteSize = files[0].getSize();
if(localSize<RemoteSize){
//设置下载的开始位置
ftpClient.setRestartOffset(localSize);
}
}
InputStreamin = ftpClient.retrieveFileStream(remote);
byte[]bytes = new byte[1024*1024];
int c;
while((c =in.read(bytes))!= -1){
out.write(bytes,0,c);
}
in.close();
out.close();
returnftpClient.completePendingCommand();
}
public staticvoid main(String[] args) {
TestFTPmyFtp = new TestFTP();
try {
myFtp.connect("127.0.0.1",2121,"admin","admin");
System.out.println(myFtp.upload("D:\\upload1\\合成A.jpg","/我们/合成A.jpg"));
//System.out.println(myFtp.download("/zhenchen.jpg","D:\\upload1\\我们\\cc1.txt"));
myFtp.disconnect();
} catch(IOException e) {
System.out.println("连接FTP出错:"+e.getMessage());
}
}
}