在项目中可能有时候上传的文件比较大。如果用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 stop
method
* will always be called before destroy
.
*
* A subclass of Applet
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
* init
method to create the threads and the
* destroy
method to kill them.
*/
public
void
destroy() {
//
Put your code here
}
/**
* Returns information about this applet. An applet should override
* this method to return a String
containing information
* about the author, version, and copyright of the applet.
*
*
@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 start
method is
* called.
*
* A subclass of Applet
should override this method if
* it has initialization to perform. For example, an applet with
* threads would use the init
method to create the
* threads and the destroy
method to kill them.
*/
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 init
method and each time the applet is revisited
* in a Web page.
*
* A subclass of Applet
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 start
method to
* resume animation, and the stop
method to suspend the
* animation.
*/
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.
*
* A subclass of Applet
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 start
method to
* resume animation, and the stop
method to suspend the
* animation.
*/
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
* 范例:
*/
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
>
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