• 结合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]

      在线交谈

  • 相关阅读:
    杭电acm1517
    杭电acm1228
    杭电acm1859
    杭电acm1124
    杭电acm1327
    CPP Templates 之 template 关键字的用法技巧
    malloc与calloc区别
    CPP Templates 之 类模板的继承
    CPP Templates 之 模板演绎的注意事项
    CPP Templates 之 局部类模板特化
  • 原文地址:https://www.cnblogs.com/dennisit/p/2843506.html
Copyright © 2020-2023  润新知