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 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 ...
随机推荐
- OKHttp的简单使用
一方面,最近关于OKHttp的讨论甚嚣尘上,另一方面,我最近也更新了android6.0,发现在6.0中HttpClient不能使用了,于是决定抽时间也看一下OKHttp,总结了一点东西,与大家分享. ...
- 使用Socket模拟一个简单的Webservice调用
webservice是对socket的一个封装,让远程调用调用变得更加简单,那么使用socket究竟有多么麻烦呢?来看看. 做一个简单的天气查询: 服务端: public class SocketSe ...
- git 远程追踪
$ git branch --set-upstream-to origin/master http://stackoverflow.com/questions/21729560/how-to-make ...
- JS获取URL的参数
function request(paras) { var url = location.href; , url.length).split("&"); var paraO ...
- UESTCOJ-BiliBili, ACFun… And More!(水题)
BiliBili, ACFun… And More! Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Ja ...
- final----这篇文章是我收获很大
final 用于声明属性.方法和类,分别表示属性不可变,方法不可重写,类不可继承. [转]Java final 修饰符知识点总结 final从字面上理解含义为“最后的,最终的”.在Java中也同样表示 ...
- A题笔记(4)
No. 1384 这题没啥 不过网考成绩出了,发现我的口语分数相较其他人还挺高的~~~哈哈哈 Code::Blocks 有时在程序运行结束后,.exe 并没有结束,因而之后无论怎么调试和修改代码,运行 ...
- Oracle 11g 虚拟列 Virtual Column介绍
Oracle 11G 虚拟列 Virtual Column Oracle 11G 在表中引入了虚拟列,虚拟列是一个表达式,在运行时计算,不存储在数据库中,不能更新虚拟列的值. 定义一个虚拟列的语法: ...
- (七)Struts2 验证框架
所有的学习我们必须先搭建好Struts2的环境(1.导入对应的jar包,2.web.xml,3.struts.xml) 第一节:Struts2 验证简介 Struts2 基于Struts2 拦截器,为 ...
- struts2 测试错题解析
解析:$.parseJSON()方法是将字符串转换成Json类型数据,$.getJSON()方法是获取JSON数据,两者不用联合使用. 解析: A:ActionContext接口没有getReques ...