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 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 ...
随机推荐
- Android开发之ContentProvider(内容提供者)
1. ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据,但数据访问 ...
- Java基础知识强化之网络编程笔记05:UDP之多线程实现聊天室案例
1. 通过多线程改进刚才的聊天程序,这样我就可以实现在一个窗口发送和接收数据了 2. 代码示例: (1)SendThread.java,如下: package com.himi.udpDemo2; ...
- yo bootstrap mui 使用对比
昨天晚上 又被问及职业发展方向,提及我的老本行css,切了几年的页面,近两年投入进css的时间屈指可数,被问及之前公司用的yo框架 对比业界内其他css 框架的优势. 1. yo模块化,碎片化 可自 ...
- jQuery 分页插件 jqPagination的使用
jqPagination 是一个简单易用的轻量级 jQuery分页插件,其使用了 HTML5 和 CSS3 技术来实现.此插件提供了几个参数设置选项,通过简单的配置即可生成分页控件.此外,它的外观样式 ...
- Oracle REGEXP_INSTR 用法
原文出处 ORACLE中的支持正则表达式的函数主要有下面四个: 1,REGEXP_LIKE :与LIKE的功能相似 2,REGEXP_INSTR :与INSTR的功能相似 3,REG ...
- 一个js 变量作用域问题
一个js 域问题,有一本书 叫 javasrcip pattert 好像是,写的很好,, <!DOCTYPE html> <html> <head lang=" ...
- JavaScript+DOM编程艺术【读书笔记】
第四章笔记: 如何让一个a标签不跳转: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www. ...
- C#当中的多线程_任务并行库(上)
复习: 第三章内容中我们提到了三种异步编程模型,这里简单复习一下,分别如下 1.APM(异步编程模式):形如Beginxxx,Endxxx. 2.EAP(基于事件的异步编程模式):这个我们在.net中 ...
- AutoResetEvent 详解
AutoResetEvent 允许线程通过发信号互相通信.通常,此通信涉及线程需要独占访问的资源. 线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号.如果 AutoRese ...
- 使用UIBezierPath和CAShapeLayer画各种图形
转载自:http://www.cocoachina.com/ios/20160214/15251.html CAShapeLayer 是 CALayer 的子类,但是比 CALayer 更灵活,可以画 ...