由于巨懒的我不太喜欢使用U盘操作文件,特此开发一个简易的文件传输程序。
目前仅限局域网内传输,后期会尝试写下跨网传输。
本软件主要功能模块如下:
(1) 文件收发模块
(2) 局域网同网段用户检测模块
(3) 用户配置模块
文件收发模块:任意文件类型传输,仅限单个文件传输,可打包传输。
局域网同网段用户检测模块:同网段用户广播,直接发送,无需目标IP,前提需要同一网段下,且没有限制广播发送。
用户配置模块:用户自主选择是否允许同网段用户发现,自主选择文件保存目录,配置文件通过软件根目录下Config.cnf保存。
1.由于发送的模块需要read接收模块的确认接受信息,所以需要一定的阻塞时间,设置为无限
注意:设置了无限超时后,也要注意连接时的超时,防止输入错误IP后,一直尝试连接。
即可以单独设置连接超时:socket.connect(address,timeout);
项目中示例(SendThread中):
//0-为无限等待
socket.setSoTimeout(0);
//TCP连接--设置了连接超时
socket.connect(address,3000);
2. 局域网IP获取问题
Java已经提供好了网络接口类,非常方便。
详情见博文:https://blog.csdn.net/weixin_43670802/article/details/103166435
3. 线程重复开启关闭问题
项目中涉及暴露线程关闭于重启问题。
由于同一线程对象的重启很麻烦,所以我省去了关闭过程,只是让其一直continue;
(wait()
似乎也可以,可以尝试下)
也就是说此线程一直没关过。
//JVM默认初始化为false
private boolean sign;
/*
线程无法重启!!!所以这辈子我也不把它给关了!!
*/
while(true){
if(sign) {
//重新获取广播地址
broadcastAddress = CommonUtils.getBroadcastAddress();
//重新打包
DatagramPacket packet = new DatagramPacket(data, 0, data.length, broadcastAddress, R.Configs.UDP_PORT);
System.out.println(packet.getAddress().getHostAddress());
socket.send(packet);
//1s一次
Thread.sleep(1000);
}else{
//1s一次
Thread.sleep(1000);
continue;
}
}
} catch(Exception e){
e.printStackTrace();
JOptionPane.showMessageDialog(null,R.Strings.NETWORK_CHANGE);
//重新获取广播地址
broadcastAddress=CommonUtils.getBroadcastAddress();
//--test
System.out.println(broadcastAddress.getHostAddress());
//--test
if(broadcastAddress!=null)
this.run();
else {
JOptionPane.showConfirmDialog(null,R.Strings.NOT_NETWORK);
System.exit(0);
}
}
}
/*
线程开关
*/
public void setSwitch(boolean sign){
this.sign=sign;
}
4. 重复上传文件重名问题
获取到发送模块的文件名之后,使用File
类的exist()
方法检测是否已经存在。然后再利用字符串处理函数改名。
//输出文件路径+文件名
thisFile=new File(fileDirectory,fileName);
int fileCount=0;
//判断文件名是否冲突
while(thisFile.exists()){
int position=fileName.lastIndexOf(".");
String preFileName=fileName.substring(0,position);
String latFileType=fileName.substring(position);//不用去除'.'了
fileName=preFileName+"("+(++fileCount)+")"+latFileType;
thisFile=new File(fileDirectory,fileName);
}
另外,用户中止上传后(即接收模块会抛出异常),可以调用thisFile.delete();
来删除无用文件。
项目异常处理代码块中:
//删除失败文件
thisFile.delete();
5. Java设置背景图片问题(复用)
项目中将设置界面背景的函数写入了CommonUtils
工具类,另外注意中间媒介(图床)JLabel的地址一直没有变。
JLabel为MainView
成员变量。(虽然很鸡肋。。。)
/*
设置JFrame背景
前提-需要内容面板透明
````框架-载体地址-图片
*/
public static void setBackground(JFrame jFrame,JLabel jLabel,Image image){
JLayeredPane jLayeredPane=jFrame.getLayeredPane();
jLayeredPane.remove(jLabel);
jLabel.setIcon(new ImageIcon(image));
jLabel.setBounds(0,0,jFrame.getWidth(),jFrame.getHeight());
jLayeredPane.add(jLabel,new Integer(Integer.MIN_VALUE));
//jLayeredPane.add(jFrame.getContentPane(),new Integer(Integer.MAX_VALUE));
//jFrame.remove(jFrame.getLayeredPane());
//jFrame.setLayeredPane(jLayeredPane);
jFrame.validate();
jFrame.repaint();
}
7. 顶部图标问题
本项目为了美化,即将初始标题栏去掉后,将图标放置在了JMenuBar
上,自己体会吧。
menuBar.add(Box.createHorizontalStrut(400));//间隔
menuBar.add(minimizeMenuItem);
menuBar.add(closeMenuItem);
8.配置文件临时存储与写入问题
本项目创建了一个配置信息类,程序全局配置信息全部从此类中(静态方法)获取。
package transfer.data;
public class Configs {
//暴露开关,默认为开
private static int udpSwitch=0;
//默认保存地址为当前目录
private static String fileDirectory=".";
public static int getUdpSwitch() {
return udpSwitch;
}
public static void setUdpSwitch(int udpSwitch) {
Configs.udpSwitch = udpSwitch;
}
public static String getFileDiretory() {
return fileDirectory;
}
public static void setFileDirectory(String fileDirectory) {
Configs.fileDirectory = fileDirectory;
}
public static String toFile(){
return udpSwitch+","+fileDirectory;
}
public static void fromFile(String config){
String[] data=config.split(",");
Configs.setUdpSwitch(Integer.parseInt(data[0]));
Configs.setFileDirectory(data[1]);
}
}
正常关闭时写入本机文件Config,cnf
/*
---之所以不直接用字符流,是因为不想让人随意查看配置文件--抬高一点逼格,然并卵...haha...
*/
private void saveConfigs(){
DataOutputStream dataOutputStream=null;
try {
dataOutputStream = new DataOutputStream(new FileOutputStream(configFile));
dataOutputStream.writeUTF(Configs.toFile());
dataOutputStream.close();
}catch (Exception e){
e.printStackTrace();
try {
if (dataOutputStream!=null)
dataOutputStream.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
打开时读取文件至Config
类中的静态变量中:
/*
初始化读取配置文件
*/
private void initConfig(){
DataInputStream dataInputStream=null;
try{
dataInputStream=new DataInputStream(new FileInputStream(configFile));
String config=dataInputStream.readUTF();
if(config==null||config.equals(""))
throw new Exception();
else{
Configs.fromFile(config);
}
}catch(Exception e){
e.printStackTrace();
JOptionPane.showMessageDialog(null,R.Strings.READ_CONFIG_ERROR,R.Strings.TIP_TITLE,JOptionPane.ERROR_MESSAGE);
}
}