结合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
     *            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 + "&albumid=" + album.getId() + "&outstyle=json";
        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]

  在线交谈

原文地址:https://www.cnblogs.com/dennisit/p/2843506.html