applet实现大文件ftp上传(一)

  • Post author:
  • Post category:其他


由于要用APPLET实现大文件FTP上传下载,从网上搜索了几下,找到很多资料,最后决定采用基于 org.apache.commons.net.ftp包实现FTP上传下载,Net包中的类既提供对协议的底层访问也有高层的抽象。在大多数情况下,抽 象是足够的,它可以使你不必编写解析各种协议的底层套接字的代码。使用抽象不会损失任何功能。

借此感叹,org.apache.commons开源包真是森罗万象,应有尽有。反观国内打着开源旗号的软件也不少,但没几个能在当前软林扬名立万的,借山本赵一句经典台词:悲哀 悲哀 真是悲哀。

开发环境:winxp+eclipse3.2.2+struts2.0+jse5.0

最早采用以下代码:

package yp;

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 java.util.ArrayList;

import java.util.List;

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;

public class ContinueFTP{

private File file=null;

//是否完整保留原文件名

private boolean isSaveFileName = true;

//枚举上传状态

public enum UploadStatus {

Create_Directory_Fail,      //远程服务器相应目录创建失败

Create_Directory_Success,   //远程服务器创建目录成功

Upload_New_File_Success,    //上传新文件成功

Upload_New_File_Failed,     //上传新文件失败

File_Exits,                 //文件已经存在

Remote_Bigger_Local,        //远程文件大于本地文件

Upload_From_Break_Success,  //断点续传成功

Upload_From_Break_Failed,   //断点续传失败

Delete_Remote_Faild;        //删除远程文件失败

}

//枚举下载状态

public enum DownloadStatus {

Remote_File_Noexist,    //远程文件不存在

Local_Bigger_Remote,    //本地文件大于远程文件

Download_From_Break_Success,    //断点下载文件成功

Download_From_Break_Failed,     //断点下载文件失败

Download_New_Success,           //全新下载文件成功

Download_New_Failed;            //全新下载文件失败

}

public void init(){

}

public FTPClient ftpClient = new FTPClient();

public ContinueFTP(){

//设置将过程中使用到的命令输出到控制台

this.ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));

}

public String getRemoteFileName(String localFileName){

String fileName =””;

//分隔符

String sepaRator=”\”;

if (localFileName.indexOf(sepaRator)<0)

sepaRator=”/”;

//最后分隔符位置

int idx = localFileName.lastIndexOf(sepaRator)+1;

fileName = localFileName.substring(idx);

return fileName;

}

public boolean isFileExist(String remoteFileName) throws IOException{

boolean isFileExist = false;

//检查远程是否存在文件

FTPFile[] files = ftpClient.listFiles(new String(remoteFileName.getBytes(“GBK”),”iso-8859-1″));

if(files!=null && files.length >= 1){

isFileExist = true;

}

return isFileExist;

}

public boolean connect(String hostname,int port,String username,String password) throws Exception{

boolean bl = false;

try{

ftpClient.connect(hostname, port);

}catch(Exception e){

//可具体报错到主机和端口号

throw new BaseException(“FTPConnError01”,new String[]{“connect”,e.getMessage()});

}

try{

//ftpClient.setControlEncoding(“GBK”);

if(FTPReply.isPositiveCompletion(ftpClient.getReplyCode())){

if(ftpClient.login(username, password)){

bl = true;

}

}

}catch(Exception e){

//可具体报错到用户和密码

throw new BaseException(“FTPConnError02”,new String[]{“connect”,e.getMessage()});

}

return bl;

}

public DownloadStatus download(String remote,String local) throws Exception{

//设置被动模式

ftpClient.enterLocalPassiveMode();

//设置以二进制方式传输

ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

DownloadStatus result;

//检查远程文件是否存在

FTPFile[] files = ftpClient.listFiles(new String(remote.getBytes(“GBK”),”iso-8859-1″));

if(files.length != 1){

throw new BaseException(“CellDataInputService”,new String[]{“download”,”远程文件”+remote+”不存在”});

}

long lRemoteSize = files[0].getSize();

File f = new File(local);

//本地存在文件,进行断点下载

if(f.exists()){

long localSize = f.length();

//判断本地文件大小是否大于远程文件大小

if(localSize >= lRemoteSize){

System.out.println(“本地文件大于远程文件,下载中止”);

return DownloadStatus.Local_Bigger_Remote;

}

//进行断点续传,并记录状态

FileOutputStream out = new FileOutputStream(f,true);

ftpClient.setRestartOffset(localSize);

InputStream in = ftpClient.retrieveFileStream(new String(remote.getBytes(“GBK”),”iso-8859-1″));

byte[] bytes = new byte[1024];

long step = lRemoteSize /100;

long process=localSize /step;

int c;

while((c = in.read(bytes))!= -1){

out.write(bytes,0,c);

localSize+=c;

long nowProcess = localSize /step;

if(nowProcess > process){

process = nowProcess;

if(process % 10 == 0)

System.out.println(“下载进度:”+process);

//TODO 更新文件下载进度,值存放在process变量中

}

}

in.close();

out.close();

boolean isDo = ftpClient.completePendingCommand();

if(isDo){

result = DownloadStatus.Download_From_Break_Success;

}else {

result = DownloadStatus.Download_From_Break_Failed;

}

}else {

OutputStream out = new FileOutputStream(f);

InputStream in= ftpClient.retrieveFileStream(new String(remote.getBytes(“GBK”),”iso-8859-1″));

byte[] bytes = new byte[1024];

long step = lRemoteSize /100;

long process=0;

long localSize = 0L;

int c;

while((c = in.read(bytes))!= -1){

out.write(bytes, 0, c);

localSize+=c;

long nowProcess = localSize /step;

if(nowProcess > process){

process = nowProcess;

if(process % 10 == 0)

System.out.println(“下载进度:”+process);

//TODO 更新文件下载进度,值存放在process变量中

}

}

in.close();

out.close();

boolean upNewStatus = ftpClient.completePendingCommand();

if(upNewStatus){

result = DownloadStatus.Download_New_Success;

}else {

result = DownloadStatus.Download_New_Failed;

}

}

return result;

}

