个人项目-并发文件下载助手
各个模块的预期耗时和实际耗时
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 1440 | 1080 |
Development | 开发 | 1080 | |
· Analysis | · 需求分析 (包括学习新技术) | 360 | 240 |
· Design Spec | · 生成设计文档 | 60 | 60 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 30 |
· Design | · 具体设计 | 60 | 120 |
· Coding | · 具体编码 | 360 | 300 |
· Code Review | · 代码复审 | 60 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 240 |
Reporting | 报告 | 360 | 360 |
· Test Report | · 测试报告 | 180 | 180 |
· Size Measurement | · 计算工作量 | 90 | 120 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 90 | 60 |
合计 | 1500 | 1470 |
需求分析
得到第三阶段项目需求后,首先对项目需求进行一个整体的分析。得到了第三阶段的项目重点。
第三阶段中,重点需要实现的需求点为:图形界面。这个需求的要求是摆脱前两个阶段所必须依赖的控制台输入输出,
将程序的运行结果以图形界面的形式展示出来。并配备合适的提示方法,提醒用户下载完成。
另外一个重点的项目需求点是获取剪切板数据,方便用户进行链接的复制粘贴,改善用户体验。
再一个需要着重注意的项目需求点是保存一些基本的配置数据,方便用户使用。
思路描述
首先是确定项目的图形界面布局,这个直接关系到用户的使用体验。经过一番设计,确立了项目的图形界面的基本布局。
确定好界面布局后需要做的就是按照想法添加组件,设定组件位置了,这部分我在网上搜寻了相关的资料,采用了Java Swing
技术来进行图形界面的添加与设计,需要花费时间去着重学习页面的排版布局,和各个组件的具体使用方法。
获取剪切板的数据这部分功能的实现只需要去网上搜索获取剪切板数据需要使用到的方法即可,这里的思路上很直接。
保存配置数据这里我重点考虑了新建配置文件用于配置信息的存取。因为需要保存过去的设计,所以没办法简单的设置为默认值,
必须牵扯到存放本地数据。最终选择了将其存放在一个.txt文件中,每次打开运行软件后只需获取该txt文件的数据即可,方便快捷。
设计实现过程
本阶段的功能实现过程中,主要添加了一个类来实现相关功能。类中包含了所需的图形组件和相关的方法。添加类之后的类间关系为:
项目整体顺序图调用过程如下所示:
使用Jprofiler进行性能监控,(模拟进行了一次读取文件下载)结果如下:
线程的实时监控如下:
cpu的实时监控如下:
新增代码说明(已消除所有有意义的警告):
###App类
import javax.swing.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.*;
public class App{
private JButton commit;
private JRadioButton linkChoose;
private JRadioButton fileChoose;
private JTextField filePath;
private JTextField httpText;
private JButton quit;
private JTextField ipText;
private JTextField portText;
private JTextField remoteText;
private JTextField fileText;
private JTextField threadCount;
private JTextField localText;
private JPanel panel1;
private JTextField usernameText;
private JPasswordField passwordText;
public static void main(String[] args) {//启动方法
JFrame frame = new JFrame("App");
frame.setContentPane(new App().panel1);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public void init(){//初始化方法
File file = new File("\\property.txt");//获取之前存放的配置数据
try {
if(file.exists()){
BufferedReader reader = new BufferedReader(new FileReader(file));
String readLine = reader.readLine();
if(readLine!=null){//按照存放的格式进行字符串切割,获取配置信息
String[] s = readLine.split(" ");
threadCount.setText(s[0]);
localText.setText(s[1]);
}
}
} catch (IOException e) {
JOptionPane.showMessageDialog(null,"初始化出错");
e.printStackTrace();
}
}
public App() {
init();//首先调用初始化方法
linkChoose.addActionListener(e -> {//开启监听方法,监听按钮,当选择是输入链接的方式时,关闭输入文件的输入框
httpText.setEnabled(true);
ipText.setEnabled(true);
portText.setEnabled(true);
remoteText.setEnabled(true);
fileText.setEnabled(true);
usernameText.setEnabled(true);
passwordText.setEnabled(true);
filePath.setEnabled(false);
});
fileChoose.addActionListener(e -> {//当选择输入文件,关闭输入链接的输入框
filePath.setEnabled(true);
httpText.setEnabled(false);
ipText.setEnabled(false);
portText.setEnabled(false);
remoteText.setEnabled(false);
fileText.setEnabled(false);
usernameText.setEnabled(false);
passwordText.setEnabled(false);
});
commit.addActionListener(e -> {//监听提交按钮,点击之后开启下载
if(!"".equals(filePath.getText())){//判断文件路径输入框是否为空
JOptionPane.showMessageDialog(null,"正在全力下载,请耐心等待完成窗口");
FileDown fileDown = new FileDown(filePath.getText(),localText.getText(),Integer.parseInt(threadCount.getText()));
try {
fileDown.down();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null,"输入有误");
ex.printStackTrace();
}
JOptionPane.showMessageDialog(null,"下载完成");
}
else{
if(!"".equals(httpText.getText())){//判断http链接输入框是否为空
JOptionPane.showMessageDialog(null,"正在全力下载,请耐心等待完成窗口");
System.out.println(httpText.getText().equals(""));
HttpDownLoad downLoad = new HttpDownLoad(Integer.parseInt(threadCount.getText()));
downLoad.down(httpText.getText(),localText.getText());
JOptionPane.showMessageDialog(null,"下载完成!");
}
if(!"".equals(ipText.getText())){//判断ftp的ip输入框是否为空
JOptionPane.showMessageDialog(null,"正在全力下载,请耐心等待完成窗口");
System.out.println(ipText.getText());
FtpDownLoad ftpDownLoad = new FtpDownLoad(ipText.getText(),Integer.parseInt(portText.getText()),usernameText.getText(),new String(passwordText.getPassword()),Integer.parseInt(threadCount.getText()));
ftpDownLoad.downLoad(remoteText.getText(),fileText.getText(),localText.getText());
JOptionPane.showMessageDialog(null,"下载完成");
}
if("".equals(httpText.getText())&&"".equals(ipText.getText())){
JOptionPane.showMessageDialog(null,"不能输入空值");
}//全都是空时,弹出提示信息
}
});
quit.addActionListener(e -> {//监听退出按钮,点击保存退出后会获取设定好的配置信息,然后存放在txt文件中
File file = new File("/property.txt");
if(file.exists()){
boolean result = file.delete();
if(!result){
System.out.println("删除历史失败");
}
}
boolean result = false;
try {
result = file.createNewFile();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null,"创建文件失败");
ex.printStackTrace();
}
if(!result){
System.out.println("创建存储文件失败");
}
try {
FileWriter writer = new FileWriter(file);
if((threadCount.getText()!=null)&&(localText.getText()!=null)){
String property = threadCount.getText()+" "+localText.getText();
writer.write(property);
}
writer.close();
} catch (FileNotFoundException ex) {
JOptionPane.showMessageDialog(null, "默认配置文件路径出错");
ex.printStackTrace();
} catch (IOException ioException) {
JOptionPane.showMessageDialog(null, "写入错误");
ioException.printStackTrace();
}
System.exit(0);
});
filePath.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
filePath.paste();
}
});
//从这里向下是各个输入文本框的获取剪切板内容的功能实现。
localText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
localText.paste();
}
});
httpText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
httpText.paste();
}
});
ipText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
ipText.paste();
}
});
portText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
portText.paste();
}
});
usernameText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
usernameText.paste();
}
});
passwordText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
passwordText.paste();
}
});
remoteText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
remoteText.paste();
}
});
fileText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
fileText.paste();
}
});
}
}
核心代码如上所示,简单解释一下。基本思路就是添加一下图形化组件,然后添加一下对应的图形化组件的监听方法。各个输入框获取剪切板数据的代码
是在点击输入框时实现的,点击后自动获取输入框数据粘贴上去。获取保存的配置文件功能是在创建界面时实现的,
写在了init方法中。至于保存按钮,我是使用了File类的相关方法,对配置数据在本地进行了存放,保存过去的配置信息。
项目的心路历程与收获
这次选择本项目于我而言是一次不小的挑战,最起码从当时选项目时的直观感觉来看,这个项目是我感觉上是最不熟悉的。
在项目的整体完成的过程中我也遇到了相当多的困难,解决这些困难费了我不少的力气,但是最终的结果还是挺可观的,我自认为完成的还算是很不错。
在第一阶段和第三阶段的项目完成过程中我都没有遇到过多的问题,我个人认为我最大的遗憾是第二阶段没能完成磁力链接下载这个功能,
也正如为在二阶段博客中写的那样,我在这部分付出了巨大的努力,但是结果并不尽人意,最终还是折戟沉沙,止步在向tracker请求获取peer ip。
但是有遗憾才是学习,有遗憾才是成长,在未来的时间中我也会铭记这次项目我所遇到的困难,并坚决做到不服输不放弃,加油努力下去。