结合Java实现的一个腾讯空间备份器谈谈MVC思想在Swing桌面项目中的应用
HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。现在HttpClient最新版本为 HttpClient 4.2 (GA)。系统中用来实现信息爬取.
笔者寄语
腾讯空间备份器,个人玩乐之作,站在前人的肩膀上, 对知识的巩固,也顺便练练手,熟悉一下Swing编程,好久都不用了,项目虽小,五脏俱全,与大家交流分享.
项目功能:对指定QQ用户,执行空间相册批量备份,日志批量备份、留言备份、说说备份.
技术说明:使用HttpClient和_HtmlParser实现简易爬虫,信息提取
MVC思想在Swing应用程序中的使用,Log4j进行系统日志的记录.
用户信息通过配置文件获取.
本文旨在谈如何在Swing应用程序中使用MVC思想,所以代码只贴核心模块.
这里谈谈个人对MVC的理解,为初学者做个简单的介绍.
MVC是一种经典的程序设计理念.
此模式将程序分为3个部分,分别为模型层(Model)、视图层(View)、控制层(Controller).
模型层(Model)
模型层是应用程序的核心部分,主要由JavaBean组件来充当,可以是一个实体对象或一种业务逻辑,之所以称之为模型,是因为它在应用程序中有更好的重用性、扩展性.
视图层(View)
视图层提供应用程序与用户之间的交互界面,在MVC模式中,这一层并不包含任何的业务逻辑,仅仅提供一种与用户相交互的视图.在Web应用中由JSP、HTML界面充当.
控制层(Controller)
控制层用于对程序中的请求进行控制,起到一种宏观调控的作用,它可以通过容器选择什么样的视图、什么样的模型组件、在Web应用中由Servlet充当.
项目界面截图
用到的知识点就这么点,接下来咱代码说话,。
项目架构如下
从项目架构就可以很明显的看出MVC思想如何应用的了,接下来,分析一下我的模块设计
log4j.properties文件不用说了,当然是为配置日志输出的,关于Log4j的使用与总结,参照http://www.cnblogs.com/dennisit/archive/2013/01/01/2841603.html
系统中的QQ用户是通过userlist_config.xml来初始化系统数据的.因为是练手,所以就多的绕了这么一圈,当然你也可以直接设计成用户输入.
说明一下我的XML文档设计
<?xml version="1.0" encoding="UTF-8"?><userList>
<!-- 标准属性配置样式
name : 用户的QQ,配置文件中一般不允许重复,如果配置重复则以第一个为准
-->
<user name="799089378" >
<!-- name: 用户模块(值=为日志、相册、说说、留言),
tag: 界面显示的标签
savePath: 图片本地保存路径
-->
<type name="日志" tag="苏若年吧" savePath="D:/PictureCrawler/799089378/diary/"></type>
<type name="相册" tag="我的相册" savePath="D:/PictureCrawler/799089378/album/"></type>
<type name="说说" tag="快乐心情" savePath="D:/PictureCrawler/799089378/state/"></type>
<type name="留言" tag="留言模块" savePath="D:/PictureCrawler/799089378/news/"></type>
</user>
<user name="2601184786" >
<type name="日志" tag="老大扯淡" savePath="D:/PictureCrawler/2601184786/album/"></type>
<type name="相册" tag="老大艳照" savePath="D:/PictureCrawler/2601184786/album/"></type>
</user>
<user name="1377295526" >
<type name="说说" tag="阿中姑娘的说说" savePath="D:/PictureCrawler/1377295526/album/"></type>
<type name="留言" tag="阿中姑娘的留言" savePath="D:/PictureCrawler/1377295526/album/"></type>
<type name="日志" tag="阿中姑娘的日志" savePath="D:/PictureCrawler/1377295526/album/"></type>
<type name="相册" tag="阿中姑娘的相册" savePath="D:/PictureCrawler/1377295526/album/"></type>
</user>
</userList>
每个标签及属性的功能都在注释中说明,就不多说了,
配置的部分就这么搞定了
接下来我们以相册模块来分析,模块是相同的,一个模块的过程顺了,其它模块举一反三自然也就会了,项目中复杂的是业务逻辑.
接下来看Model怎么应用,Model即我们Java开发中的Bean,分为数据Bean和业务Bean.
这里的实体Bean.即为系统所用的实体对象.本项目中在org.crawler.picture.dennisit.entity包下.
包下的实体有Album(对应QQ用户的相册)、Photo(对应每一张照片)、Type(对应配置文件的type表现)、User(对应配置文件的每一个用户标签)
Album(id<相册ID>, name<相册名称>,imgcount<相册图片数目>)Photo(Album<所属相册>,url<照片路径>,name<照片名称>)
Type(name<类别名称> tag<类别标签>,savePath<保存路径>)
User(name<QQ用户>,types[List<Type> types = new ArrayList<Type>()])
org.crawler.picture.dennisit.util包中定义XML读取工具.这里将XML的工具类贴出来(xml的东西不难,但是比较繁琐).系统中这里用单例设计模式
package org.crawler.picture.dennisit.util;import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.crawler.picture.dennisit.entity.Type;
import org.crawler.picture.dennisit.entity.User;
import org.crawler.picture.dennisit.exception.PictureCrawlerException;
import org.crawler.picture.dennisit.service.Constant;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-1 下午07:45:35
*
* @function: 用户获取XML中配置的信息(单例设计模式)
*
*/
public class XMLConfigureUtil {
private static Log logger = LogFactory.getLog(XMLConfigureUtil.class); //日志应用对象
private Map<String, User> userMap = new HashMap<String, User>(); //界面初始化时用
private static XMLConfigureUtil xmlConfigure = new XMLConfigureUtil(); //单例创建的唯一对象
/**
* 构造私有化,保证只有一个对象
*/
private XMLConfigureUtil(){
readXML();
}
/**
* 单例对象获取方法
* @return
*/
public static XMLConfigureUtil getInstance(){
return xmlConfigure;
}
/**
*
* 检查配置文件路径并读取下载配置文件XML
*
*/
private void readXML() {
try {
//URL url = Toolkit.getDefaultToolkit().getClass().getResource(Constant.CONFIG_XML_WEBSITE_PATH);
URL url = getClass().getResource(Constant.CONFIG_XML_PATH);
if(null == url){
throw new PictureCrawlerException("未找到站点下载配置文件:" + Constant.CONFIG_XML_PATH);
}
readXML(url); //解析站点下载配置文件
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
/**
* 解析站点下载配置文件并进行数据对象化封装
* @param url
* @throws PictureCrawlerException
*/
private void readXML(URL url)throws PictureCrawlerException{
StringBuffer buffer = new StringBuffer("配置的下载用户QQ为:\n");
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(url.toExternalForm());
NodeList typeNodes = doc.getElementsByTagName("user");
if(null==typeNodes || 0==typeNodes.getLength()){
throw new PictureCrawlerException("不存在userList标签");
}
for(int i=0; i<typeNodes.getLength(); i++){
Node typeNode = typeNodes.item(i); //获取user标签组
NamedNodeMap userListAttr = typeNode.getAttributes();
NodeList elements = typeNode.getChildNodes(); //获取user下所有的子节点
System.out.println(elements.getLength());
User user = new User(); //创建节点对象,进行物理数据到对象的填充
for(int k=0; k<userListAttr.getLength();k++){ //遍历user标签的属性
Node attribute = userListAttr.item(k);
String attributeName = attribute.getNodeName();
if(attributeName.equalsIgnoreCase("name")){ //对应user标签的name属性
user.setName(attribute.getNodeValue());
userMap.put(attribute.getNodeValue(), user); //按照name标签名-->name对应的User对象值进行存放到Map集合中
buffer.append("[").append(attribute.getNodeValue()).append("]").append("\n");
}else {
throw new PictureCrawlerException("不存在此类属性" + attributeName);
}
}
logger.info(buffer.toString()); //日志录入信息
for(int j=0; j<elements.getLength(); j++){
Node element = elements.item(j);
if(element instanceof Element){
Type type = new Type(); //创建类别对象,对应配置文件中的type标签
NamedNodeMap typeAttr = element.getAttributes(); //获取type标签中的所有属性
for(int k=0; k<typeAttr.getLength();k++){ //遍历属性
Node attribute = typeAttr.item(k); //依次获取属性
String attributName = attribute.getNodeName();
if(attributName.equalsIgnoreCase("name")){ //对应type标签的name属性
type.setName(attribute.getNodeValue());
}else if(attributName.equalsIgnoreCase("tag")){ //对应type标签的tag属性
type.setTag(attribute.getNodeValue());
}else if(attributName.equalsIgnoreCase("savePath")){ //type标签中不存在的属性显示不存在
type.setSavePath(attribute.getNodeValue());
}else{
throw new PictureCrawlerException("不存在属性:" + attributName);
}
}//遍历一行type标签结束
user.addType(type); //遍历完一个type标签,封装成对象,添加到user对象中
}
}//end for var j
}//end for var i
} catch (Exception e) {
throw new PictureCrawlerException(e.getMessage(),e);
}
}
/**
* 返回读取到的配置文件的封装的对象映射集合信息
* @return
*/
public Map<String, User> geUserMap() {
return this.userMap;
}
}
org.crawler.picture.dennisit.exception包下定义系统异常
View Code
package org.crawler.picture.dennisit.exception;/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-1 下午06:58:23
*
* @function: TODO
*
*/
public class PictureCrawlerException extends Exception{
/**
*
*/
private static final long serialVersionUID = -8272962911990755136L;
public PictureCrawlerException(){
super();
}
public PictureCrawlerException(String message){
super(message);
}
public PictureCrawlerException(String message,Throwable cause)throws NoSuchMethodError {
super(message, cause);
}
}
images包下面是系统界面中用到的图片
辅助包与类基本上说完了,接下来的包与类是MVC的核心
org.crawler.picture.dennisit.view包下定义系统视图界面,界面可以自己根据需要去设计.但是最好设计成扩展性强的,死板的界面让人很不舒服.
这里贴一下我的系统界面的部分,其它的可以自己设计.但是这里注意的要点是如何将控制器植入到视图界面中
package org.crawler.picture.dennisit.view;import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TextArea;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.EmptyBorder;
import org.crawler.picture.dennisit.entity.Type;
import org.crawler.picture.dennisit.entity.User;
import org.crawler.picture.dennisit.exception.PictureCrawlerException;
import org.crawler.picture.dennisit.service.Constant;
import org.crawler.picture.dennisit.service.ControlContext;
import org.crawler.picture.dennisit.util.XMLConfigureUtil;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-1 下午07:10:26
*
* @function: TODO
*
*/
public class PictureCrawlerJFrame extends JFrame{
/**
*
*/
private static final long serialVersionUID = 3112116020999565721L;
private ControlContext context; //控制器
private JComboBox userBox = new JComboBox(); //用户选择列表
private JComboBox typeBox = new JComboBox(); //模块选择列表
private JButton downloadButton; //下载按钮
private JButton exitButton; //系统退出按钮
private TextArea logTextArea; //日志显示屏幕
private JButton clearButton; //情况屏幕
private Map<String, User> userMap;
public PictureCrawlerJFrame(){
init();
}
public PictureCrawlerJFrame(ControlContext context){
init();
this.context = context;
}
/**
*
* 界面初始化
*
*/
public void init(){
this.setTitle(Constant.SYS_FRAME_TITLE);
this.setSize(Constant.SYS_WIND_WIDTH, Constant.SYS_WIND_HEIGHT);
this.setContentPane(createContentPanel());
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
context.exit(PictureCrawlerJFrame.this);
}
});
}
/**
* 创建内容面板的方法
* @return
*/
public JPanel createContentPanel(){
JPanel panel = new JPanel(new BorderLayout(3,1));
panel.add(creatSetInfoPanel(),BorderLayout.NORTH);
panel.add(createLogInfoPanel(),BorderLayout.CENTER);
panel.add(createClearPanel(),BorderLayout.SOUTH);
panel.setBorder(new EmptyBorder(15,4,15,4));
return panel;
}
/**
* 创建下载设置面板
* @return
*/
public JPanel creatSetInfoPanel(){
JPanel panel = new JPanel();
//从xml文件中获取配置信息,填充面板
setUserMap(XMLConfigureUtil.getInstance().geUserMap()); //获取用户集合
String choiceItem = getUserChoicedItem(getUserMap().keySet()); //获取选择的用户
userBox = setUsersComboBox(getUserMap().keySet(),choiceItem); //填充用户下来列表信息
userBox.setPreferredSize(new Dimension(Constant.SYS_COMBOX_WIDTH, Constant.SYS_COMBOX_HEIGHT));
userBox.addActionListener(new ActionListener(){ //为用户下拉列表添加监听
@Override
public void actionPerformed(ActionEvent e) { //用户拉列表选定后,模块列表要更新
typeBox = setTypesComboBox(getUserMap().get(userBox.getSelectedItem()).getTypes());
}
});
typeBox = setTypesComboBox(getUserMap().get(choiceItem).getTypes());
typeBox.setPreferredSize(new Dimension(Constant.SYS_COMBOX_WIDTH+30, Constant.SYS_COMBOX_HEIGHT));
downloadButton = new JButton(Constant.SYS_BTN_DCOPY_LABEL);
downloadButton.setPreferredSize(new Dimension(Constant.SYS_COMBOX_WIDTH+40,Constant.SYS_COMBOX_HEIGHT));
downloadButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
try {
context.download(PictureCrawlerJFrame.this);
} catch (PictureCrawlerException e1) {
e1.printStackTrace();
}
}
});
exitButton = createImageButton(" ","/images/exit.png");
exitButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
context.exit(PictureCrawlerJFrame.this);
}
});
panel.add(new JLabel(Constant.SYS_QQUSER_LABEL));
panel.add(userBox);
panel.add(new JLabel(Constant.SYS_TYPE_LABEL));
panel.add(typeBox);
panel.add(downloadButton);
panel.add(exitButton);
return panel;
}
/**
* 获取选择的QQ用户信息
* @param keySet
* @return
*/
public String getUserChoicedItem(Set<String> keySet) {
for(String key : keySet){
return key;
}
return null;
}
/**
*
* 获取选取的模块类别
* @return
*/
public Type getChoicedType() {
List<Type> types = getUserMap().get(userBox.getSelectedItem()).getTypes();
String selectType = (String) typeBox.getSelectedItem();
for (Type type : types) {
if (type.getTag().equals(selectType)) { //如果标签相同
return type;
}
}
return null;
}
/**
* 填充用户下列表数据
* @param keySet
* @param choiceItem
* @return
*/
private JComboBox setUsersComboBox(Set<String> keySet, String choiceItem) {
for (String key : keySet) {
userBox.addItem(key);
if (key.equals(choiceItem)) {
userBox.setSelectedItem(key);
}
}
return userBox;
}
/**
* 填充模块下拉列表
* @param types
* @return
*/
private JComboBox setTypesComboBox(List<Type> types) {
typeBox.removeAllItems();
for (Type type : types) {
typeBox.addItem(type.getTag()); //根据用户选择对应的模块标签
}
return typeBox;
}
/**
* 创建图片按钮的方法
* @param text
* @param url
* @return
*/
public JButton createImageButton(String text,String url){
final JButton button = new JButton(text);
button.setMargin(new Insets(0, 0, 0, 0)); // 设置按钮边框和标题文本之间的间隔
button.setContentAreaFilled(false); // 设置不绘制按钮的内容区域
button.setBorderPainted(false); // 设置不绘制按钮的边框
button.setHorizontalTextPosition(JButton.CENTER);
button.setIcon(new ImageIcon(this.getClass().getResource(url)));
button.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
setCursor(Cursor.HAND_CURSOR);
}
public void mouseExited(MouseEvent e) {
setCursor(Cursor.DEFAULT_CURSOR);
}
});
return button;
}
/**
*
* 创建下载信息输出面板
* @return
*/
public JPanel createLogInfoPanel(){
JPanel panel = new JPanel(new BorderLayout());
JScrollPane logScrollPane = new JScrollPane();
logTextArea = new TextArea("请选择站点跟栏目,执行图片下载");
logScrollPane.setViewportView(logTextArea);
......将textArea与控制器绑定
return panel;
}
/**
* 创建清楚屏幕面板
* @return
*/
public JPanel createClearPanel(){
JPanel panel = new JPanel();
...将按钮与控制器绑定
panel.add(clearButton);
return panel;
}
/**
* 创建系统托盘
* @throws PictureCrawlerException
*/
public void systemTray() throws PictureCrawlerException{
if (SystemTray.isSupported()) {
//创建系统托盘...
}
}
/**
* 显示窗体
*/
public void showView(){
this.setVisible(true);
}
/**
* 隐藏窗体
*/
public void unshowView(){
this.setVisible(false);
}
//getter and setter 提供控制器对视图界面数据获取
}
系统的视图介绍完毕.
接下来说说控制器
org.crawler.picture.dennisit.service包下2个文件Constant.java和ControlContext.java
Constant中存放的是系统中定义的字面值,可以归到视图,笔者设计时放在service下,
package org.crawler.picture.dennisit.service;import java.awt.Toolkit;
import java.io.File;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-2 下午03:30:28
*
* @function: 系统常量定义
*
*/
public class Constant {
public static final String CONFIG_XML_PATH ="/userlist_config.xml"; //网址参数配置文件
public static final String FRAME_ICON_IMG = "/images/icon.gif"; //系统窗体图标
public static final String SYS_TRAY_IMG = "/images/icon.gif"; //系统托盘图标
public static final String SYS_SHOWWINDOW_LABEL = "显示窗体"; //系统托盘 显示窗体标签文字
public static final String SYS_EXITSYS_LABEL = "退出系统"; //系统托盘 退出系统标签文字
public static final String SYS_SET_LABEL = "系统设置"; //系统托盘 系统设置标签
public static final String SYS_USEHELP_LABEL = "系统帮助"; //系统托盘 系统帮助标签
public static final int SYS_WIND_WIDTH = 710; //系统界面设定 宽度
public static final int SYS_WIND_HEIGHT = 400; //系统界面设定 高度
public static final String SYS_FRAME_TITLE = "QQ空间备份软件"; //系统界面标签 frame文字标签
public static final String SYS_QQUSER_LABEL = "选择要备份的QQ用户:"; //系统界面标签 label文字标签
public static final String SYS_BTN_DCOPY_LABEL = "开始备份"; //系统界面标签 label文字标签
public static final String SYS_TYPE_LABEL = "模块:"; //系统界面标签 lebel文字标签
public static final String SYS_BTN_CLEAR_SCREEN = "清除日志显示屏幕"; //系统界面标签 按钮文字标签
public static final int SYS_COMBOX_WIDTH = 120; //系统界面标签 下拉列表宽度
public static final int SYS_COMBOX_HEIGHT = 30; //系统界面标签 下拉列表高度
public static final String SYS_ENCODING = "UTF-8"; //系统编码
public static final int TIMEOUT = 20000; //连接超时时间
public static final int SO_TIMEOUT = 20000; //数据传输超时
}
控制器主要是连接视图跟对应的业务逻辑的,获取数据,然后根据请求转到对应的业务Bean处理.
package org.crawler.picture.dennisit.service;import java.awt.Image;
import java.awt.Toolkit;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.Writer;
import java.util.List;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
import org.apache.log4j.WriterAppender;
import org.crawler.picture.dennisit.action.AlbumDownloadAction;
import org.crawler.picture.dennisit.action.DownloadAction;
import org.crawler.picture.dennisit.action.DownloadActionFactory;
import org.crawler.picture.dennisit.entity.Album;
import org.crawler.picture.dennisit.exception.PictureCrawlerException;
import org.crawler.picture.dennisit.util.ConnectionUtil;
import org.crawler.picture.dennisit.view.PictureCrawlerJFrame;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-1 下午07:08:59
*
* @function: 控制器
*
*/
public class ControlContext {
private DownloadAction downloadAction ;
private DownloadActionFactory downloadActionFactory;
public ControlContext(){
downloadActionFactory = new DownloadActionFactory();
}
public ControlContext(DownloadAction downloadAction){
this();
this.setDownloadAction(downloadAction);
}
/**
* 启动窗体显示
* @param frame
*/
public void start(PictureCrawlerJFrame frame){
center(frame);
try {
frame.systemTray();
} catch (PictureCrawlerException e) {
e.printStackTrace();
}
frame.setVisible(true);
}
/**
* 窗口居中显示,设定应该图标
* @param frame
*/
private void center(JFrame frame){
Image iconImg = Toolkit.getDefaultToolkit().getImage(getClass().getResource(Constant.FRAME_ICON_IMG)) ;
frame.setIconImage(iconImg);
frame.setLocationRelativeTo(null);
}
/**
* 退出系统
* @param frame
*/
public void exit(JFrame frame) {
String message = "退出系统应用?";
int opt = JOptionPane.showConfirmDialog(frame, message, "离开系统",JOptionPane.YES_NO_OPTION);
if(opt == JOptionPane.YES_OPTION) {
System.out.println("系统退出...");
frame.setVisible(false);
System.exit(0);
}
}
public void download(final PictureCrawlerJFrame frame) throws PictureCrawlerException {
System.out.println("选定的用户为:" + frame.getUserBox() + ",选定的用户的模块:" + frame.getChoicedType().getName());
System.out.println("相册保存路径:" + frame.getChoicedType().getSavePath());
String qqUser = frame.getUserBox(); // 获取选择的用户
String type = frame.getChoicedType().getName(); //获取选择的模块
String savePath = frame.getChoicedType().getSavePath();
if("相册".equals(type)){
//开始备份用户的相册
this.downloadAction = downloadActionFactory.getAlbumDownloadAction();
System.out.println("action创建为空:"+ (null==this.downloadAction));
downloadAlbum((AlbumDownloadAction)downloadAction, qqUser,savePath);
System.out.println("开始下载图片");
}else if("日志".equals(type)){
//开始备份用户的日志
this.downloadAction = downloadActionFactory.getDiaryDownloadAction();
...
调用日志备份的Action处理
}else if("说说".equals(type)){
this.downloadAction = downloadActionFactory.getStateDownloadAction();
...
调用说说备份的Action处理
//开始备份用户的说说
}else if("留言".equals(type)){
//开始备份用户的留言
this.downloadAction = downloadActionFactory.getNewsDownloadAction();
...
调用留言备份的Action处理
}else{
throw new PictureCrawlerException("配置参数不准确,[name=日志|说书|相册|留言]");
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
logAppender(frame);
}
}).start();
}
public void downloadAlbum(AlbumDownloadAction downloadAction,String qq, String savePath){
// 获取的所有相册
List<Album> albums = downloadAction.getAlbumsByAlubmSE1(qq);
if (albums == null || albums.size() == 0) {
albums = downloadAction.getAlbumsByAlubmSE2(qq);
}
if (albums == null || albums.size() == 0) {
System.out.println("没有获取到相册");
}
int len = albums.size();
System.out.println("相册信息获取成功,用户共有" + len + "个相册.");
for (int i = 0; i < len; i++) { // 考虑到相册数量不会很多,相册采用顺序下载,不使用异步下载
downloadAction.savePhoto(albums,savePath,i, qq);
albums.remove(i);
downloadAction.setCurIndex(0);
}
}
/**
* 输出日志到TextArea
*/
private void logAppender(PictureCrawlerJFrame frame) {
Logger root = Logger.getRootLogger();
Appender appender = root.getAppender("consoleA");
PipedReader reader = new PipedReader();
Writer writer = null;
try {
writer = new PipedWriter(reader);
} catch (IOException e) {
e.printStackTrace();
}
((WriterAppender) appender).setWriter(writer);
Scanner scanner = new Scanner(reader);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
frame.getLogTextArea().append("\n");
frame.getLogTextArea().append(line);
frame.getLogTextArea().append("\n");
line = null;
}
}
public void clearScreen(PictureCrawlerJFrame frame) {
System.out.println("清除屏幕...");
frame.getLogTextArea().setText("");
}
public void setDownloadAction(DownloadAction downloadAction) {
this.downloadAction = downloadAction;
}
public DownloadAction getDownloadAction() {
return downloadAction;
}
public void exit() {
String message = "退出系统应用?";
int opt = JOptionPane.showConfirmDialog(null, message, "离开系统",JOptionPane.YES_NO_OPTION);
if(opt == JOptionPane.YES_OPTION) {
System.out.println("系统退出...");
System.exit(0);
}
}
/**
* 显示窗体
* @param pictureCrawlerJFrame
*/
public void showView(PictureCrawlerJFrame pictureCrawlerJFrame) {
pictureCrawlerJFrame.setVisible(true);
}
}
控制器的部门介完毕
接下来就是核心的业务Bean.工厂类根据请求创建对应的请求Action.
View Code
package org.crawler.picture.dennisit.action;/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-2 下午11:51:44
*
* @function: TODO
*
*/
public class DownloadActionFactory {
/**
* 相册下载处理Action
* @return
*/
public DownloadAction getAlbumDownloadAction(){
return new AlbumDownloadAction();
}
/**
* 日志下载处理Action
* @return
*/
public DownloadAction getDiaryDownloadAction(){
return new DiaryDownloadAction();
}
/**
* 留言下载处理Action
* @return
*/
public DownloadAction getNewsDownloadAction(){
return new NewsDownloadAction();
}
/**
* 说说下载处理Action
* @return
*/
public DownloadAction getStateDownloadAction(){
return new StateDownloadAction();
}
}
对相册下载模块Action代码如下.
package org.crawler.picture.dennisit.action;import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.crawler.picture.dennisit.entity.Album;
import org.crawler.picture.dennisit.entity.Photo;
import org.crawler.picture.dennisit.service.Constant;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-2 下午11:36:06
*
* @function: 相册下载备份类
*
*/
public class AlbumDownloadAction extends DownloadAction {
private static final String albumbase1 = "http://alist.photo.qq.com/fcgi-bin/fcg_list_album?uin=";// 如果没有设置密保的相册是通过这个地址访问的
private static final String albumbase2 = "http://xalist.photo.qq.com/fcgi-bin/fcg_list_album?uin=";// 设置密保的相册是通过这个地址访问的
// private static final String photobase =
private static final String photobase1 = "http://plist.photo.qq.com/fcgi-bin/fcg_list_photo?uin=";
private static final String photobase2 = "http://xaplist.photo.qq.com/fcgi-bin/fcg_list_photo?uin=";
private static List<Album> albums; // 获取的所有相册
private int curIndex = 0; // 每个相册当前正在下载的图片指针
public List<Album> getAlbumsByAlubmSE1(String qq) {
return getAlbums(qq, albumbase1);
}
public List<Album> getAlbumsByAlubmSE2(String qq) {
return getAlbums(qq, albumbase2);
}
/**
*
* 获取用户的所有相册
*
* @param qq
* @param url
* @return
*/
public List<Album> getAlbums(String qq, String url) {
List<Album> result = new ArrayList<Album>();
HttpClient client = new HttpClient();
String getUri = url + qq + "&outstyle=2"; // outstyle!=2服务器将以xml的形式返回结果,
HttpMethod method = new GetMethod(getUri); // 这里只以简单文本解析提取相关信息,不做xml解析了.
method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,Constant.SYS_ENCODING);
int status = 0;
try {
status = client.executeMethod(method);
if (status != HttpStatus.SC_OK) {
System.err.println("发生网络错误!");
return null;
}
} catch (HttpException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
InputStream is = null;
BufferedReader br = null;
InputStreamReader isr = null;
List<String> ids = new ArrayList<String>();
List<String> names = new ArrayList<String>();
List<Integer> totals = new ArrayList<Integer>();
try {
is = method.getResponseBodyAsStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String temp = null;
while ((temp = br.readLine()) != null) {
if (temp.contains("\"id\" :")) {
String id = temp.substring(temp.indexOf("\"id\" :") + 8,
temp.length() - 2);
ids.add(id);
}
if (temp.contains("\"name\" :")) {
String name = temp.substring(
temp.indexOf("\"name\" :") + 10, temp.length() - 3);
names.add(name);
}
if (temp.contains("\"total\" :")) {
String total = temp
.substring(temp.indexOf("\"total\" :") + 10, temp
.length() - 1);
totals.add(Integer.parseInt(total));
}
if (temp.contains("\"left\" :")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
method.releaseConnection();
try {
br.close();
isr.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
for (int i = 0; i < ids.size(); i++) {
Album album = new Album(ids.get(i), names.get(i), totals.get(i));
result.add(album);
}
return result;
}
/**
* 获取一个相册的图片信息
*
* @param album
* 相册的信息
* @param qq
* @param phpUrl
* 地址
* @return
*/
public List<Photo> getPhotoByAlbum(Album album, String qq, String phpUrl) {
List<Photo> result = new ArrayList<Photo>();
HttpClient client = new HttpClient();
System.out.println("相册名字:" + album.getId());
String getUri = phpUrl + qq + "&album>;
HttpMethod method = new GetMethod(getUri);
method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,Constant.SYS_ENCODING);
int status = 0;
try {
status = client.executeMethod(method);
if (status != HttpStatus.SC_OK) {
System.err.println("发生网络错误!");
return null;
}
} catch (HttpException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
InputStream is = null;
BufferedReader br = null;
InputStreamReader isr = null;
List<String> names = new ArrayList<String>();
List<String> urls = new ArrayList<String>();
try {
is = method.getResponseBodyAsStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String temp = null;
boolean sign = false;
while ((temp = br.readLine()) != null) {
int len = temp.length();
if (temp.contains("\"pic\" : [")) {
sign = true;
}
if (sign && temp.contains("\"name\" :")) {
String name = temp.substring(
temp.indexOf("\"name\" :") + 10, len - 2);
names.add(name);
}
if (temp.contains("\"url\" :")) {
String url = temp.substring(temp.indexOf("\"url\" :") + 9,
len - 3);
urls.add(url);
}
if (temp.contains(" ],")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
method.releaseConnection();
try {
br.close();
isr.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
for (int i = 0; i < names.size(); i++) {
Photo photo = new Photo();
photo.setName(names.get(i));
photo.setUrl(urls.get(i));
photo.setAlbum(album);
result.add(photo);
}
return result;
}
/**
* 通过uri下载远程图片到本地
*
* @param
* @param 要保存的图片名
* (非路径)
*/
public void saveImgFromUrl(String savePath, Photo photo, String qq) {
URL imgurl = null;
BufferedInputStream bis = null;
OutputStream os = null;
try {
String phpUrl = photo.getUrl();
phpUrl = phpUrl.replace("\\", "");
imgurl = new URL(phpUrl);
bis = new BufferedInputStream(imgurl.openStream());
byte[] buffer = new byte[512];
File path = new File(savePath + "//" + qq + "//"
+ photo.getAlbum().getName().trim());
if (!path.exists()) {
path.mkdirs();
}
os = new FileOutputStream(new File(path, photo.getName().trim()
+ ".jpeg"));
int len = 0;
while ((len = bis.read(buffer)) > 0) {
os.write(buffer, 0, len);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bis.close();
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 下载一个相册的图片
*
* @param index
* 相册序号
*/
public void savePhoto(final List<Album> albums,final String savePath, final int index,
final String qq) {
Album album = albums.get(index);
if (album.getName().indexOf("微博") >= 0) {
System.out.println("微博相册不下载");
return;
}
List<Photo> photosTemp = this.getPhotoByAlbum(album, qq, photobase1);
if (photosTemp == null || photosTemp.size() == 0) {
photosTemp = this.getPhotoByAlbum(album, qq, photobase2);
}
if (photosTemp == null || photosTemp.size() == 0) {
System.out.println("相册信息为空");
return;
} else {
final List<Photo> photos = photosTemp;
final int maxThreadCnt = 10; // 每个相册最多开启10个线程进行下载
final int total = album.getImgCount();
int realThreadCnt = total >= maxThreadCnt ? maxThreadCnt : total; // 实际下载一个相册的线程数
/**
* 线程驱动下载任务
*
* @author wensefu.jerry.Ling<br/>
* wrote on 2011-1-29
*/
/*
* 初始化各线程状态 此处给每个线程分配一个下载起始点
*/
for (int i = 0; i < realThreadCnt; i++) {
startDownloadThread(total, photos,i, index, savePath, qq);
}
}
}
private void startDownloadThread(final int total,final List<Photo> photos,final int id,
final int index, final String savePath,final String qq) {
new Thread(new Runnable() {
int id; // 线程标识
int pindex = id + 1;// 下载的图片指针
public void run() {
while (getCurIndex() <= total - 1) {
int temp = pindex;
pindex = getCurIndex();
setCurIndex(getCurIndex() + 1);
Photo photo = photos.get(temp);
System.out.println("线程" + (index + 1) + "_" + id
+ "开始下载第" + (index + 1) + "个相册第" + (pindex + 1)
+ "张图片...");
saveImgFromUrl(savePath, photo, qq);
System.out.println("线程" + (index + 1) + "_" + id
+ "完成第" + (index + 1) + "个相册第" + (pindex + 1)
+ "张图片下载");
}
}
}).start();
}
...
getter and setter
}
至此, 整个项目的MVC应用分析结束,不过Swing编程在企业开发中很少用,所以建议初学的朋友没必要深究.
转载请注明出处[http://www.cnblogs.com/dennisit/archive/2013/01/03/2843506.html]
以上是 结合Java实现的一个腾讯空间备份器谈谈MVC思想在Swing桌面项目中的应用 的全部内容, 来源链接: utcz.com/z/393066.html