public UploadStatus upload(File localFile,String remote) throws IOException{

//设置PassiveMode传输

ftpClient.enterLocalPassiveMode();

//设置以二进制流的方式传输

ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

//ftpClient.setControlEncoding(“GBK”);

UploadStatus result;

//对远程目录的处理

String remoteFileName = remote;

if(remote.contains(“/”)){

remoteFileName = remote.substring(remote.lastIndexOf(“/”)+1);

//创建服务器远程目录结构,创建失败直接返回

if(CreateDirecroty(remote, ftpClient)==UploadStatus.Create_Directory_Fail){

return UploadStatus.Create_Directory_Fail;

}

}

//检查远程是否存在文件

FTPFile[] files = ftpClient.listFiles(new String(remoteFileName.getBytes(“GBK”),”iso-8859-1″));

if(files!=null && files.length == 1){

long remoteSize = files[0].getSize();

//File f = new File(local);

long localSize = localFile.length();

if(remoteSize==localSize){

return UploadStatus.File_Exits;

}else if(remoteSize > localSize){

return UploadStatus.Remote_Bigger_Local;

}

//尝试移动文件内读取指针,实现断点续传

result = uploadFile(remoteFileName, localFile, ftpClient, remoteSize);

//如果断点续传没有成功,则删除服务器上文件,重新上传

if(result == UploadStatus.Upload_From_Break_Failed){

if(!ftpClient.deleteFile(remoteFileName)){

return UploadStatus.Delete_Remote_Faild;

}

result = uploadFile(remoteFileName, localFile, ftpClient, 0);

}

}else {

result = uploadFile(remoteFileName, localFile, ftpClient, 0);

}

return result;

}

public void disconnect() throws IOException{

if(this.ftpClient.isConnected()){

this.ftpClient.disconnect();

}

}

public String CreateDirecroty(String remoteDir)throws IOException{

String fillDir = “”;

UploadStatus st = CreateDirecroty(remoteDir,this.ftpClient);

if (st == UploadStatus.Create_Directory_Success)

fillDir = remoteDir;

else

fillDir = “”;

return fillDir;

}

public UploadStatus CreateDirecroty(String remote,FTPClient ftpClient) throws IOException{

UploadStatus status = UploadStatus.Create_Directory_Success;

String directory = remote.substring(0,remote.lastIndexOf(“/”)+1);

if(!directory.equalsIgnoreCase(“/”)&&!ftpClient.changeWorkingDirectory(new String(directory.getBytes(“GBK”),”iso-8859-1″))){

//如果远程目录不存在,则递归创建远程服务器目录

int start=0;

int end = 0;

if(directory.startsWith(“/”)){

start = 1;

}else{

start = 0;

}

end = directory.indexOf(“/”,start);

while(true){

String subDirectory = new String(remote.substring(start,end).getBytes(“GBK”),”iso-8859-1″);

if(!ftpClient.changeWorkingDirectory(subDirectory)){

if(ftpClient.makeDirectory(subDirectory)){

ftpClient.changeWorkingDirectory(subDirectory);

}else {

System.out.println(“创建目录失败”);

return UploadStatus.Create_Directory_Fail;

}

}

start = end + 1;

end = directory.indexOf(“/”,start);

//检查所有目录是否创建完毕

if(end <= start){

break;

}

}

}

return status;

}

public UploadStatus uploadFile(String remoteFile,File localFile,FTPClient ftpClient,long remoteSize) throws IOException{

UploadStatus status;

//显示进度的上传

long step = localFile.length() / 100;

long process = 0;

long localreadbytes = 0L;

RandomAccessFile raf = new RandomAccessFile(localFile,”r”);

OutputStream out = ftpClient.appendFileStream(new String(remoteFile.getBytes(“GBK”),”iso-8859-1″));

//断点续传

if(remoteSize>0){

ftpClient.setRestartOffset(remoteSize);

process = remoteSize /step;

raf.seek(remoteSize);

localreadbytes = remoteSize;

}

byte[] bytes = new byte[1024];

int c;

while((c = raf.read(bytes))!= -1){

out.write(bytes,0,c);

localreadbytes+=c;

//TODO 汇报上传状态

if(localreadbytes / step != process){

process = localreadbytes / step;

System.out.println(“上传进度:” + process);

}

}

out.flush();

raf.close();

out.close();

boolean result =ftpClient.completePendingCommand();

if(remoteSize > 0){

status = result?UploadStatus.Upload_From_Break_Success:UploadStatus.Upload_From_Break_Failed;

}else {

status = result?UploadStatus.Upload_New_File_Success:UploadStatus.Upload_New_File_Failed;

}

return status;

}

