Java ftp断点续传
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
/**
* 在这个版本中希望做的是软件的多线程下载
* 现在也做了断点下载 ,是否断点下载的依据为目标文件是否已经存在 ,不管是否从断点出继续下载,
* 分的线程下载的文件都是从指定的位置开始下载的
*
*/
public class FtpTransFile {
private static String fileName; // 要上传或下载的文件的名字
private static String path;// 临时文件夹的目录,用于存放多个线程下载的文件
static long threadBlock = 100 * 1024 * 1024L;
/**
*
* @param path
* 要上传的本地文件路径 如"C:/Users/repace/Desktop/zhangke1.txt";
* @param server
* ftp服务器ip地址 192.168.242.133
* @param userName
* 登录ftp的用户名 test
* @param password
* 登录ftp用户名对应的密码 123456
*/
public static void fileUpload(String OStype, String path, String server,
String userName, String password) { // 要上传的文件的本地路径路径
// 目前可完成单个文件的上传
if (!(OStype.equalsIgnoreCase("windows") || OStype
.equalsIgnoreCase("linux"))) {
System.out.println("操作系统类型输入错误,应为windows或linux");
return;
}
FTPClient ftpClient = new FTPClient();
ftpClient.enterLocalPassiveMode(); // 这一句话一定要记得加上
FileInputStream fis = null;
try {
ftpClient.connect(server);
ftpClient.login(userName, password);
File srcFile = new File(path);// 要上传的本地文件路径
fis = new FileInputStream(srcFile);
String storeName = srcFile.getName();// 要存储的文件的名字
String remoteFilename = "/mnt/data/ftp/www/" + OStype.toLowerCase() + "/"
+ storeName;
ftpClient.changeWorkingDirectory("/mnt/data/ftp/www/" + OStype.toLowerCase()
+ "/"); // 设置上传的文件在centos上的目录,文件上传不成功是要查看指定目录的权限
ftpClient.setBufferSize(1024);
ftpClient.setControlEncoding("UTF-8");
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);// 设置文件类型(二进制)
FTPFile[] files = ftpClient.listFiles(remoteFilename);// 判断软件中心是否包含这个文件
if (files.length == 1) {// 软件中心包含该文件
long remoteSize = files[0].getSize();// 软件中心的文件大小
long localSize = srcFile.length();// 打算要上传的文件大小
if (remoteSize == localSize) { // 软件中心有这个文件,并且和打算要上传的文件大小一样,则说要上传的文件已存在
System.out.println("要上传的文件已存在");
ftpClient.disconnect();
return;
} else if (remoteSize > localSize) {// 软件中心的文件比要上传的大,可能新上传的文件被修改了,然后再次上传的
System.out.println("软件中心的软件比即将上传的要大,无须上传或重新命名要上传的文件名");
ftpClient.disconnect();
return;
}
// 软件中心存的文件比要上传的文件小,则尝试移动文件内读取指针,实现断点续传 **************
if (fis.skip(remoteSize) == remoteSize) {
ftpClient.setRestartOffset(remoteSize);
boolean i = ftpClient.storeFile(
new String(storeName.getBytes("UTF-8"),
"iso-8859-1"), fis);
if (i) {
System.out.println("文件断点续传成功");
ftpClient.disconnect();
return;
}
}
} else { // 软件中心不包含要上传的文件,或者续传不成功,则上传全新的文件即可
boolean i = ftpClient.storeFile(
new String(storeName.getBytes("UTF-8"), "iso-8859-1"),
fis);
System.out.println("文件上传" + i);
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("FTP客户端出错!", e);
} finally {
try {
fis.close();
ftpClient.disconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException("关闭FTP连接发生异常!", e);
}
}
}
/**
* *
*
* @param OStype
* 操作系统的类型 windows或者是linux
* @param fileName
* 指出要下载的文件名字 加后缀的
* @param storePath
* 下载之后想要在本地的存储路径,在window系统中支持两种文件路径\\ 或者/
* @param server
* ftp服务器IP地址
* @param userName
* ftp分配的登录名 test
* @param password
* 与登录名对应的登录密码 123456
* @throws FileNotFoundException
* @throws InterruptedException
*/
public static void fileDownload(String OStype, String fileNames,
String storePath, String server, String userName, String password)
throws FileNotFoundException, InterruptedException { // 参数是带后缀的文件名字和下载之后要存储的本地路径
// 可完成单个文件的下载 ,
if (!(OStype.equalsIgnoreCase("windows") || OStype
.equalsIgnoreCase("linux"))) {
System.out.println("操作系统类型输入错误,应为windows或linux");
return;
}
fileName = fileNames;
File file = new File(storePath);
if (!file.exists()) {// 判断文件夹是否存在,如果不存在则创建文件夹
file.mkdir();
}
FTPClient ftpClient = new FTPClient();
ftpClient.enterLocalPassiveMode(); // 这一句话一定要记得加上
String remoteFileName = "/mnt/data/ftp/www/" + OStype.toLowerCase() + "/"
+ fileName; // 服务器上的文件,前面是文件夹的名字,后面的是文件的名字
String localFileName = "";// 本地要存储的文件绝对路径 文件夹加上文件名
try {
ftpClient.connect(server);
ftpClient.login(userName, password);
ftpClient.setBufferSize(1024);
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 设置文件类型(二进制)
FTPFile[] files = ftpClient.listFiles(remoteFileName);
if (files.length == 0) { // 判断软件中心是否有要下载的软件
System.out.println("软件中心没有找到要下载的软件");
ftpClient.disconnect();
return;
} else { //软件中心包含请求下载的文件
long localSize = 0L; // 记录本地文件的大小
if (storePath.endsWith("\\") || storePath.endsWith("/"))// 存储路径直接是某个盘下的根目录或者用户加上了最后的斜线
{
localFileName = storePath + fileName;
path = storePath
+ fileName.substring(0, fileName.indexOf("."))
+ "Temp/";
} else {
localFileName = storePath + "/" + fileName;
path = storePath + "/"
+ fileName.substring(0, fileName.indexOf("."))
+ "Temp/";
}
File localFile = new File(localFileName);
long remoteSize = files[0].getSize();// 软件中心的文件大小
if (localFile.exists()) {// 指定下载的文件在本地文件夹内已经存在
localSize = localFile.length();// 已存在的文件大小
if (remoteSize == localSize) {
System.out.println("文件已下载过,无需再下载");
return;
} else if (remoteSize > localSize) { // 之前下载未完成,实现断点下载
System.out.println("断点下载。。。");
}
if (remoteSize < localSize) {// 如果本地的文件比软件中心的文件大,则说明本地的文件可能有错,删除,然后从头开始下载
localFile.delete();
System.out.println("软件从新开始下载");
localSize = 0L;
}
} else {// 指定下载的文件在本地文件夹内不存在,从头下载文件
localSize = 0L;
System.out.println("软件从头下载");
}
File tempfile = new File(path);
if (tempfile.exists()) {// 判断文件夹是否存在,如果已经存在,则删除该文件夹及其所有的子文件,以免其包含的线程影响后面的下载过程
System.out.println("delete 之前的临时文件夹");
deleteTempFile(path);
}
tempfile.mkdir();// 新建存放临时文件夹的目录
ExecutorService exec = Executors.newCachedThreadPool(); // 开始启动多线程下载文件
int threadNum = (int) ((remoteSize - localSize) / threadBlock + 1);// 每100M分一个线程下载
// 计算线程总数
System.out.println("分成的线程个数" + threadNum);
CountDownLatch latch = new CountDownLatch(threadNum);
System.out.println(fileNames + "请求还需下载的文件大小"
+ (remoteSize - localSize));
long[] startPos = new long[threadNum];
ChildThread[] childThreads = new ChildThread[threadNum];// ChildThread
// 变成ChildThread1共有4处修改
for (int i = 0; i < threadNum; i++) {
startPos[i] = localSize + i * threadBlock; // 设置每个线程开始下载文件的起始位置
childThreads[i] = new ChildThread(OStype, fileName,
storePath, server, userName, password, startPos[i],
i, latch); // 创建线程 线程编号从0开始
exec.execute(childThreads[i]);// 开始执行线程
}
latch.await(); // 等待所有的线程都运行结束
exec.shutdown();
tempFileToTargetFile(localFileName, childThreads, threadNum);// 把临时得到的文件夹内的文件合并到目标文件
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("FTP客户端出错!", e);
} finally {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("关闭FTP连接发生异常!", e);
}
}
}
/**
* @author repace 把临时文件夹内的文件都写入目标文件内 即将各个线程所下的文件进行合并
* @param target
* 目标文件 是之前用户发送的请求要把请求下载的文件存放的绝对路径的目录
* 比如要下载的是test.txt文件,想存在c:\\123\\文件夹内 则目标文件target
* 指的的就是c:\\123\\test.txt
* @param tempFile
* 临时文件夹的目录则是 c:\\123\\testTemp\\
* @param threadNum
* @return
* @throws IOException
*/
public static boolean tempFileToTargetFile(String target,
ChildThread[] childThreads, int threadNum) throws IOException { // 完成把临时文件夹内的日志都写到目标文件中
System.out.println("KAISHI HEBING");
boolean result = true;
FileInputStream inputStream = null;
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(target, true); // 追加内容
for (int i = 0; i < threadNum; i++) { // 遍历所有子线程创建的临时文件,按顺序把下载内容写入目标文件中
inputStream = new FileInputStream(
childThreads[i].localTempFileName);
int len = 0;
byte[] b = new byte[1024];
int count = 0;
while ((len = inputStream.read(b)) != -1) {
outputStream.write(b, 0, len);
outputStream.flush();
count += len;
}
inputStream.close();
}
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (outputStream != null) {
outputStream.close();
}
File file = new File(target);
System.out.print(target + "下载得到的文件大小是 " + file.length());
deleteTempFile(path);// 删除临时文件夹
return result;
}
public static void deleteTempFile(String Path) {//删除临时文件夹
File file = new File(Path);
if (file.isFile()) {// 表示该文件不是文件夹
file.delete();
} else {
// 首先得到当前的路径
String[] childFilePaths = file.list();
for (String childFilePath : childFilePaths) {
File childFile = new File(file.getAbsolutePath() + "/"
+ childFilePath);
String s = childFile.getAbsolutePath();
deleteTempFile(s);
}
file.delete();
}
}
}
|
ChildThread类
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
import org.apache.commons.net.ftp.FTPClient;
public class ChildThread extends Thread {
public int id;
private long startPosition;
CountDownLatch latch;
String remoteFileName; //要下载的文件在软件中心的文件
String localTempFileName; //用于存放每个线程下载的临时文件的绝对路径 (带上临时文件的名字和后缀)
String path;//临时文件夹的目录
FTPClient ftpClient = new FTPClient();
public ChildThread(String OStype,String fileName, String storePath,
String server, String userName, String password,long startPos,int id,CountDownLatch latch) {
ftpClient.enterLocalPassiveMode(); // 这一句话一定要记得加上
remoteFileName = "/mnt/data/ftp/www/"+OStype.toLowerCase() +"/"+ fileName; // 服务器上的文件,前面是文件夹的名字,后面的是文件的名字
startPosition=startPos;
this.latch=latch;
this.id=id;
try {
ftpClient.connect(server);
ftpClient.login(userName, password);
ftpClient.setBufferSize(1024);
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 设置文件类型(二进制)
if (storePath.endsWith("\\") || storePath.endsWith("/"))//给出的路径下新建一个临时文件夹,里面存储的是各个线程下载的文件
{
localTempFileName=storePath +fileName.substring(0, fileName.indexOf("."))+"Temp/" +id+"_"+fileName;//保证临时文件夹唯一 也应保证临时文件的命名唯一
} else{
localTempFileName=storePath + "/" +fileName.substring(0, fileName.indexOf("."))+"Temp/" + id+"_"+fileName;
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("FTP客户端出错!", e);
} finally {
}
}
public void run() {
FileOutputStream outputStream = null;
try {
File threadTempFile=new File(localTempFileName);
outputStream = new FileOutputStream(localTempFileName,true);
ftpClient.setRestartOffset(startPosition+threadTempFile.length()); //设置每个线程开始的下载位置 如果之前threadTempFile.length()不等于0,则从上次那个地方继续下载 断点下载
InputStream in= ftpClient.retrieveFileStream(remoteFileName);
int len = 0;
byte[] b = new byte[1024];
long count=threadTempFile.length();
while((len = in.read(b)) != -1) {
count +=len;//记录文件中的长度加上这次准备写的长度
if (count > FtpTransFile.threadBlock) { //加上最后一次读到的已经比规定的线程块大,则只取前面一部分即可
int lastLen= (int) (FtpTransFile.threadBlock-threadTempFile.length());
outputStream.write(b, 0,lastLen);//方法write(b, off, len),b[off]是写入的第一个字节和b[off+len-1]是写的这个操作的最后一个字节。
outputStream.flush();
break;
}
outputStream.write(b, 0, len);
outputStream.flush();
}
in.close();//关闭流
File file=new File(localTempFileName);
System.out.println("Thread file "+id+" "+file.length());
outputStream.close();
ftpClient.disconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
latch.countDown();//每个线程结束的时候,则总的线程数减1
}
}
|
Java ftp断点续传的更多相关文章
- Java FTP客户端开源类库 edtFTPj
edtFTPj/Free是免费的流行的Java FTP库,全球公司依靠edtFTPj /Free 为它们的Java应用程序添加FTP客户端功能. (收费的支持SFTP.FTPS的edtFTPj/PRO ...
- 用 Java 实现断点续传 (HTTP)
断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 wwww.sjtu.edu ...
- 用edtftpj实现Java FTP客户端工具
edtftpj是一个java FTP工具包,使用非常方便,感觉比Apache的好用,但Apache更灵活.edtftpj有多种版本,分别是java..net和js版本.对于Java版的有一个免费版本. ...
- 关于Java FTP SFTP的相关实际问题
第一个: java ftp使用的是Apache common-net,但是FTP服务侧提供的FTP服务器只支持SFTP,结果报 java.net.ConnectException: Connectio ...
- Java ftp上传文件方法效率对比
Java ftp上传文件方法效率对比 一.功能简介: txt文件采用ftp方式从windows传输到Linux系统: 二.ftp实现方法 (1)方法一:采用二进制流传输,设置缓冲区,速度快,50M的t ...
- java 实现断点续传
请求头一:>>>>>>>>>>>>>>>>>>>>>>>> ...
- (转)【Java FTP及FTP服务器搭建】
转至 http://blog.csdn.net/studyvcmfc/article/details/8147052 目录(?)[+] -[Java FTP及FTP服务器搭建] 一:本文采用apach ...
- 用Java实现断点续传的基本思路和代码
用Java实现断点续传的基本思路和代码 URL url = new URL(http://www.oschina.net/no-exist.zip); HttpURLConnection http ...
- 用 Java 实现断点续传参考 (HTTP)
断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 ...
随机推荐
- vs2012 aspx 没有设计视图了?
vs2012的html设计视图没有了!重新安装一次都不行!现在已经通过简单办法来解决了 其实当你打开 HTML设计器 设置时, “启用 HTML设计器" 这里是打勾的!这时千万不要放弃.先 ...
- Android源码编译过程之九鼎开发板
build_kernel() { # 进入源码顶层目录 cd ${BS_DIR_KERNEL} || # 编译配置文件 make ${BS_CONFIG_KERNEL} ARCH=arm CROSS_ ...
- JSON 学习总结 <一>:什么是JSON
JSON的相关资料和博客很多,JSON无处不用,最近项目中一直要用到JSON,今天没有加班,就写下,算是对自己的总结,对JSON又一次深入的认识. 废话不多了,直接进入今天的主题: 如题:今天就介绍下 ...
- ASP.NET MVC 第六回 过滤器Filter
在Asp.netMvc中当你有以下及类似以下需求时你可以使用Filter功能 判断登录与否或用户权限 决策输出缓存 防盗链 防蜘蛛 本地化与国际化设置 实现动态Action Filter是一种声明式编 ...
- Javascript基础学习(3)_对象和数组
一.对象是一种无序的属性集合,每个属性都有自己的名字和值. 1.创建对象 花括号内逗号分隔 var person = { "Name" : "LiCheng", ...
- 查看oracle 启动了多久
想看一下系统正常运行了多少天?开机多长时间没有重启了? windows系统 C:\>systeminfo |find “系统启动时间”系统启动时间: 265 天 4 小时 26 分 32 ...
- 动效解析工厂:Mask 动画
转载自:http://www.cocoachina.com/ios/20160214/15250.html 前言:很多动效都是多种动画的组合,有时候你可能只是需要其中某个动画,但面对庞杂的代码库或是教 ...
- IOS-UI- UIScrollView 滚动视图(1)
滚动视图多个页面实现的原理 滚动视图位置不变 内容的位置发生改变. 滚动视图的运用1.分页查看图片 2.查看大图片 3.当内容过多需要一个页面显示,如:注册,修改个人信息等等4.当不希望用户感觉咱们的 ...
- C#程序:如何创建xml文件以及xml文件的增、删、改、查
其实今天的这篇博文 ,是对请几天发表的博文的一个总结,只是想把xml文件的增删改查结合起来,这样更容易让初学的朋友理解,废话也不多说了,开始吧! 下面是我把我在vs环境下写的代码ctrl+V然后ctr ...
- sae的kvdb使用注意
之前没仔细看,原来sae的kvdb使用一定要先调用初始化函数 $kv = new SaeKV(); $kv->init();//必须使用 $kv->set('index', $data);