在项目中可能有时候上传的文件比较大。如果用http方式来进行文件上传的话,问题比较多。 所用可以采用ftp的方式,但是一般都是做的web项目,要在浏览器中嵌入,因为对于java的话,有applet的方式,当然applet用户需要安装JRE。一般的JRE大概在 10M多点。applet是在一个“沙箱”里运行,不能对用户的文件进行读取,如果要读取本地的文件,就需要对其进行授权。需要用到java_home/bin目录下的一些工具。 下面的网上找的一些关于文件上传的代码和操作方式,稍微修改了一下。
一、下面是applet的代码,其他需要用到commons-net-3.0-src包,可以到apache的官网网站上下载。
之所以用src的包是因为需要把applet的代码和commons-net的代码都打成一个jar包。所以把commons-net-3.0-src下的源码和applet的代码放到同一个目录下,
然后打成jar包。
package com.test.ftp;
import java.applet.Applet;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
/**
* 说明:本APPLET只是测试大文件FTP上传可行性
* 至于其他功能比如FTP下载、删除、FTP服务器文件列表可调用ContinueFTP相应功能。
*/
public class FileFtpApplet extends Applet {
/**
* Constructor of the applet.
*
* @exception HeadlessException if GraphicsEnvironment.isHeadless()
* returns true.
*/
/* public FileFtpApplet() throws HeadlessException {
super();
}*/
/**
* Called by the browser or applet viewer to inform
* this applet that it is being reclaimed and that it should destroy
* any resources that it has allocated. The <code>stop</code> method
* will always be called before <code>destroy</code>. <p>
*
* A subclass of <code>Applet</code> should override this method if
* it has any operation that it wants to perform before it is
* destroyed. For example, an applet with threads would use the
* <code>init</code> method to create the threads and the
* <code>destroy</code> method to kill them. <p>
*/
public void destroy() {
// Put your code here
}
/**
* Returns information about this applet. An applet should override
* this method to return a <code>String</code> containing information
* about the author, version, and copyright of the applet. <p>
*
* @return a string containing information about the author, version, and
* copyright of the applet.
*/
public String getAppletInfo() {
return "This is my default applet created by Eclipse";
}
/**
* Called by the browser or applet viewer to inform
* this applet that it has been loaded into the system. It is always
* called before the first time that the <code>start</code> method is
* called. <p>
*
* A subclass of <code>Applet</code> should override this method if
* it has initialization to perform. For example, an applet with
* threads would use the <code>init</code> method to create the
* threads and the <code>destroy</code> method to kill them. <p>
*/
public void init() {
// Put your code here
}
/**
* Called by the browser or applet viewer to inform
* this applet that it should start its execution. It is called after
* the <code>init</code> method and each time the applet is revisited
* in a Web page. <p>
*
* A subclass of <code>Applet</code> should override this method if
* it has any operation that it wants to perform each time the Web
* page containing it is visited. For example, an applet with
* animation might want to use the <code>start</code> method to
* resume animation, and the <code>stop</code> method to suspend the
* animation. <p>
*/
public void start() {
// Put your code here
}
/**
* Called by the browser or applet viewer to inform
* this applet that it should stop its execution. It is called when
* the Web page that contains this applet has been replaced by
* another page, and also just before the applet is to be destroyed. <p>
*
* A subclass of <code>Applet</code> should override this method if
* it has any operation that it wants to perform each time the Web
* page containing it is no longer visible. For example, an applet
* with animation might want to use the <code>start</code> method to
* resume animation, and the <code>stop</code> method to suspend the
* animation. <p>
*/
public void stop() {
// Put your code here
}
private static final long serialVersionUID = 1L;
private FileFtpApplet jFrame = null;
private JButton jFileButton = null;
public FileFtpApplet() {
// TODO Auto-generated constructor stub
jFrame = this;
this.setSize(496, 260);
jFileButton = new JButton("打开文件");
jFrame.add(jFileButton);
jFileButton.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent e) {
// System.out.println("mouseClicked()"); // TODO
// Auto-generated Event stub mouseClicked()
JFileChooser jfChooser = new JFileChooser("D:\\..\\..");
jfChooser.setDialogTitle("打开并上传文件");
jfChooser.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
if (f.getName().endsWith("dat") || f.isDirectory())
return true;
return false;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return "数据文件(*.dat)";
}
});
int result = jfChooser.showOpenDialog(jFrame);
if (result == JFileChooser.APPROVE_OPTION) { // 确认打开
File fileIn = jfChooser.getSelectedFile();
if (fileIn.exists()) {
//JOptionPane.showMessageDialog(jFrame, "OPEN"); // 提示框
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 remotePath = "/";
String remoteFile = myFtp.getRemoteFileName(fileIn.getName());
if (remotePath == null || remotePath.trim().equals(""))
remotePath = "/";
//文件扩展名
String kzNm = "";
if (remoteFile.indexOf(".")>=0)
kzNm = remoteFile.substring(remoteFile.indexOf("."));
String cellCode = "yp";
boolean isSaveFileName = false;
//若不保留原文件,则重新组装远程文件名
if (!isSaveFileName)
remoteFile= cellCode+"_"+System.currentTimeMillis()+kzNm;
//获得远程路径最后一位
String lastStr = remotePath.substring(remotePath.length()-1);
if (lastStr.trim().equals("/"))
remoteFile = remotePath + cellCode + "/" + remoteFile;
else
remoteFile = remotePath + "/" +cellCode + "/" + remoteFile;
myFtp.upload(fileIn, remoteFile);
myFtp.disconnect();
long l2 = System.currentTimeMillis();
System.out.println("end:"+ l2);
System.out.println("remaining:"+(l2-l1));
}
} catch (Exception e1) {
System.out.println("连接FTP出错:"+e1.getMessage());
}
} else {
}
} else if (result == JFileChooser.CANCEL_OPTION) {
System.out.println("Cancel button is pushed.");
} else if (result == JFileChooser.ERROR_OPTION) {
System.err.println("Error when select file.");
}
}
}
);
}
}
package com.test.ftp;
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;
/**
* FTP上传下载文件
* 支持断点续传
* @version: 2009-10-23 下午04:28:48
*/
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)));
}
/**
* 功能:通过本地文件名指定远程文件名
* @param localFileName
* @return
* String
* 范例:
*/
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;
}
/**
* 功能:检查远程是否存在文件
* @param remoteFileName 远程文件名
* @return
* @throws IOException
* boolean
* 范例:
*/
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;
}
/**
* 连接到FTP服务器
* @param hostname 主机名
* @param port 端口
* @param username 用户名
* @param password 密码
* @return 是否连接成功
* @throws IOException
*/
public boolean connect(String hostname,int port,String username,String password) throws Exception{
boolean bl = false;
try{
ftpClient.connect(hostname, port);
}catch(Exception e){
//可具体报错到主机和端口号
e.printStackTrace();
// 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()});
e.printStackTrace();
}
return bl;
}
/**
* 从FTP服务器上下载文件,支持断点续传,上传百分比汇报
* @param remote 远程文件路径
* @param local 本地文件路径
* @return 上传的状态
* @throws IOException
*/
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+"不存在"});
System.out.println("远程文件"+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;
}
/**
* 上传文件到FTP服务器,支持断点续传
* @param local 本地文件名称,绝对路径
* @param remote 远程文件路径,使用/home/directory1/subdirectory/file.ext 按照Linux上的路径指定方式,支持多级目录嵌套,支持递归创建不存在的目录结构
* @return 上传结果
* @throws IOException
*/
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;
}
/**
* 断开与远程服务器的连接
* @throws IOException
*/
public void disconnect() throws IOException{
if(this.ftpClient.isConnected()){
this.ftpClient.disconnect();
}
}
/**
* 功能:创建目录
* 若传入路径已经存在,则返回该路径,否则创建
* 目前暂不支持中文列名
* @param remoteDir
* @return
* @throws IOException
* String
* 范例:
*/
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;
}
/**
* 递归创建远程服务器目录
* @param remote 远程服务器文件绝对路径
* @param ftpClient FTPClient对象
* @return 目录创建是否成功
* @throws IOException
*/
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;
}
/**
* 上传文件到服务器,新上传和断点续传
* @param remoteFile 远程文件名,在上传之前已经将服务器工作目录做了改变
* @param localFile 本地文件File句柄,绝对路径
* @param processStep 需要显示的处理进度步进值
* @param ftpClient FTPClient引用
* @return
* @throws IOException
*/
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;
}
/**
* 功能:获得远程文件列表
* @param remoteDir 远程路径
* @return
* List<String>
* 范例:
*/
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;
}
/**
* 功能:删除指定远程文件
* @param fillFileName 包括路径的完整文件名
* @return
* @throws Exception
* boolean
* 范例:
*/
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;
}
/**
* 功能:删除指定远程路径
* @param remoteDir
* @return
* @throws Exception
* boolean
* 范例:
*/
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("192.168.1.101", 21, "cfd", "123456")) {
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;
}
}
二、下面是数字签名的方法和html内容
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My applet 'FileFtpApplet' starting page</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<applet codebase="."
code="com.test.ftp.FileFtpApplet.class"
name="FileFtpApplet"
width="320"
archive="FileFtpApplet.jar"
height="240">
</applet>
<!-- archive:存放applet的jar包的名称 code:要运行的applet的名称(包括包名) -->
</body>
</html>
签名方法:
<1>、生成密匙证书(key certificate),该证书将存储在你的.keystore文件中。Validity指的是密匙的有效期,默认是180,但是这里我们需要一年的时间,所以我们设置为365
keytool -genkey -alias FileFtpApplet -validity 365 -keystore FileFtpApplet.keystore
<2>、用我们的密匙来设计我们的APPLET
jarsigner -keystore FileFtpApplet.keystore FileFtpApplet.jar FileFtpApplet
<3>、导出证书
keytool -export -keystore FileFtpApplet.keystore -alias FileFtpApplet -file FileFtpApplet.cer
签名的时候把上面applet打成的jar包放到cmd当前的目录下。