public List<String> getRemoteFileList(String remoteDir){

List<String> list = new ArrayList<String>();

FTPFile[] files;

try {

files = ftpClient.listFiles(remoteDir);

for (int i = 0; i < files.length; i++) {

list.add(files[i].getName());

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return list;

}

public boolean deleteFile(String fillFileName) throws Exception {

boolean bl = false;

this.ftpClient.deleteFile(fillFileName);

int status = this.ftpClient.getReplyCode();

if(status == 250){

bl = true;

System.out.println(“成功删除FTP服务器中文件:” + fillFileName);

}

return bl;

}

public boolean deleteDir(String remoteDir)throws Exception {

boolean isDel = false;

this.ftpClient.removeDirectory(remoteDir);

int status = this.ftpClient.getReplyCode();

if(status == 250){

isDel = true;

System.out.println(“成功删除FTP服务器中目录:” + remoteDir);

}

return isDel;

}

public static void main(String[] args) throws Exception {

ContinueFTP myFtp = new ContinueFTP();

try {

long l1 = System.currentTimeMillis();

System.out.println(“begin:”+ l1);

if (myFtp.connect(“10.68.7.182”, 21, “a”, “a”)) {

String mkDir = myFtp.CreateDirecroty(“TTT/ccc/”);

if (mkDir != null && !mkDir.trim().equals(“”))

System.out.println(“mkDir success:”+mkDir);

//myFtp.download( “/XA01B03H05/5.mp3″,file,”0”);

//myFtp.upload(“/XA01B03H05/5.mp3”, “/云台山.mpg”);

//myFtp.delete_file(“/tmp.txt”);

//String str = new String(“电视剧”);

//myFtp.ftpClient.removeDirectory(“/kkk/jk/”);

//myFtp.ftpClient.makeDirectory(new String(str.getBytes(“GBK”),”iso-8859-1″));

myFtp.disconnect();

long l2 = System.currentTimeMillis();

System.out.println(“end:”+ l2);

System.out.println(“remaining:”+(l2-l1));

}

} catch (IOException e) {

System.out.println(“连接FTP出错:”+e.getMessage());

}

}

public File getFile() {

return file;

}

public void setFile(File file) {

this.file = file;

}

public boolean isSaveFileName() {

return isSaveFileName;

}

public void setSaveFileName(boolean isSaveFileName) {

this.isSaveFileName = isSaveFileName;

}

}

通过main函数测试效果也不错,还显示上传或下载进度,再写一个对应action及jsp实现ftp上传下载,不过我吃过一次大亏,本来web服务器是本机,我原来做的上传下载都没有问题,但web服务器不在本机,就报说找不到文件!

后来终于想明白了,系统还是web服务器上路径找文件,当然找不到。

难道通过浏览器访问本地资源就真的无解了吗?

当然不是,也在找了很多解决方案,各有所长,又各有短处.

1.使用表单直接提交

这个方案肯定是被弊掉的,人一多就可能拖跨服务器.

2.使用FLASH进行文件上传

这个方案也尝试过,不过据说AS3只支持100M左右的文件上传,大文件无法上传,不知道是不是这样?如果是这样的话,那么这个方案也将行不通了.使用FLASH进行文件上传相对而言开发不会太困难.

3.使用APPLET进行文件上传

使用APPLET开发文件上传控件,这个对于使用JAVA来进行开发的非常方便,因为我们项目就是使用JAVA的,不过APPLET有安全策略的问题,无 法读取客户端的问题.如果要,那么用户必须修改其java.policy文件,这对于用户来讲简直是不可能的.如果使用程序下载动态修改也会比较麻烦.其 实还有一个破解之法,就是采用数字证书。

缺点:1)、客户端必须安装JRE;2)、想办法突破APPLET有安全策略的问题

优点:java编写,支持多浏览器

4.使用ACTIVEX进行文件上传

这是我目前认为比较可行的方式,而且网上也有很多类似这样的控件,但是都不是免费的,所以只能自己想办法解决了.使用ACTIVEX开发,可以使用C++、DELPHI等来进行开发

缺点:1>客户端推荐IE;2>IE安全设置中启用activex控件

优点:ACTIVEX开发速度快、界面友好

以下方案先采用第三种方案—Java applet,当年让Java 大火了一把的号称与微软的ActiveX 相提并论的技术当然,现在Java 出了JavaFX,是不是Applet 的替代品呢?你猜,猜不着呀?你再猜^_^